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 innerClasses = new ArrayList();
092    private final List 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        final ClassFileEntry[] result = new ClassFileEntry[nestedClassFileEntries.size()];
129        for (int index = 0; index < result.length; index++) {
130            result[index] = (ClassFileEntry) nestedClassFileEntries.get(index);
131        }
132        return result;
133    }
134
135    @Override
136    public int hashCode() {
137        final int PRIME = 31;
138        int result = super.hashCode();
139        result = PRIME * result + ((getAttributeName() == null) ? 0 : getAttributeName().hashCode());
140        return result;
141    }
142
143    @Override
144    protected void resolve(final ClassConstantPool pool) {
145        super.resolve(pool);
146        for (int it = 0; it < innerClasses.size(); it++) {
147            final InnerClassesEntry entry = (InnerClassesEntry) innerClasses.get(it);
148            entry.resolve(pool);
149        }
150    }
151
152    @Override
153    public String toString() {
154        return "InnerClasses: " + getAttributeName();
155    }
156
157    @Override
158    protected void doWrite(final DataOutputStream dos) throws IOException {
159        // Hack so I can see what's being written.
160        super.doWrite(dos);
161    }
162
163    @Override
164    protected void writeBody(final DataOutputStream dos) throws IOException {
165        dos.writeShort(innerClasses.size());
166
167        for (int it = 0; it < innerClasses.size(); it++) {
168            final InnerClassesEntry entry = (InnerClassesEntry) innerClasses.get(it);
169            entry.write(dos);
170        }
171    }
172
173    public void addInnerClassesEntry(final CPClass innerClass, final CPClass outerClass, final CPUTF8 innerName,
174        final int flags) {
175        if (innerClass != null) {
176            nestedClassFileEntries.add(innerClass);
177        }
178        if (outerClass != null) {
179            nestedClassFileEntries.add(outerClass);
180        }
181        if (innerName != null) {
182            nestedClassFileEntries.add(innerName);
183        }
184        addInnerClassesEntry(new InnerClassesEntry(innerClass, outerClass, innerName, flags));
185    }
186
187    private void addInnerClassesEntry(final InnerClassesEntry innerClassesEntry) {
188        innerClasses.add(innerClassesEntry);
189    }
190}