001    package serp.bytecode;
002    
003    import java.io.*;
004    import java.util.*;
005    
006    import 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     */
014    public 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    }