001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.commons.compress.harmony.unpack200.bytecode;
020
021import java.io.DataOutputStream;
022import java.io.IOException;
023import java.util.ArrayList;
024import java.util.List;
025
026/**
027 * Inner classes class file attribute
028 */
029public class InnerClassesAttribute extends Attribute {
030
031    private static final class InnerClassesEntry {
032
033        CPClass innerClassInfo;
034        CPClass outerClassInfo;
035        CPUTF8 innerClassName;
036
037        int innerClassInfoIndex = -1;
038        int outerClassInfoIndex = -1;
039        int innerNameIndex = -1;
040        int innerClassAccessFlags = -1;
041
042        InnerClassesEntry(final CPClass innerClass, final CPClass outerClass, final CPUTF8 innerName, final int flags) {
043            this.innerClassInfo = innerClass;
044            this.outerClassInfo = outerClass;
045            this.innerClassName = innerName;
046            this.innerClassAccessFlags = flags;
047        }
048
049        /**
050         * Determine the indices of the things in the receiver which point to elements of the ClassConstantPool
051         *
052         * @param pool ClassConstantPool which holds the CPClass and CPUTF8 objects.
053         */
054        public void resolve(final ClassConstantPool pool) {
055            if (innerClassInfo != null) {
056                innerClassInfo.resolve(pool);
057                innerClassInfoIndex = pool.indexOf(innerClassInfo);
058            } else {
059                innerClassInfoIndex = 0;
060            }
061
062            if (innerClassName != null) {
063                innerClassName.resolve(pool);
064                innerNameIndex = pool.indexOf(innerClassName);
065            } else {
066                innerNameIndex = 0;
067            }
068
069            if (outerClassInfo != null) {
070                outerClassInfo.resolve(pool);
071                outerClassInfoIndex = pool.indexOf(outerClassInfo);
072            } else {
073                outerClassInfoIndex = 0;
074            }
075        }
076
077        public void write(final DataOutputStream dos) throws IOException {
078            dos.writeShort(innerClassInfoIndex);
079            dos.writeShort(outerClassInfoIndex);
080            dos.writeShort(innerNameIndex);
081            dos.writeShort(innerClassAccessFlags);
082        }
083
084    }
085
086    private static CPUTF8 attributeName;
087
088    public static void setAttributeName(final CPUTF8 cpUTF8Value) {
089        attributeName = cpUTF8Value;
090    }
091
092    private final List<InnerClassesEntry> innerClasses = new ArrayList<>();
093    private final List<ConstantPoolEntry> nestedClassFileEntries = new ArrayList<>();
094
095    public InnerClassesAttribute(final String name) {
096        super(attributeName);
097        nestedClassFileEntries.add(getAttributeName());
098    }
099
100    public void addInnerClassesEntry(final CPClass innerClass, final CPClass outerClass, final CPUTF8 innerName, final int flags) {
101        if (innerClass != null) {
102            nestedClassFileEntries.add(innerClass);
103        }
104        if (outerClass != null) {
105            nestedClassFileEntries.add(outerClass);
106        }
107        if (innerName != null) {
108            nestedClassFileEntries.add(innerName);
109        }
110        addInnerClassesEntry(new InnerClassesEntry(innerClass, outerClass, innerName, flags));
111    }
112
113    private void addInnerClassesEntry(final InnerClassesEntry innerClassesEntry) {
114        innerClasses.add(innerClassesEntry);
115    }
116
117    @Override
118    public boolean equals(final Object obj) {
119        if (this == obj) {
120            return true;
121        }
122        if (!super.equals(obj) || this.getClass() != obj.getClass()) {
123            return false;
124        }
125        final InnerClassesAttribute other = (InnerClassesAttribute) obj;
126        if (getAttributeName() == null) {
127            if (other.getAttributeName() != null) {
128                return false;
129            }
130        } else if (!getAttributeName().equals(other.getAttributeName())) {
131            return false;
132        }
133        return true;
134    }
135
136    @Override
137    protected int getLength() {
138        return 2 + (2 + 2 + 2 + 2) * innerClasses.size();
139    }
140
141    @Override
142    protected ClassFileEntry[] getNestedClassFileEntries() {
143        return nestedClassFileEntries.toArray(NONE);
144    }
145
146    @Override
147    public int hashCode() {
148        final int PRIME = 31;
149        int result = super.hashCode();
150        result = PRIME * result + (getAttributeName() == null ? 0 : getAttributeName().hashCode());
151        return result;
152    }
153
154    @Override
155    protected void resolve(final ClassConstantPool pool) {
156        super.resolve(pool);
157        for (final InnerClassesEntry entry : innerClasses) {
158            entry.resolve(pool);
159        }
160    }
161
162    @Override
163    public String toString() {
164        return "InnerClasses: " + getAttributeName();
165    }
166
167    @Override
168    protected void writeBody(final DataOutputStream dos) throws IOException {
169        dos.writeShort(innerClasses.size());
170
171        for (final InnerClassesEntry entry : innerClasses) {
172            entry.write(dos);
173        }
174    }
175}