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