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 }