001    package serp.bytecode;
002    
003    import java.io.*;
004    import java.util.*;
005    
006    import serp.bytecode.lowlevel.*;
007    import serp.bytecode.visitor.*;
008    
009    /**
010     * Abstract superclass for all bytecode entities that hold attributes.
011     *
012     * @author Abe White
013     */
014    public abstract class Attributes implements BCEntity {
015        /**
016         * Return all the attributes owned by this entity.
017         *
018         * @return all owned attributes, or empty array if none
019         */
020        public Attribute[] getAttributes() {
021            Collection attrs = getAttributesHolder();
022            return (Attribute[]) attrs.toArray(new Attribute[attrs.size()]);
023        }
024    
025        /**
026         * Return the attribute with the given name. If multiple attributes
027         * share the name, which is returned is undefined.
028         */
029        public Attribute getAttribute(String name) {
030            Collection attrs = getAttributesHolder();
031            Attribute attr;
032            for (Iterator itr = attrs.iterator(); itr.hasNext();) {
033                attr = (Attribute) itr.next();
034                if (attr.getName().equals(name))
035                    return attr;
036            }
037            return null;
038        }
039    
040        /**
041         * Return all attributes with the given name.
042         *
043         * @return the matching attributes, or empty array if none
044         */
045        public Attribute[] getAttributes(String name) {
046            List matches = new LinkedList();
047            Collection attrs = getAttributesHolder();
048            Attribute attr;
049            for (Iterator itr = attrs.iterator(); itr.hasNext();) {
050                attr = (Attribute) itr.next();
051                if (attr.getName().equals(name))
052                    matches.add(attr);
053            }
054            return (Attribute[]) matches.toArray(new Attribute[matches.size()]);
055        }
056    
057        /**
058         * Set the attributes for this entity; this method is useful for importing
059         * all attributes from another entity. Set to null or empty array if none.
060         */
061        public void setAttributes(Attribute[] attrs) {
062            clearAttributes();
063            if (attrs != null) 
064                for (int i = 0; i < attrs.length; i++)
065                    addAttribute(attrs[i]);
066        }
067    
068        /**
069         * Import an attribute from another entity, or make a copy of one
070         * on this entity.
071         */
072        public Attribute addAttribute(Attribute attr) {
073            Attribute newAttr = addAttribute(attr.getName());
074            newAttr.read(attr);
075            return newAttr;
076        }
077    
078        /**
079         * Add an attribute of the given type.
080         */
081        public Attribute addAttribute(String name) {
082            Attribute attr = Attribute.create(name, this);
083            getAttributesHolder().add(attr);
084            return attr;
085        }
086    
087        /**
088         * Clear all attributes from this entity.
089         */
090        public void clearAttributes() {
091            Collection attrs = getAttributesHolder();
092            Attribute attr;
093            for (Iterator itr = attrs.iterator(); itr.hasNext();) {
094                attr = (Attribute) itr.next();
095                itr.remove();
096                attr.invalidate();
097            }
098        }
099    
100        /**
101         * Remove all attributes with the given name from this entity.
102         *
103         * @return true if an attribute was removed, false otherwise
104         */
105        public boolean removeAttribute(String name) {
106            return removeAttribute(getAttribute(name));
107        }
108    
109        /**
110         * Remove the given attribute. After being removed, the attribute
111         * is invalid, and the result of any operations on it are undefined.
112         *
113         * @return true if the attribute was removed, false otherwise
114         */
115        public boolean removeAttribute(Attribute attribute) {
116            if ((attribute == null) || !getAttributesHolder().remove(attribute))
117                return false;
118            attribute.invalidate();
119            return true;
120        }
121    
122        /**
123         * Convenience method to be called by BCEntities when being visited
124         * by a {@link BCVisitor}; this method will allow the visitor to visit all
125         * attributes of this entity.
126         */
127        void visitAttributes(BCVisitor visit) {
128            Attribute attr;
129            for (Iterator itr = getAttributesHolder().iterator(); itr.hasNext();) {
130                attr = (Attribute) itr.next();
131                visit.enterAttribute(attr);
132                attr.acceptVisit(visit);
133                visit.exitAttribute(attr);
134            }
135        }
136    
137        /**
138         * Build the attribute list from the given stream.
139         * Relies on the ability of attributes to read themselves, and
140         * requires access to the constant pool, which must already by read.
141         */
142        void readAttributes(DataInput in) throws IOException {
143            Collection attrs = getAttributesHolder();
144            attrs.clear();
145    
146            Attribute attribute;
147            String name;
148            for (int i = in.readUnsignedShort(); i > 0; i--) {
149                name = ((UTF8Entry) getPool().getEntry(in.readUnsignedShort())).
150                    getValue();
151                attribute = addAttribute(name);
152                attribute.read(in, in.readInt());
153            }
154        }
155    
156        /**
157         * Writes all the owned attributes to the given stream.
158         * Relies on the ability of attributes to write themselves.
159         */
160        void writeAttributes(DataOutput out) throws IOException {
161            Collection attrs = getAttributesHolder();
162            out.writeShort(attrs.size());
163    
164            Attribute attribute;
165            int length;
166            for (Iterator itr = attrs.iterator(); itr.hasNext();) {
167                attribute = (Attribute) itr.next();
168                out.writeShort(attribute.getNameIndex());
169                length = attribute.getLength();
170                out.writeInt(length);
171                attribute.write(out, length);
172            }
173        }
174    
175        /**
176         * Return the collection used to hold the attributes of this entity.
177         */
178        abstract Collection getAttributesHolder();
179    }