001package serp.bytecode;
002
003import java.io.*;
004import java.util.*;
005
006import serp.bytecode.visitor.*;
007
008/**
009 * Attribute describing all referenced classes that are not package
010 * members. This includes all member interfaces and classes.
011 *
012 * @author Abe White
013 */
014public class InnerClasses extends Attribute {
015    private List _innerClasses = new LinkedList();
016
017    InnerClasses(int nameIndex, Attributes owner) {
018        super(nameIndex, owner);
019    }
020
021    /**
022     * Return all referenced inner classes, or empty array if none.
023     */
024    public InnerClass[] getInnerClasses() {
025        return (InnerClass[]) _innerClasses.toArray
026            (new InnerClass[_innerClasses.size()]);
027    }
028
029    /**
030     * Return the inner class with the given name. If multiple inner classes
031     * share the name, which is returned is undefined. Use null to retrieve
032     * anonymous classes.
033     */
034    public InnerClass getInnerClass(String name) {
035        InnerClass[] inners = getInnerClasses();
036        String inner;
037        for (int i = 0; i < inners.length; i++) {
038            inner = inners[i].getName();
039            if ((inner == null && name == null) 
040                || (inner != null && inner.equals(name)))
041                return inners[i];
042        }
043        return null;
044    }
045
046    /**
047     * Return all inner classes with the given name, or empty array if none.
048     * Use null to retrieve anonymous classes.
049     */
050    public InnerClass[] getInnerClasses(String name) {
051        List matches = new LinkedList();
052        InnerClass[] inners = getInnerClasses();
053        String inner;
054        for (int i = 0; i < inners.length; i++) {
055            inner = inners[i].getName();
056            if ((inner == null && name == null) 
057                || (inner != null && inner.equals(name)))
058                matches.add(inners[i]);
059        }
060        return (InnerClass[]) matches.toArray(new InnerClass[matches.size()]);
061    }
062
063    /**
064     * Set the inner class references for this class. This method is
065     * useful when importing inner class references from another class.
066     */
067    public void setInnerClasses(InnerClass[] inners) {
068        clear();
069        if (inners != null)
070            for (int i = 0; i < inners.length; i++)
071                addInnerClass(inners[i]);
072    }
073
074    /**
075     * Import an inner class from another entity, or make a copy of one
076     * on this entity.
077     *
078     * @return the newly added inner class
079     */
080    public InnerClass addInnerClass(InnerClass inner) {
081        InnerClass newInner = addInnerClass(inner.getName(),
082            inner.getTypeName(), inner.getDeclarerName());
083        newInner.setAccessFlags(inner.getAccessFlags());
084        return newInner;
085    }
086
087    /**
088     * Add an inner class.
089     */
090    public InnerClass addInnerClass() {
091        InnerClass inner = new InnerClass(this);
092        _innerClasses.add(inner);
093        return inner;
094    }
095
096    /**
097     * Add an inner class.
098     *
099     * @param name the simple name of the class, or null if anonymous
100     * @param type the full class name of the inner class
101     * @param owner the declaring class, or null if not a member class
102     */
103    public InnerClass addInnerClass(String name, String type, String owner) {
104        InnerClass inner = addInnerClass();
105        inner.setName(name);
106        inner.setType(type);
107        inner.setDeclarer(owner);
108        return inner;
109    }
110
111    /**
112     * Add an inner class.
113     *
114     * @param name the simple name of the class, or null if anonymous
115     * @param type the class of the inner class
116     * @param owner the declaring class, or null if not a member class
117     */
118    public InnerClass addInnerClass(String name, Class type, Class owner) {
119        String typeName = (type == null) ? null : type.getName();
120        String ownerName = (owner == null) ? null : owner.getName();
121        return addInnerClass(name, typeName, ownerName);
122    }
123
124    /**
125     * Add an inner class.
126     *
127     * @param name the simple name of the class, or null if anonymous
128     * @param type the class of the inner class
129     * @param owner the declaring class, or null if not a member class
130     */
131    public InnerClass addInnerClass(String name, BCClass type, BCClass owner) {
132        String typeName = (type == null) ? null : type.getName();
133        String ownerName = (owner == null) ? null : owner.getName();
134        return addInnerClass(name, typeName, ownerName);
135    }
136
137    /**
138     * Clear all inner classes from this entity.
139     */
140    public void clear() {
141        InnerClass inner;
142        for (Iterator itr = _innerClasses.iterator(); itr.hasNext();) {
143            inner = (InnerClass) itr.next();
144            itr.remove();
145            inner.invalidate();
146        }
147    }
148
149    /**
150     * Remove the inner class with the given name. Use null for anonymous
151     * classes.
152     *
153     * @return true if an inner class was removed, false otherwise
154     */
155    public boolean removeInnerClass(String name) {
156        return removeInnerClass(getInnerClass(name));
157    }
158
159    /**
160     * Remove the given inner class. After being removed, the given inner
161     * class is invalid, and the result of any operations on it are undefined.
162     *
163     * @return true if the inner class was removed, false otherwise
164     */
165    public boolean removeInnerClass(InnerClass innerClass) {
166        if (innerClass == null || !_innerClasses.remove(innerClass))
167            return false;
168        innerClass.invalidate();
169        return true;
170    }
171
172    public void acceptVisit(BCVisitor visit) {
173        visit.enterInnerClasses(this);
174        InnerClass[] inners = getInnerClasses();
175        for (int i = 0; i < inners.length; i++)
176            inners[i].acceptVisit(visit);
177        visit.exitInnerClasses(this);
178    }
179
180    int getLength() {
181        return 2 + (8 * _innerClasses.size());
182    }
183
184    void read(Attribute other) {
185        setInnerClasses(((InnerClasses) other).getInnerClasses());
186    }
187
188    void read(DataInput in, int length) throws IOException {
189        clear();
190        int numInnerClasses = in.readUnsignedShort();
191        InnerClass innerClass;
192        for (int i = 0; i < numInnerClasses; i++) {
193            innerClass = addInnerClass();
194            innerClass.read(in);
195        }
196    }
197
198    void write(DataOutput out, int length) throws IOException {
199        InnerClass[] inners = getInnerClasses();
200        out.writeShort(inners.length);
201        for (int i = 0; i < inners.length; i++)
202            inners[i].write(out);
203    }
204}