You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ClassGen.java 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. package org.aspectj.apache.bcel.generic;
  2. /* ====================================================================
  3. * The Apache Software License, Version 1.1
  4. *
  5. * Copyright (c) 2001 The Apache Software Foundation. All rights
  6. * reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Apache" and "Apache Software Foundation" and
  28. * "Apache BCEL" must not be used to endorse or promote products
  29. * derived from this software without prior written permission. For
  30. * written permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * "Apache BCEL", nor may "Apache" appear in their name, without
  34. * prior written permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation. For more
  52. * information on the Apache Software Foundation, please see
  53. * <http://www.apache.org/>.
  54. */
  55. import java.io.ByteArrayOutputStream;
  56. import java.io.DataOutputStream;
  57. import java.lang.reflect.Modifier;
  58. import java.security.MessageDigest;
  59. import java.util.ArrayList;
  60. import java.util.Arrays;
  61. import java.util.Collection;
  62. import java.util.Collections;
  63. import java.util.Comparator;
  64. import java.util.List;
  65. import org.aspectj.apache.bcel.Constants;
  66. import org.aspectj.apache.bcel.classfile.Attribute;
  67. import org.aspectj.apache.bcel.classfile.ConstantPool;
  68. import org.aspectj.apache.bcel.classfile.Field;
  69. import org.aspectj.apache.bcel.classfile.JavaClass;
  70. import org.aspectj.apache.bcel.classfile.Method;
  71. import org.aspectj.apache.bcel.classfile.Modifiers;
  72. import org.aspectj.apache.bcel.classfile.SourceFile;
  73. import org.aspectj.apache.bcel.classfile.Utility;
  74. import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen;
  75. import org.aspectj.apache.bcel.classfile.annotation.RuntimeInvisAnnos;
  76. import org.aspectj.apache.bcel.classfile.annotation.RuntimeVisAnnos;
  77. /**
  78. * Template class for building up a java class. May be initialized with an existing java class.
  79. *
  80. * @see JavaClass
  81. * @version $Id: ClassGen.java,v 1.15 2009/09/15 19:40:14 aclement Exp $
  82. * @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
  83. *
  84. * Upgraded, Andy Clement 9th Mar 06 - calculates SUID
  85. */
  86. public class ClassGen extends Modifiers implements Cloneable {
  87. private String classname;
  88. private String superclassname;
  89. private String filename;
  90. private int classnameIndex = -1;
  91. private int superclassnameIndex = -1;
  92. private int major = Constants.MAJOR_1_1;
  93. private int minor = Constants.MINOR_1_1;
  94. private ConstantPool cpool;
  95. private List<Field> fieldsList = new ArrayList<Field>();
  96. private List<Method> methodsList = new ArrayList<Method>();
  97. private List<Attribute> attributesList = new ArrayList<Attribute>();
  98. private List<String> interfaceList = new ArrayList<String>();
  99. private List<AnnotationGen> annotationsList = new ArrayList<AnnotationGen>();
  100. public ClassGen(String classname, String superclassname, String filename, int modifiers, String[] interfacenames,
  101. ConstantPool cpool) {
  102. this.classname = classname;
  103. this.superclassname = superclassname;
  104. this.filename = filename;
  105. this.modifiers = modifiers;
  106. this.cpool = cpool;
  107. if (filename != null) {
  108. addAttribute(new SourceFile(cpool.addUtf8("SourceFile"), 2, cpool.addUtf8(filename), cpool));
  109. }
  110. this.classnameIndex = cpool.addClass(classname);
  111. this.superclassnameIndex = cpool.addClass(superclassname);
  112. if (interfacenames != null) {
  113. for (String interfacename : interfacenames) {
  114. addInterface(interfacename);
  115. }
  116. }
  117. }
  118. public ClassGen(String classname, String superclassname, String filename, int modifiers, String[] interfacenames) {
  119. this(classname, superclassname, filename, modifiers, interfacenames, new ConstantPool());
  120. }
  121. public ClassGen(JavaClass clazz) {
  122. classnameIndex = clazz.getClassNameIndex();
  123. superclassnameIndex = clazz.getSuperclassNameIndex();
  124. classname = clazz.getClassName();
  125. superclassname = clazz.getSuperclassName();
  126. filename = clazz.getSourceFileName();
  127. modifiers = clazz.getModifiers();
  128. cpool = clazz.getConstantPool().copy();
  129. major = clazz.getMajor();
  130. minor = clazz.getMinor();
  131. Method[] methods = clazz.getMethods();
  132. Field[] fields = clazz.getFields();
  133. String[] interfaces = clazz.getInterfaceNames();
  134. for (String anInterface : interfaces) {
  135. addInterface(anInterface);
  136. }
  137. // OPTIMIZE Could make unpacking lazy, done on first reference
  138. Attribute[] attributes = clazz.getAttributes();
  139. for (Attribute attr : attributes) {
  140. if (attr instanceof RuntimeVisAnnos) {
  141. RuntimeVisAnnos rva = (RuntimeVisAnnos) attr;
  142. List<AnnotationGen> annos = rva.getAnnotations();
  143. for (AnnotationGen a : annos) {
  144. annotationsList.add(new AnnotationGen(a, cpool, false));
  145. }
  146. } else if (attr instanceof RuntimeInvisAnnos) {
  147. RuntimeInvisAnnos ria = (RuntimeInvisAnnos) attr;
  148. List<AnnotationGen> annos = ria.getAnnotations();
  149. for (AnnotationGen anno : annos) {
  150. annotationsList.add(new AnnotationGen(anno, cpool, false));
  151. }
  152. } else {
  153. attributesList.add(attr);
  154. }
  155. }
  156. for (Method method : methods) {
  157. addMethod(method);
  158. }
  159. for (Field field : fields) {
  160. addField(field);
  161. }
  162. }
  163. /**
  164. * @return build and return a JavaClass
  165. */
  166. public JavaClass getJavaClass() {
  167. int[] interfaces = getInterfaces();
  168. Field[] fields = getFields();
  169. Method[] methods = getMethods();
  170. Collection<Attribute> attributes = null;
  171. if (annotationsList.size() == 0) {
  172. attributes = attributesList;
  173. } else {
  174. // TODO: Sometime later, trash any attributes called 'RuntimeVisibleAnnotations' or 'RuntimeInvisibleAnnotations'
  175. attributes = new ArrayList<Attribute>();
  176. attributes.addAll(Utility.getAnnotationAttributes(cpool, annotationsList));
  177. attributes.addAll(attributesList);
  178. }
  179. // Must be last since the above calls may still add something to it
  180. ConstantPool cp = this.cpool.getFinalConstantPool();
  181. return new JavaClass(classnameIndex, superclassnameIndex, filename, major, minor, modifiers, cp, interfaces, fields,
  182. methods, attributes.toArray(new Attribute[attributes.size()]));// OPTIMIZE avoid toArray()?
  183. }
  184. public void addInterface(String name) {
  185. interfaceList.add(name);
  186. }
  187. public void removeInterface(String name) {
  188. interfaceList.remove(name);
  189. }
  190. public int getMajor() {
  191. return major;
  192. }
  193. public void setMajor(int major) {
  194. this.major = major;
  195. }
  196. public void setMinor(int minor) {
  197. this.minor = minor;
  198. }
  199. public int getMinor() {
  200. return minor;
  201. }
  202. public void addAttribute(Attribute a) {
  203. attributesList.add(a);
  204. }
  205. public void addAnnotation(AnnotationGen a) {
  206. annotationsList.add(a);
  207. }
  208. public void addMethod(Method m) {
  209. methodsList.add(m);
  210. }
  211. /**
  212. * Convenience method.
  213. *
  214. * Add an empty constructor to this class that does nothing but calling super().
  215. *
  216. * @param access rights for constructor
  217. */
  218. public void addEmptyConstructor(int access_flags) {
  219. InstructionList il = new InstructionList();
  220. il.append(InstructionConstants.THIS); // Push `this'
  221. il.append(new InvokeInstruction(Constants.INVOKESPECIAL, cpool.addMethodref(superclassname, "<init>", "()V")));
  222. il.append(InstructionConstants.RETURN);
  223. MethodGen mg = new MethodGen(access_flags, Type.VOID, Type.NO_ARGS, null, "<init>", classname, il, cpool);
  224. mg.setMaxStack(1);
  225. mg.setMaxLocals();
  226. addMethod(mg.getMethod());
  227. }
  228. /**
  229. * Add a field to this class.
  230. *
  231. * @param f field to add
  232. */
  233. public void addField(Field f) {
  234. fieldsList.add(f);
  235. }
  236. public boolean containsField(Field f) {
  237. return fieldsList.contains(f);
  238. }
  239. /**
  240. * @return field object with given name, or null if not found
  241. */
  242. public Field findsField(String name) {
  243. for (Field field : fieldsList) {
  244. if (field.getName().equals(name)) {
  245. return field;
  246. }
  247. }
  248. return null;
  249. }
  250. /**
  251. * @return method object with given name and signature, or null if not found
  252. */
  253. public Method containsMethod(String name, String signature) {
  254. for (Method method : methodsList) {
  255. if (method.getName().equals(name) && method.getSignature().equals(signature)) {
  256. return method;
  257. }
  258. }
  259. return null;
  260. }
  261. public void removeAttribute(Attribute a) {
  262. attributesList.remove(a);
  263. }
  264. public void removeAnnotation(AnnotationGen a) {
  265. annotationsList.remove(a);
  266. }
  267. public void removeMethod(Method m) {
  268. methodsList.remove(m);
  269. }
  270. /**
  271. * Replace given method with new one. If the old one does not exist add the new_ method to the class anyway.
  272. */
  273. public void replaceMethod(Method old, Method new_) {
  274. if (new_ == null)
  275. throw new ClassGenException("Replacement method must not be null");
  276. int i = methodsList.indexOf(old);
  277. if (i < 0)
  278. methodsList.add(new_);
  279. else
  280. methodsList.set(i, new_);
  281. }
  282. /**
  283. * Replace given field with new one. If the old one does not exist add the new_ field to the class anyway.
  284. */
  285. public void replaceField(Field old, Field new_) {
  286. if (new_ == null)
  287. throw new ClassGenException("Replacement method must not be null");
  288. int i = fieldsList.indexOf(old);
  289. if (i < 0)
  290. fieldsList.add(new_);
  291. else
  292. fieldsList.set(i, new_);
  293. }
  294. public void removeField(Field f) {
  295. fieldsList.remove(f);
  296. }
  297. public String getClassName() {
  298. return classname;
  299. }
  300. public String getSuperclassName() {
  301. return superclassname;
  302. }
  303. public String getFileName() {
  304. return filename;
  305. }
  306. public void setClassName(String name) {
  307. classname = name.replace('/', '.');
  308. classnameIndex = cpool.addClass(name);
  309. }
  310. public void setSuperclassName(String name) {
  311. superclassname = name.replace('/', '.');
  312. superclassnameIndex = cpool.addClass(name);
  313. }
  314. public Method[] getMethods() {
  315. Method[] methods = new Method[methodsList.size()];
  316. methodsList.toArray(methods);
  317. return methods;
  318. }
  319. public void setMethods(Method[] methods) {
  320. methodsList.clear();
  321. for (Method method : methods) addMethod(method);
  322. }
  323. public void setFields(Field[] fs) {
  324. fieldsList.clear();
  325. for (Field f : fs) addField(f);
  326. }
  327. public void setMethodAt(Method method, int pos) {
  328. methodsList.set(pos, method);
  329. }
  330. public Method getMethodAt(int pos) {
  331. return methodsList.get(pos);
  332. }
  333. public String[] getInterfaceNames() {
  334. int size = interfaceList.size();
  335. String[] interfaces = new String[size];
  336. interfaceList.toArray(interfaces);
  337. return interfaces;
  338. }
  339. public int[] getInterfaces() {
  340. int size = interfaceList.size();
  341. int[] interfaces = new int[size];
  342. for (int i = 0; i < size; i++)
  343. interfaces[i] = cpool.addClass(interfaceList.get(i));
  344. return interfaces;
  345. }
  346. public Field[] getFields() {
  347. Field[] fields = new Field[fieldsList.size()];
  348. fieldsList.toArray(fields);
  349. return fields;
  350. }
  351. public Collection<Attribute> getAttributes() {
  352. return attributesList;
  353. }
  354. // J5TODO: Should we make calling unpackAnnotations() lazy and put it in here?
  355. public AnnotationGen[] getAnnotations() {
  356. AnnotationGen[] annotations = new AnnotationGen[annotationsList.size()];
  357. annotationsList.toArray(annotations);
  358. return annotations;
  359. }
  360. public ConstantPool getConstantPool() {
  361. return cpool;
  362. }
  363. public void setConstantPool(ConstantPool constant_pool) {
  364. cpool = constant_pool;
  365. }
  366. public void setClassNameIndex(int class_name_index) {
  367. this.classnameIndex = class_name_index;
  368. classname = cpool.getConstantString(class_name_index, Constants.CONSTANT_Class).replace('/', '.');
  369. }
  370. public void setSuperclassNameIndex(int superclass_name_index) {
  371. this.superclassnameIndex = superclass_name_index;
  372. superclassname = cpool.getConstantString(superclass_name_index, Constants.CONSTANT_Class).replace('/', '.');
  373. }
  374. public int getSuperclassNameIndex() {
  375. return superclassnameIndex;
  376. }
  377. public int getClassNameIndex() {
  378. return classnameIndex;
  379. }
  380. @Override
  381. public Object clone() {
  382. try {
  383. return super.clone();
  384. } catch (CloneNotSupportedException e) {
  385. System.err.println(e);
  386. return null;
  387. }
  388. }
  389. public final boolean isAnnotation() {
  390. return (modifiers & Constants.ACC_ANNOTATION) != 0;
  391. }
  392. public final boolean isEnum() {
  393. return (modifiers & Constants.ACC_ENUM) != 0;
  394. }
  395. /**
  396. * Calculate the SerialVersionUID for a class.
  397. */
  398. public long getSUID() {
  399. try {
  400. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  401. DataOutputStream dos = new DataOutputStream(baos);
  402. // 1. classname
  403. dos.writeUTF(getClassName());
  404. // 2. classmodifiers: ACC_PUBLIC, ACC_FINAL, ACC_INTERFACE, and ACC_ABSTRACT
  405. int classmods = 0;
  406. classmods |= (isPublic() ? Constants.ACC_PUBLIC : 0);
  407. classmods |= (isFinal() ? Constants.ACC_FINAL : 0);
  408. classmods |= (isInterface() ? Constants.ACC_INTERFACE : 0);
  409. if (isAbstract()) {
  410. // if an interface then abstract is only set if it has methods
  411. if (isInterface()) {
  412. if (methodsList.size() > 0)
  413. classmods |= Constants.ACC_ABSTRACT;
  414. } else {
  415. classmods |= Constants.ACC_ABSTRACT;
  416. }
  417. }
  418. dos.writeInt(classmods);
  419. // 3. ordered list of interfaces
  420. String[] names = getInterfaceNames();
  421. if (names != null) {
  422. Arrays.sort(names);
  423. for (String name : names) dos.writeUTF(name);
  424. }
  425. // 4. ordered list of fields (ignoring private static and private transient fields):
  426. // (relevant modifiers are ACC_PUBLIC, ACC_PRIVATE,
  427. // ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_VOLATILE,
  428. // ACC_TRANSIENT)
  429. List<Field> relevantFields = new ArrayList<Field>();
  430. for (Field field : fieldsList) {
  431. if (!(field.isPrivate() && field.isStatic()) && !(field.isPrivate() && field.isTransient())) {
  432. relevantFields.add(field);
  433. }
  434. }
  435. Collections.sort(relevantFields, new FieldComparator());
  436. int relevantFlags = Constants.ACC_PUBLIC | Constants.ACC_PRIVATE | Constants.ACC_PROTECTED | Constants.ACC_STATIC
  437. | Constants.ACC_FINAL | Constants.ACC_VOLATILE | Constants.ACC_TRANSIENT;
  438. for (Field f : relevantFields) {
  439. dos.writeUTF(f.getName());
  440. dos.writeInt(relevantFlags & f.getModifiers());
  441. dos.writeUTF(f.getType().getSignature());
  442. }
  443. // some up front method processing: discover clinit, init and ordinary methods of interest:
  444. List<Method> relevantMethods = new ArrayList<Method>();
  445. List<Method> relevantCtors = new ArrayList<Method>();
  446. boolean hasClinit = false;
  447. for (Method m : methodsList) {
  448. boolean couldBeInitializer = m.getName().charAt(0) == '<';
  449. if (couldBeInitializer && m.getName().equals("<clinit>")) {
  450. hasClinit = true;
  451. } else if (couldBeInitializer && m.getName().equals("<init>")) {
  452. if (!m.isPrivate())
  453. relevantCtors.add(m);
  454. } else {
  455. if (!m.isPrivate())
  456. relevantMethods.add(m);
  457. }
  458. }
  459. Collections.sort(relevantCtors, new ConstructorComparator());
  460. Collections.sort(relevantMethods, new MethodComparator());
  461. // 5. If a class initializer exists, write out the following:
  462. // 1. The name of the method, <clinit>.
  463. // 2. The modifier of the method, java.lang.reflect.Modifier.STATIC, written as a 32-bit integer.
  464. // 3. The descriptor of the method, ()V.
  465. if (hasClinit) {
  466. dos.writeUTF("<clinit>");
  467. dos.writeInt(Modifier.STATIC);
  468. dos.writeUTF("()V");
  469. }
  470. // for methods and constructors:
  471. // ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED,
  472. // ACC_NATIVE, ACC_ABSTRACT and ACC_STRICT
  473. relevantFlags = Constants.ACC_PUBLIC | Constants.ACC_PRIVATE | Constants.ACC_PROTECTED | Constants.ACC_STATIC
  474. | Constants.ACC_FINAL | Constants.ACC_SYNCHRONIZED | Constants.ACC_NATIVE | Constants.ACC_ABSTRACT
  475. | Constants.ACC_STRICT;
  476. // 6. sorted non-private constructors
  477. for (Method ctor : relevantCtors) {
  478. dos.writeUTF(ctor.getName()); // <init>
  479. dos.writeInt(relevantFlags & ctor.getModifiers());
  480. dos.writeUTF(ctor.getSignature().replace('/', '.'));
  481. }
  482. // 7. sorted non-private methods
  483. for (Method m : relevantMethods) {
  484. dos.writeUTF(m.getName());
  485. dos.writeInt(relevantFlags & m.getModifiers());
  486. dos.writeUTF(m.getSignature().replace('/', '.'));
  487. }
  488. dos.flush();
  489. dos.close();
  490. byte[] bs = baos.toByteArray();
  491. MessageDigest md = MessageDigest.getInstance("SHA");
  492. byte[] result = md.digest(bs);
  493. long suid = 0L;
  494. int pos = result.length > 8 ? 7 : result.length - 1; // use the bytes we have
  495. while (pos >= 0) {
  496. suid = suid << 8 | ((long) result[pos--] & 0xff);
  497. }
  498. // if it was definetly 8 everytime...
  499. // long suid = ((long)(sha[0]&0xff) | (long)(sha[1]&0xff) << 8 |
  500. // (long)(sha[2]&0xff) << 16 | (long)(sha[3]&0xff) << 24 |
  501. // (long)(sha[4]&0xff) << 32 | (long)(sha[5]&0xff) << 40 |
  502. // (long)(sha[6]&0xff) << 48 | (long)(sha[7]&0xff) << 56);
  503. return suid;
  504. } catch (Exception e) {
  505. e.printStackTrace();
  506. throw new RuntimeException("Unable to calculate suid for " + getClassName() + ": " + e.toString());
  507. }
  508. }
  509. private static class FieldComparator implements Comparator<Field> {
  510. public int compare(Field f0, Field f1) {
  511. return f0.getName().compareTo(f1.getName());
  512. }
  513. }
  514. private static class ConstructorComparator implements Comparator<Method> {
  515. public int compare(Method m0, Method m1) {
  516. // can ignore the name...
  517. return (m0).getSignature().compareTo(m1.getSignature());
  518. }
  519. }
  520. private static class MethodComparator implements Comparator<Method> {
  521. public int compare(Method m0, Method m1) {
  522. int result = m0.getName().compareTo(m1.getName());
  523. if (result == 0) {
  524. result = m0.getSignature().compareTo(m1.getSignature());
  525. }
  526. return result;
  527. }
  528. }
  529. public boolean hasAttribute(String attributeName) {
  530. for (Attribute attr : attributesList) {
  531. if (attr.getName().equals(attributeName)) {
  532. return true;
  533. }
  534. }
  535. return false;
  536. }
  537. public Attribute getAttribute(String attributeName) {
  538. for (Attribute attr : attributesList) {
  539. if (attr.getName().equals(attributeName)) {
  540. return attr;
  541. }
  542. }
  543. return null;
  544. }
  545. }