001package serp.bytecode;
002
003import java.io.*;
004import java.util.*;
005
006import serp.bytecode.lowlevel.*;
007import serp.bytecode.visitor.*;
008
009/**
010 * Abstract superclass for all bytecode entities that hold attributes.
011 *
012 * @author Abe White
013 */
014public 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}