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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  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 (int i = 0; i < interfaces.length; i++) {
  135. addInterface(interfaces[i]);
  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 (int i = 0; i < methods.length; i++) {
  157. addMethod(methods[i]);
  158. }
  159. for (int i = 0; i < fields.length; i++) {
  160. addField(fields[i]);
  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 (int m = 0; m < methods.length; m++)
  322. addMethod(methods[m]);
  323. }
  324. public void setFields(Field[] fs) {
  325. fieldsList.clear();
  326. for (int m = 0; m < fs.length; m++)
  327. addField(fs[m]);
  328. }
  329. public void setMethodAt(Method method, int pos) {
  330. methodsList.set(pos, method);
  331. }
  332. public Method getMethodAt(int pos) {
  333. return methodsList.get(pos);
  334. }
  335. public String[] getInterfaceNames() {
  336. int size = interfaceList.size();
  337. String[] interfaces = new String[size];
  338. interfaceList.toArray(interfaces);
  339. return interfaces;
  340. }
  341. public int[] getInterfaces() {
  342. int size = interfaceList.size();
  343. int[] interfaces = new int[size];
  344. for (int i = 0; i < size; i++)
  345. interfaces[i] = cpool.addClass(interfaceList.get(i));
  346. return interfaces;
  347. }
  348. public Field[] getFields() {
  349. Field[] fields = new Field[fieldsList.size()];
  350. fieldsList.toArray(fields);
  351. return fields;
  352. }
  353. public Collection<Attribute> getAttributes() {
  354. return attributesList;
  355. }
  356. // J5TODO: Should we make calling unpackAnnotations() lazy and put it in here?
  357. public AnnotationGen[] getAnnotations() {
  358. AnnotationGen[] annotations = new AnnotationGen[annotationsList.size()];
  359. annotationsList.toArray(annotations);
  360. return annotations;
  361. }
  362. public ConstantPool getConstantPool() {
  363. return cpool;
  364. }
  365. public void setConstantPool(ConstantPool constant_pool) {
  366. cpool = constant_pool;
  367. }
  368. public void setClassNameIndex(int class_name_index) {
  369. this.classnameIndex = class_name_index;
  370. classname = cpool.getConstantString(class_name_index, Constants.CONSTANT_Class).replace('/', '.');
  371. }
  372. public void setSuperclassNameIndex(int superclass_name_index) {
  373. this.superclassnameIndex = superclass_name_index;
  374. superclassname = cpool.getConstantString(superclass_name_index, Constants.CONSTANT_Class).replace('/', '.');
  375. }
  376. public int getSuperclassNameIndex() {
  377. return superclassnameIndex;
  378. }
  379. public int getClassNameIndex() {
  380. return classnameIndex;
  381. }
  382. @Override
  383. public Object clone() {
  384. try {
  385. return super.clone();
  386. } catch (CloneNotSupportedException e) {
  387. System.err.println(e);
  388. return null;
  389. }
  390. }
  391. public final boolean isAnnotation() {
  392. return (modifiers & Constants.ACC_ANNOTATION) != 0;
  393. }
  394. public final boolean isEnum() {
  395. return (modifiers & Constants.ACC_ENUM) != 0;
  396. }
  397. /**
  398. * Calculate the SerialVersionUID for a class.
  399. */
  400. public long getSUID() {
  401. try {
  402. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  403. DataOutputStream dos = new DataOutputStream(baos);
  404. // 1. classname
  405. dos.writeUTF(getClassName());
  406. // 2. classmodifiers: ACC_PUBLIC, ACC_FINAL, ACC_INTERFACE, and ACC_ABSTRACT
  407. int classmods = 0;
  408. classmods |= (isPublic() ? Constants.ACC_PUBLIC : 0);
  409. classmods |= (isFinal() ? Constants.ACC_FINAL : 0);
  410. classmods |= (isInterface() ? Constants.ACC_INTERFACE : 0);
  411. if (isAbstract()) {
  412. // if an interface then abstract is only set if it has methods
  413. if (isInterface()) {
  414. if (methodsList.size() > 0)
  415. classmods |= Constants.ACC_ABSTRACT;
  416. } else {
  417. classmods |= Constants.ACC_ABSTRACT;
  418. }
  419. }
  420. dos.writeInt(classmods);
  421. // 3. ordered list of interfaces
  422. String[] names = getInterfaceNames();
  423. if (names != null) {
  424. Arrays.sort(names);
  425. for (int i = 0; i < names.length; i++)
  426. dos.writeUTF(names[i]);
  427. }
  428. // 4. ordered list of fields (ignoring private static and private transient fields):
  429. // (relevant modifiers are ACC_PUBLIC, ACC_PRIVATE,
  430. // ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_VOLATILE,
  431. // ACC_TRANSIENT)
  432. List<Field> relevantFields = new ArrayList<Field>();
  433. for (Field field : fieldsList) {
  434. if (!(field.isPrivate() && field.isStatic()) && !(field.isPrivate() && field.isTransient())) {
  435. relevantFields.add(field);
  436. }
  437. }
  438. Collections.sort(relevantFields, new FieldComparator());
  439. int relevantFlags = Constants.ACC_PUBLIC | Constants.ACC_PRIVATE | Constants.ACC_PROTECTED | Constants.ACC_STATIC
  440. | Constants.ACC_FINAL | Constants.ACC_VOLATILE | Constants.ACC_TRANSIENT;
  441. for (Field f : relevantFields) {
  442. dos.writeUTF(f.getName());
  443. dos.writeInt(relevantFlags & f.getModifiers());
  444. dos.writeUTF(f.getType().getSignature());
  445. }
  446. // some up front method processing: discover clinit, init and ordinary methods of interest:
  447. List<Method> relevantMethods = new ArrayList<Method>();
  448. List<Method> relevantCtors = new ArrayList<Method>();
  449. boolean hasClinit = false;
  450. for (Method m : methodsList) {
  451. boolean couldBeInitializer = m.getName().charAt(0) == '<';
  452. if (couldBeInitializer && m.getName().equals("<clinit>")) {
  453. hasClinit = true;
  454. } else if (couldBeInitializer && m.getName().equals("<init>")) {
  455. if (!m.isPrivate())
  456. relevantCtors.add(m);
  457. } else {
  458. if (!m.isPrivate())
  459. relevantMethods.add(m);
  460. }
  461. }
  462. Collections.sort(relevantCtors, new ConstructorComparator());
  463. Collections.sort(relevantMethods, new MethodComparator());
  464. // 5. If a class initializer exists, write out the following:
  465. // 1. The name of the method, <clinit>.
  466. // 2. The modifier of the method, java.lang.reflect.Modifier.STATIC, written as a 32-bit integer.
  467. // 3. The descriptor of the method, ()V.
  468. if (hasClinit) {
  469. dos.writeUTF("<clinit>");
  470. dos.writeInt(Modifier.STATIC);
  471. dos.writeUTF("()V");
  472. }
  473. // for methods and constructors:
  474. // ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED,
  475. // ACC_NATIVE, ACC_ABSTRACT and ACC_STRICT
  476. relevantFlags = Constants.ACC_PUBLIC | Constants.ACC_PRIVATE | Constants.ACC_PROTECTED | Constants.ACC_STATIC
  477. | Constants.ACC_FINAL | Constants.ACC_SYNCHRONIZED | Constants.ACC_NATIVE | Constants.ACC_ABSTRACT
  478. | Constants.ACC_STRICT;
  479. // 6. sorted non-private constructors
  480. for (Method ctor : relevantCtors) {
  481. dos.writeUTF(ctor.getName()); // <init>
  482. dos.writeInt(relevantFlags & ctor.getModifiers());
  483. dos.writeUTF(ctor.getSignature().replace('/', '.'));
  484. }
  485. // 7. sorted non-private methods
  486. for (Method m : relevantMethods) {
  487. dos.writeUTF(m.getName());
  488. dos.writeInt(relevantFlags & m.getModifiers());
  489. dos.writeUTF(m.getSignature().replace('/', '.'));
  490. }
  491. dos.flush();
  492. dos.close();
  493. byte[] bs = baos.toByteArray();
  494. MessageDigest md = MessageDigest.getInstance("SHA");
  495. byte[] result = md.digest(bs);
  496. long suid = 0L;
  497. int pos = result.length > 8 ? 7 : result.length - 1; // use the bytes we have
  498. while (pos >= 0) {
  499. suid = suid << 8 | ((long) result[pos--] & 0xff);
  500. }
  501. // if it was definetly 8 everytime...
  502. // long suid = ((long)(sha[0]&0xff) | (long)(sha[1]&0xff) << 8 |
  503. // (long)(sha[2]&0xff) << 16 | (long)(sha[3]&0xff) << 24 |
  504. // (long)(sha[4]&0xff) << 32 | (long)(sha[5]&0xff) << 40 |
  505. // (long)(sha[6]&0xff) << 48 | (long)(sha[7]&0xff) << 56);
  506. return suid;
  507. } catch (Exception e) {
  508. e.printStackTrace();
  509. throw new RuntimeException("Unable to calculate suid for " + getClassName() + ": " + e.toString());
  510. }
  511. }
  512. private static class FieldComparator implements Comparator<Field> {
  513. public int compare(Field f0, Field f1) {
  514. return f0.getName().compareTo(f1.getName());
  515. }
  516. }
  517. private static class ConstructorComparator implements Comparator<Method> {
  518. public int compare(Method m0, Method m1) {
  519. // can ignore the name...
  520. return (m0).getSignature().compareTo(m1.getSignature());
  521. }
  522. }
  523. private static class MethodComparator implements Comparator<Method> {
  524. public int compare(Method m0, Method m1) {
  525. int result = m0.getName().compareTo(m1.getName());
  526. if (result == 0) {
  527. result = m0.getSignature().compareTo(m1.getSignature());
  528. }
  529. return result;
  530. }
  531. }
  532. public boolean hasAttribute(String attributeName) {
  533. for (Attribute attr : attributesList) {
  534. if (attr.getName().equals(attributeName)) {
  535. return true;
  536. }
  537. }
  538. return false;
  539. }
  540. public Attribute getAttribute(String attributeName) {
  541. for (Attribute attr : attributesList) {
  542. if (attr.getName().equals(attributeName)) {
  543. return attr;
  544. }
  545. }
  546. return null;
  547. }
  548. }