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

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