001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.bytecode;
031:
032: import com.caucho.vfs.ReadStream;
033: import com.caucho.vfs.Vfs;
034: import com.caucho.vfs.WriteStream;
035:
036: import java.io.ByteArrayInputStream;
037: import java.io.IOException;
038: import java.io.InputStream;
039: import java.lang.reflect.Modifier;
040: import java.net.URL;
041: import java.util.ArrayList;
042: import java.util.logging.Level;
043: import java.util.logging.Logger;
044:
045: /**
046: * Represents a java class.
047: */
048: public class JavaClass extends JClass {
049: static private final Logger log = Logger.getLogger(JavaClass.class
050: .getName());
051:
052: public static final int MAGIC = 0xcafebabe;
053:
054: public static final int ACC_PUBLIC = 0x0001;
055: public static final int ACC_PRIVATE = 0x0002;
056: public static final int ACC_PROTECTED = 0x0004;
057: public static final int ACC_STATIC = 0x0008;
058: public static final int ACC_FINAL = 0x0010;
059: public static final int ACC_SUPER = 0x0020;
060:
061: private JavaClassLoader _loader;
062:
063: private URL _url;
064:
065: private int _major;
066: private int _minor;
067:
068: private ConstantPool _constantPool = new ConstantPool();
069:
070: private int _accessFlags;
071:
072: private String _this Class;
073: private String _super Class;
074:
075: private ArrayList<String> _interfaces = new ArrayList<String>();
076:
077: private ArrayList<JavaField> _fields = new ArrayList<JavaField>();
078:
079: private ArrayList<JavaMethod> _methods = new ArrayList<JavaMethod>();
080:
081: private ArrayList<Attribute> _attributes = new ArrayList<Attribute>();
082:
083: private JavaAnnotation[] _annotations;
084:
085: private boolean _isWrite;
086:
087: public JavaClass(JavaClassLoader loader) {
088: if (loader == null)
089: throw new NullPointerException();
090:
091: _loader = loader;
092: }
093:
094: /**
095: * Returns the loader.
096: */
097: public JavaClassLoader getClassLoader() {
098: return _loader;
099: }
100:
101: public void setWrite(boolean isWrite) {
102: _isWrite = isWrite;
103: }
104:
105: /**
106: * Sets the URL.
107: */
108: public void setURL(URL url) {
109: _url = url;
110: }
111:
112: /**
113: * Sets the major identifier of the class file.
114: */
115: public void setMajor(int major) {
116: _major = major;
117: }
118:
119: /**
120: * Gets the major identifier of the class file.
121: */
122: public int getMajor() {
123: return _major;
124: }
125:
126: /**
127: * Sets the minor identifier of the class file.
128: */
129: public void setMinor(int minor) {
130: _minor = minor;
131: }
132:
133: /**
134: * Gets the minor identifier of the class file.
135: */
136: public int getMinor() {
137: return _minor;
138: }
139:
140: /**
141: * Returns the class's constant pool.
142: */
143: public ConstantPool getConstantPool() {
144: return _constantPool;
145: }
146:
147: /**
148: * Sets the access flags.
149: */
150: public void setAccessFlags(int flags) {
151: _accessFlags = flags;
152: }
153:
154: /**
155: * Gets the access flags.
156: */
157: public int getAccessFlags() {
158: lazyLoad();
159:
160: return _accessFlags;
161: }
162:
163: /**
164: * Sets this class.
165: */
166: public void setThisClass(String className) {
167: _this Class = className;
168:
169: if (_isWrite)
170: getConstantPool().addClass(className);
171: }
172:
173: /**
174: * Gets this class name.
175: */
176: public String getThisClass() {
177: return _this Class;
178: }
179:
180: /**
181: * Sets the super class.
182: */
183: public void setSuperClass(String className) {
184: _super Class = className;
185:
186: getConstantPool().addClass(className);
187: }
188:
189: /**
190: * Gets the super class name.
191: */
192: public String getSuperClassName() {
193: lazyLoad();
194:
195: return _super Class;
196: }
197:
198: /**
199: * Gets the super class name.
200: */
201: public JClass getSuperClass() {
202: lazyLoad();
203:
204: if (_super Class == null)
205: return null;
206: else
207: return getClassLoader().forName(
208: _super Class.replace('/', '.'));
209: }
210:
211: /**
212: * Returns true for a final class.
213: */
214: public boolean isFinal() {
215: return Modifier.isFinal(getAccessFlags());
216: }
217:
218: /**
219: * Returns true for an abstract class.
220: */
221: public boolean isAbstract() {
222: return Modifier.isAbstract(getAccessFlags());
223: }
224:
225: /**
226: * Returns true for a public class.
227: */
228: public boolean isPublic() {
229: return Modifier.isPublic(getAccessFlags());
230: }
231:
232: /**
233: * Returns true for a primitive class.
234: */
235: public boolean isPrimitive() {
236: return false;
237: }
238:
239: /**
240: * Adds an interface.
241: */
242: public void addInterface(String className) {
243: _interfaces.add(className);
244:
245: if (_isWrite)
246: getConstantPool().addClass(className);
247: }
248:
249: /**
250: * Adds an interface.
251: */
252: public ArrayList<String> getInterfaceNames() {
253: return _interfaces;
254: }
255:
256: /**
257: * Gets the interfaces.
258: */
259: public JClass[] getInterfaces() {
260: lazyLoad();
261:
262: JClass[] interfaces = new JClass[_interfaces.size()];
263:
264: for (int i = 0; i < _interfaces.size(); i++) {
265: String name = _interfaces.get(i);
266: name = name.replace('/', '.');
267:
268: interfaces[i] = getClassLoader().forName(name);
269: }
270:
271: return interfaces;
272: }
273:
274: /**
275: * Adds a field
276: */
277: public void addField(JavaField field) {
278: _fields.add(field);
279: }
280:
281: public JavaField createField(String name, String descriptor) {
282: if (!_isWrite)
283: throw new IllegalStateException(
284: "create field requires write");
285:
286: JavaField jField = new JavaField();
287: jField.setWrite(true);
288: jField.setJavaClass(this );
289:
290: jField.setName(name);
291: jField.setDescriptor(descriptor);
292:
293: _fields.add(jField);
294:
295: return jField;
296: }
297:
298: /**
299: * Returns the fields.
300: */
301: public ArrayList<JavaField> getFieldList() {
302: lazyLoad();
303:
304: return _fields;
305: }
306:
307: /**
308: * Returns a fields.
309: */
310: public JavaField getField(String name) {
311: ArrayList<JavaField> fieldList = getFieldList();
312:
313: for (int i = 0; i < fieldList.size(); i++) {
314: JavaField field = fieldList.get(i);
315:
316: if (field.getName().equals(name))
317: return field;
318: }
319:
320: return null;
321: }
322:
323: /**
324: * Adds a method
325: */
326: public void addMethod(JavaMethod method) {
327: _methods.add(method);
328: }
329:
330: public JavaMethod createMethod(String name, String descriptor) {
331: if (!_isWrite)
332: throw new IllegalStateException(
333: "create method requires write");
334:
335: JavaMethod jMethod = new JavaMethod();
336: jMethod.setWrite(true);
337: jMethod.setJavaClass(this );
338:
339: jMethod.setName(name);
340: jMethod.setDescriptor(descriptor);
341:
342: _methods.add(jMethod);
343:
344: return jMethod;
345: }
346:
347: /**
348: * Returns the methods.
349: */
350: public ArrayList<JavaMethod> getMethodList() {
351: lazyLoad();
352:
353: return _methods;
354: }
355:
356: /**
357: * Returns true for an array.
358: */
359: public boolean isArray() {
360: return false;
361: }
362:
363: /**
364: * Returns true for an interface.
365: */
366: public boolean isInterface() {
367: lazyLoad();
368:
369: return Modifier.isInterface(_accessFlags);
370: }
371:
372: /**
373: * Returns a method.
374: */
375: public JavaMethod getMethod(String name) {
376: ArrayList<JavaMethod> methodList = getMethodList();
377:
378: for (int i = 0; i < methodList.size(); i++) {
379: JavaMethod method = methodList.get(i);
380:
381: if (method.getName().equals(name))
382: return method;
383: }
384:
385: return null;
386: }
387:
388: /**
389: * Finds a method.
390: */
391: public JavaMethod findMethod(String name, String descriptor) {
392: ArrayList<JavaMethod> methodList = getMethodList();
393:
394: for (int i = 0; i < methodList.size(); i++) {
395: JavaMethod method = methodList.get(i);
396:
397: if (method.getName().equals(name)
398: && method.getDescriptor().equals(descriptor))
399: return method;
400: }
401:
402: return null;
403: }
404:
405: /**
406: * Adds an attribute
407: */
408: public void addAttribute(Attribute attr) {
409: _attributes.add(attr);
410:
411: attr.addConstants(this );
412: }
413:
414: /**
415: * Returns the methods.
416: */
417: public ArrayList<Attribute> getAttributeList() {
418: lazyLoad();
419:
420: return _attributes;
421: }
422:
423: /**
424: * Returns the attribute.
425: */
426: public Attribute getAttribute(String name) {
427: ArrayList<Attribute> attributeList = getAttributeList();
428:
429: for (int i = attributeList.size() - 1; i >= 0; i--) {
430: Attribute attr = attributeList.get(i);
431:
432: if (attr.getName().equals(name))
433: return attr;
434: }
435:
436: return null;
437: }
438:
439: //
440: // JClass methods.
441: //
442:
443: /**
444: * Returns the class-equivalent name.
445: */
446: public String getName() {
447: return getThisClass().replace('/', '.');
448: }
449:
450: /**
451: * Returns true if the class is assignable from the argument.
452: */
453: public boolean isAssignableFrom(JClass cl) {
454: if (getName().equals(cl.getName()))
455: return true;
456:
457: JClass[] ifc = cl.getInterfaces();
458:
459: for (int i = 0; i < ifc.length; i++) {
460: if (isAssignableFrom(ifc[i]))
461: return true;
462: }
463:
464: if (cl.getSuperClass() != null)
465: return isAssignableFrom(cl.getSuperClass());
466: else
467: return false;
468: }
469:
470: /**
471: * Returns true if the class is assignable from the argument.
472: */
473: public boolean isAssignableFrom(Class cl) {
474: if (getName().equals(cl.getName()))
475: return true;
476:
477: Class[] ifc = cl.getInterfaces();
478:
479: for (int i = 0; i < ifc.length; i++) {
480: if (isAssignableFrom(ifc[i]))
481: return true;
482: }
483:
484: if (cl.getSuperclass() != null)
485: return isAssignableFrom(cl.getSuperclass());
486: else
487: return false;
488: }
489:
490: /**
491: * Returns true if the class is assignable from the argument.
492: */
493: public boolean isAssignableTo(Class cl) {
494: if (getName().equals(cl.getName()))
495: return true;
496:
497: JClass[] ifc = getInterfaces();
498:
499: for (int i = 0; i < ifc.length; i++) {
500: if (ifc[i].isAssignableTo(cl))
501: return true;
502: }
503:
504: if (getSuperClass() != null)
505: return getSuperClass().isAssignableTo(cl);
506: else
507: return false;
508: }
509:
510: /**
511: * Returns the array of declared methods.
512: */
513: public JMethod[] getDeclaredMethods() {
514: ArrayList<JavaMethod> methodList = getMethodList();
515:
516: JMethod[] methods = new JMethod[methodList.size()];
517:
518: methodList.toArray(methods);
519:
520: return methods;
521: }
522:
523: /**
524: * Returns the array of declared methods.
525: */
526: public JMethod[] getConstructors() {
527: ArrayList<JavaMethod> ctorList = new ArrayList<JavaMethod>();
528:
529: for (JavaMethod method : getMethodList()) {
530: if (method.getName().equals("<init>"))
531: ctorList.add(method);
532: }
533:
534: JMethod[] methods = new JMethod[ctorList.size()];
535:
536: ctorList.toArray(methods);
537:
538: return methods;
539: }
540:
541: /**
542: * Returns the matching method
543: */
544: public JMethod getMethod(String name, JClass[] paramTypes) {
545: loop: for (JMethod method : getMethods()) {
546: if (!method.getName().equals(name))
547: continue;
548:
549: JClass[] mParamTypes = method.getParameterTypes();
550: if (mParamTypes.length != paramTypes.length)
551: continue;
552:
553: for (int i = 0; i < paramTypes.length; i++) {
554: if (!paramTypes[i].getName().equals(
555: mParamTypes[i].getName()))
556: continue loop;
557: }
558:
559: return method;
560: }
561:
562: return null;
563: }
564:
565: /**
566: * Returns the matching method
567: */
568: public JMethod[] getMethods() {
569: ArrayList<JMethod> methodList = new ArrayList<JMethod>();
570:
571: getMethods(methodList);
572:
573: JMethod[] methods = new JMethod[methodList.size()];
574: methodList.toArray(methods);
575:
576: return methods;
577: }
578:
579: /**
580: * Returns the matching method
581: */
582: private void getMethods(ArrayList<JMethod> methodList) {
583: for (JMethod method : getDeclaredMethods()) {
584: if (!methodList.contains(method))
585: methodList.add(method);
586: }
587:
588: if (getSuperClass() != null) {
589: for (JMethod method : getSuperClass().getMethods()) {
590: if (!methodList.contains(method))
591: methodList.add(method);
592: }
593: }
594: }
595:
596: /**
597: * Returns the array of declared fields.
598: */
599: public JField[] getDeclaredFields() {
600: ArrayList<JavaField> fieldList = getFieldList();
601:
602: JField[] fields = new JField[fieldList.size()];
603:
604: fieldList.toArray(fields);
605:
606: return fields;
607: }
608:
609: /**
610: * Returns the array of fields.
611: */
612: public JField[] getFields() {
613: ArrayList<JField> fieldList = new ArrayList<JField>();
614:
615: getFields(fieldList);
616:
617: JField[] fields = new JField[fieldList.size()];
618: fieldList.toArray(fields);
619:
620: return fields;
621: }
622:
623: /**
624: * Returns all the fields
625: */
626: private void getFields(ArrayList<JField> fieldList) {
627: for (JField field : getDeclaredFields()) {
628: if (!fieldList.contains(field))
629: fieldList.add(field);
630: }
631:
632: if (getSuperClass() != null) {
633: for (JField field : getSuperClass().getFields()) {
634: if (!fieldList.contains(field))
635: fieldList.add(field);
636: }
637: }
638: }
639:
640: /**
641: * Returns the declared annotations.
642: */
643: public JAnnotation[] getDeclaredAnnotations() {
644: if (_annotations == null) {
645: Attribute attr = getAttribute("RuntimeVisibleAnnotations");
646:
647: if (attr instanceof OpaqueAttribute) {
648: byte[] buffer = ((OpaqueAttribute) attr).getValue();
649:
650: try {
651: ByteArrayInputStream is = new ByteArrayInputStream(
652: buffer);
653:
654: ConstantPool cp = getConstantPool();
655:
656: _annotations = JavaAnnotation.parseAnnotations(is,
657: cp, getClassLoader());
658: } catch (IOException e) {
659: log.log(Level.FINER, e.toString(), e);
660: }
661: }
662:
663: if (_annotations == null) {
664: _annotations = new JavaAnnotation[0];
665: }
666: }
667:
668: return _annotations;
669: }
670:
671: /**
672: * Returns the annotation.
673: */
674: public JAnnotation getAnnotation(String className) {
675: JAnnotation[] annList = getDeclaredAnnotations();
676:
677: for (int i = 0; i < annList.length; i++) {
678: if (annList[i].getType().equals(className))
679: return annList[i];
680: }
681:
682: return null;
683: }
684:
685: /**
686: * Lazily load the class.
687: */
688: private void lazyLoad() {
689: if (_major > 0)
690: return;
691:
692: try {
693: if (_url == null)
694: throw new IllegalStateException();
695:
696: InputStream is = _url.openStream();
697: ReadStream rs = Vfs.openRead(is);
698: try {
699: _major = 1;
700:
701: ByteCodeParser parser = new ByteCodeParser();
702: parser.setClassLoader(_loader);
703: parser.setJavaClass(this );
704:
705: parser.parse(rs);
706: } finally {
707: rs.close();
708: is.close();
709: }
710: } catch (RuntimeException e) {
711: throw e;
712: } catch (Exception e) {
713: throw new RuntimeException(e);
714: }
715: }
716:
717: /**
718: * Writes the class to the output.
719: */
720: public void write(WriteStream os) throws IOException {
721: ByteCodeWriter out = new ByteCodeWriter(os, this );
722:
723: out.writeInt(MAGIC);
724: out.writeShort(_minor);
725: out.writeShort(_major);
726:
727: _constantPool.write(out);
728:
729: out.writeShort(_accessFlags);
730: out.writeClass(_this Class);
731: out.writeClass(_super Class);
732:
733: out.writeShort(_interfaces.size());
734: for (int i = 0; i < _interfaces.size(); i++) {
735: String className = _interfaces.get(i);
736:
737: out.writeClass(className);
738: }
739:
740: out.writeShort(_fields.size());
741: for (int i = 0; i < _fields.size(); i++) {
742: JavaField field = _fields.get(i);
743:
744: field.write(out);
745: }
746:
747: out.writeShort(_methods.size());
748: for (int i = 0; i < _methods.size(); i++) {
749: JavaMethod method = _methods.get(i);
750:
751: method.write(out);
752: }
753:
754: out.writeShort(_attributes.size());
755: for (int i = 0; i < _attributes.size(); i++) {
756: Attribute attr = _attributes.get(i);
757:
758: attr.write(out);
759: }
760: }
761:
762: public String toString() {
763: return "JavaClass[" + _this Class + "]";
764: }
765: }
|