001: /*
002: * sqlc 1
003: * SQL Compiler
004: * Copyright (C) 2003 Hammurapi Group
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2 of the License, or (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * URL: http://www.hammurapi.biz/products/sqlc/index.html
021: * e-Mail: support@hammurapi.biz
022: */
023: package biz.hammurapi.codegen;
024:
025: import java.io.File;
026: import java.io.IOException;
027: import java.io.StringReader;
028: import java.util.ArrayList;
029: import java.util.Collection;
030: import java.util.HashMap;
031: import java.util.HashSet;
032: import java.util.Iterator;
033: import java.util.Map;
034: import java.util.Properties;
035: import java.util.StringTokenizer;
036:
037: import org.apache.bcel.Constants;
038: import org.apache.bcel.Repository;
039: import org.apache.bcel.classfile.Code;
040: import org.apache.bcel.classfile.Field;
041: import org.apache.bcel.classfile.JavaClass;
042: import org.apache.bcel.classfile.Method;
043: import org.apache.bcel.classfile.Utility;
044: import org.apache.bcel.generic.ClassGen;
045: import org.apache.bcel.generic.FieldGen;
046: import org.apache.bcel.generic.FieldInstruction;
047: import org.apache.bcel.generic.InstructionFactory;
048: import org.apache.bcel.generic.InstructionList;
049: import org.apache.bcel.generic.InvokeInstruction;
050: import org.apache.bcel.generic.MethodGen;
051: import org.apache.bcel.generic.ObjectType;
052: import org.apache.bcel.generic.Type;
053: import org.apache.bcel.util.ClassLoaderRepository;
054: import org.apache.bcel.verifier.VerificationResult;
055: import org.apache.bcel.verifier.Verifier;
056: import org.apache.bcel.verifier.VerifierFactory;
057:
058: import biz.hammurapi.codegen.JavaLexer;
059: import biz.hammurapi.codegen.JavaRecognizer;
060: import biz.hammurapi.codegen.JavaTokenTypes;
061:
062: import antlr.ASTFactory;
063: import antlr.RecognitionException;
064: import antlr.TokenStreamException;
065: import antlr.collections.AST;
066: import biz.hammurapi.antlr.AstVisualizable;
067: import biz.hammurapi.swing.Browser;
068: import biz.hammurapi.util.Parameter;
069:
070: /**
071: * @author Pavel Vlasov
072: * @version $Revision: 1.9 $
073: */
074: public class ClassGeneratorBase {
075: private Map methods = new HashMap();
076: private InstructionFactory iFactory;
077:
078: /**
079: *
080: * @param signature Method signature
081: * @param rtthc Method return type and throws
082: * @return true - method shall be added
083: * @throws GenerationException If method exists, but return type or throws are different
084: */
085: protected boolean checkMethod(Object signature, String returnType,
086: Collection throwsList) throws GenerationException {
087: Object ertthc = methods.get(signature);
088: ArrayList rtthc = new ArrayList();
089: rtthc.add(returnType);
090: rtthc.add(new HashSet(throwsList));
091:
092: if (ertthc == null) {
093: methods.put(signature, rtthc);
094: return true;
095: } else if (ertthc.equals(rtthc)) {
096: return false;
097: } else {
098: throw new GenerationException(
099: cg.getClassName()
100: + ": Attempt to add a method with the same signature "
101: + signature
102: + " but different return type or throws clause. Existing: "
103: + ertthc + ", being added: " + rtthc);
104: }
105: }
106:
107: public static short modifiers(Collection modifiers)
108: throws GenerationException {
109: short ret = 0;
110: Iterator it = modifiers.iterator();
111: Z: while (it.hasNext()) {
112: String modifier = (String) it.next();
113: for (int i = 0; i < Constants.ACCESS_NAMES.length; i++) {
114: if (Constants.ACCESS_NAMES[i].equals(modifier)) {
115: ret |= 1 << i;
116: continue Z;
117: }
118: }
119: throw new GenerationException("Unknown modifier: "
120: + modifier);
121: }
122:
123: return ret;
124: }
125:
126: protected GenerationListener listener;
127: protected ClassGen cg;
128:
129: /**
130: * @return
131: * @throws GenerationException
132: */
133: public JavaClass getJavaClass() {
134: JavaClass javaClass = cg.getJavaClass();
135: return javaClass;
136: }
137:
138: /**
139: * Verifies collection of classes and returns colleciton of error messages.
140: */
141: public static Collection verify(Collection javaClasses,
142: ClassLoader classLoader) {
143: ClassLoaderRepository classLoaderRepository = new ClassLoaderRepository(
144: classLoader == null ? ClassGeneratorBase.class
145: .getClassLoader() : classLoader);
146: Iterator it = javaClasses.iterator();
147: while (it.hasNext()) {
148: classLoaderRepository.storeClass((JavaClass) it.next());
149: }
150: Repository.setRepository(classLoaderRepository);
151:
152: Collection ret = new ArrayList();
153: it = javaClasses.iterator();
154: while (it.hasNext()) {
155: JavaClass javaClass = (JavaClass) it.next();
156: Verifier v = VerifierFactory.getVerifier(javaClass
157: .getClassName());
158: VerificationResult result = v.doPass1();
159: if (result.getStatus() == VerificationResult.VERIFIED_REJECTED) {
160: ret.add("Verification pass 1 failed for class "
161: + javaClass.getClassName() + ": "
162: + result.getMessage());
163: }
164:
165: result = v.doPass2();
166: if (result.getStatus() == VerificationResult.VERIFIED_REJECTED) {
167: ret.add("Verification pass 2 failed for class "
168: + javaClass.getClassName() + ": "
169: + result.getMessage());
170: }
171:
172: for (int i = 0, j = javaClass.getMethods().length; i < j; i++) {
173: result = v.doPass3a(i);
174: if (result.getStatus() == VerificationResult.VERIFIED_REJECTED) {
175: ret.add("Verification pass 3a failed for class "
176: + javaClass.getClassName() + ", method "
177: + javaClass.getMethods()[i] + ": "
178: + result.getMessage());
179: }
180:
181: result = v.doPass3b(i);
182: if (result.getStatus() == VerificationResult.VERIFIED_REJECTED) {
183: ret.add("Verification pass 3b failed for class "
184: + javaClass.getClassName() + ", method "
185: + javaClass.getMethods()[i] + ": "
186: + result.getMessage());
187: }
188: }
189: }
190: return ret;
191: }
192:
193: public ClassGen getClassGen() {
194: return cg;
195: }
196:
197: public InstructionFactory getInstructionFactory() {
198: if (iFactory == null) {
199: iFactory = new InstructionFactory(cg);
200: }
201: return iFactory;
202: }
203:
204: public void save(File dir) throws IOException {
205: JavaClass jc = getJavaClass();
206: File out = new File(dir, jc.getClassName().replace('.',
207: File.separatorChar)
208: + ".class");
209: File parentDir = out.getParentFile();
210: if (parentDir != null) {
211: parentDir.mkdirs();
212: }
213: jc.dump(out);
214: }
215:
216: /**
217: * Copies instructions of existing method.
218: * @param clazz
219: * @param signature E.g. <code>setName(java.lang.String)</code>
220: * @return
221: * @throws ClassNotFoundException
222: * @throws NoSuchMethodException
223: */
224: public InstructionList cloneInstructions(java.lang.Class clazz,
225: String signature) throws ClassNotFoundException,
226: NoSuchMethodException {
227: Method m = getMethod(clazz, signature);
228: return new InstructionList(m.getCode().getCode());
229: }
230:
231: /**
232: * @param clazz
233: * @param signature
234: * @throws ClassNotFoundException
235: * @throws NoSuchMethodException
236: */
237: public static Method getMethod(java.lang.Class clazz,
238: String signature) throws ClassNotFoundException,
239: NoSuchMethodException {
240: int idx = signature.indexOf("(");
241: if (idx == -1) {
242: throw new IllegalArgumentException("Bad method signature: "
243: + signature);
244: }
245: int edx = signature.indexOf(")", idx);
246: if (edx == -1) {
247: throw new IllegalArgumentException("Bad method signature: "
248: + signature);
249: }
250: String name = signature.substring(0, idx);
251: StringTokenizer st = new StringTokenizer(signature.substring(
252: idx + 1, edx), " \t,");
253: Type[] args = new Type[st.countTokens()];
254: for (int i = 0; st.hasMoreTokens(); i++) {
255: args[i] = Type
256: .getType(Utility.getSignature(st.nextToken()));
257: }
258:
259: ClassLoaderRepository r = new ClassLoaderRepository(clazz
260: .getClassLoader());
261: JavaClass jc = r.loadClass(clazz);
262: Method[] ma = jc.getMethods();
263: Z: for (int i = 0; i < ma.length; i++) {
264: Type[] argumentTypes = ma[i].getArgumentTypes();
265: if (name.equals(ma[i].getName())
266: && argumentTypes.length == args.length) {
267: for (int j = 0; j < args.length; j++) {
268: if (!args[j].equals(argumentTypes[j])) {
269: continue Z;
270: }
271: }
272: return ma[i];
273: }
274: }
275:
276: throw new NoSuchMethodException("Method " + signature
277: + " is not found in class " + clazz);
278: }
279:
280: public static void printMethod(java.lang.Class clazz,
281: String signature) throws ClassNotFoundException,
282: NoSuchMethodException {
283: printMethod(getMethod(clazz, signature));
284: }
285:
286: public static String concat(String packageName, String className) {
287: if (packageName == null || packageName.length() == 0) {
288: return className;
289: }
290: return packageName + "." + className;
291: }
292:
293: /**
294: * @param methods
295: * @param i
296: */
297: public static void printMethod(Method method) {
298: System.out.println("\n-------------------------");
299: System.out.println(method);
300:
301: Code code = method.getCode();
302: if (code != null) {// Non-abstract method
303: System.out.println(code);
304: }
305: }
306:
307: /**
308: * Creates invoke instruction for the method in the current class.
309: * @param signature
310: * @param type
311: * @return
312: * @throws GenerationException
313: */
314: public InvokeInstruction createInvoke(String signature,
315: Collection parameters, short type)
316: throws GenerationException {
317: return createInvoke(cg.getClassName(), signature, parameters,
318: type);
319: }
320:
321: /**
322: * Creates invoke instruction for the method in the current class.
323: * @param signature E.g. <code>int getA()</code>
324: * @param type
325: * @return
326: * @throws GenerationException
327: */
328: public InvokeInstruction createInvoke(String className,
329: final String signature, Collection parameters, short type)
330: throws GenerationException {
331: AST ast = invocation(signature);
332: String returnType = toString(ast);
333: String name = ast.getNextSibling().toString();
334:
335: Type[] args = new Type[ast.getNextSibling().getNextSibling()
336: .getNumberOfChildren()
337: + (parameters == null ? 0 : parameters.size())];
338: int i = 0;
339: for (AST pnode = ast.getNextSibling().getNextSibling()
340: .getFirstChild(); pnode != null; pnode = pnode
341: .getNextSibling()) {
342: args[i++] = java2BcelType(toString(pnode));
343: }
344: if (parameters != null) {
345: Iterator it = parameters.iterator();
346: while (it.hasNext()) {
347: Object o = it.next();
348: if (o instanceof Parameter) {
349: args[i++] = java2BcelType(((Parameter) o).getType());
350: } else {
351: args[i++] = java2BcelType(o.toString());
352: }
353: }
354: }
355:
356: return getInstructionFactory().createInvoke(className, name,
357: java2BcelType(returnType), args, type);
358: }
359:
360: /**
361: * @param type
362: * @return
363: */
364: public static Type java2BcelType(String type) {
365: return Type.getType(Utility.getSignature(type));
366: }
367:
368: protected static AST typeDefinition(String definition)
369: throws GenerationException {
370: try {
371: JavaLexer lexer = new JavaLexer(
372: new StringReader(definition));
373: JavaRecognizer parser = new JavaRecognizer(lexer);
374: parser.typeDefinition();
375: return parser.getAST();
376: } catch (TokenStreamException e) {
377: throw new GenerationException(e);
378: } catch (RecognitionException e) {
379: throw new GenerationException(e);
380: }
381: }
382:
383: protected static void showTypeDefinition(String typeDefinition)
384: throws GenerationException {
385: try {
386: JavaLexer lexer = new JavaLexer(new StringReader(
387: typeDefinition)) {
388: {
389: tokenObjectClass = biz.hammurapi.antlr.Token.class;
390: }
391: };
392: JavaRecognizer parser = new JavaRecognizer(lexer);
393: parser.setASTFactory(factory);
394: parser.field();
395: show(parser);
396: } catch (TokenStreamException e) {
397: throw new GenerationException(e);
398: } catch (RecognitionException e) {
399: throw new GenerationException(e);
400: }
401: }
402:
403: protected class FieldDescriptor {
404: Type type;
405: boolean isStatic;
406:
407: public FieldDescriptor(Type type, boolean isStatic) {
408: super ();
409: this .type = type;
410: this .isStatic = isStatic;
411: }
412: }
413:
414: public void addField(String declaration, String description,
415: Properties attributes) throws GenerationException {
416: for (AST ast = ClassGeneratorBase.field(declaration); ast != null; ast = ast
417: .getNextSibling()) {
418: if (ast.getType() == JavaTokenTypes.VARIABLE_DEF) {
419: Collection modifiers = new HashSet();
420: String name = null;
421: String type = null;
422: for (AST node = ast.getFirstChild(); node != null; node = node
423: .getNextSibling()) {
424: switch (node.getType()) {
425: case JavaTokenTypes.MODIFIERS:
426: for (AST child = node.getFirstChild(); child != null; child = child
427: .getNextSibling()) {
428: modifiers.add(child.getText());
429: }
430: break;
431: case JavaTokenTypes.IDENT:
432: name = node.getText();
433: break;
434: case JavaTokenTypes.TYPE:
435: type = ClassGeneratorBase.toString(node
436: .getFirstChild());
437: break;
438: default:
439: throw new GenerationException(
440: "Unexpected node: " + node);
441: }
442: }
443:
444: if (type == null) {
445: throw new GenerationException(
446: "Invalid field declaration, type is null: "
447: + declaration);
448: }
449:
450: if (name == null) {
451: throw new GenerationException(
452: "Invalid field declaration, name is null: "
453: + declaration);
454: }
455:
456: Type bcelType = java2BcelType(type);
457: FieldGen fg = new FieldGen(modifiers(modifiers),
458: bcelType, name, cg.getConstantPool());
459: Field field = fg.getField();
460: cg.addField(field);
461: if (fields.containsKey(name)) {
462: throw new GenerationException("Duplicate field "
463: + name + " in class " + cg.getClassName());
464: }
465: fields.put(name, new FieldDescriptor(bcelType,
466: modifiers.contains("static")));
467: if (listener != null
468: && (cg.isPublic() || cg.isProtected())) {
469: listener.onField(cg.getClassName(), declaration,
470: description, attributes);
471: }
472: } else {
473: throw new GenerationException("Invalid node type "
474: + ast.getType() + " in definition '"
475: + declaration + "'");
476: }
477: }
478: }
479:
480: public boolean hasField(String name) {
481: return fields.containsKey(name);
482: }
483:
484: protected static AST field(String definition)
485: throws GenerationException {
486: try {
487: JavaLexer lexer = new JavaLexer(
488: new StringReader(definition));
489: JavaRecognizer parser = new JavaRecognizer(lexer);
490: parser.field();
491: return parser.getAST();
492: } catch (TokenStreamException e) {
493: throw new GenerationException(definition, e);
494: } catch (RecognitionException e) {
495: throw new GenerationException(definition, e);
496: }
497: }
498:
499: private static ASTFactory factory = new ASTFactory() {
500: {
501: super .setASTNodeClass(biz.hammurapi.antlr.AST.class);
502: }
503: };
504:
505: protected static void showField(String field)
506: throws GenerationException {
507: try {
508: JavaLexer lexer = new JavaLexer(new StringReader(field)) {
509: {
510: tokenObjectClass = biz.hammurapi.antlr.Token.class;
511: }
512: };
513: JavaRecognizer parser = new JavaRecognizer(lexer);
514: parser.setASTFactory(factory);
515: parser.field();
516: show(parser);
517: } catch (TokenStreamException e) {
518: throw new GenerationException(field, e);
519: } catch (RecognitionException e) {
520: throw new GenerationException(field, e);
521: }
522: }
523:
524: protected static AST invocation(String definition)
525: throws GenerationException {
526: try {
527: JavaLexer lexer = new JavaLexer(
528: new StringReader(definition));
529: JavaRecognizer parser = new JavaRecognizer(lexer);
530: parser.invocation();
531: return parser.getAST();
532: } catch (TokenStreamException e) {
533: throw new GenerationException(definition, e);
534: } catch (RecognitionException e) {
535: throw new GenerationException(definition, e);
536: }
537: }
538:
539: protected static void showInvocation(String invocation)
540: throws GenerationException {
541: try {
542: JavaLexer lexer = new JavaLexer(
543: new StringReader(invocation)) {
544: {
545: tokenObjectClass = biz.hammurapi.antlr.Token.class;
546: }
547: };
548: JavaRecognizer parser = new JavaRecognizer(lexer);
549: parser.setASTFactory(factory);
550: parser.invocation();
551: show(parser);
552: } catch (TokenStreamException e) {
553: throw new GenerationException(invocation, e);
554: } catch (RecognitionException e) {
555: throw new GenerationException(invocation, e);
556: }
557: }
558:
559: /**
560: * @param parser
561: */
562: private static void show(JavaRecognizer parser) {
563: //((biz.hammurapi.antlr.AST) parser.getAST()).show(parser.getTokenNames());
564: AST root = factory.create(0, "Parse tree");
565: root.setFirstChild(parser.getAST());
566: Browser.show(
567: new AstVisualizable((biz.hammurapi.antlr.AST) root,
568: parser.getTokenNames()), "Parse tree");
569: }
570:
571: protected static String toString(AST ast) {
572: switch (ast.getType()) {
573: case JavaTokenTypes.DOT:
574: return toString(ast.getFirstChild()) + "."
575: + toString(ast.getFirstChild().getNextSibling());
576: case JavaTokenTypes.ARRAY_DECLARATOR:
577: return toString(ast.getFirstChild()) + "[]";
578: default:
579: return ast.getText();
580: }
581: }
582:
583: public void addStaticInitializer(InstructionList instructionList,
584: Collection exceptionHandlers, String description)
585: throws GenerationException {
586: Collection signature = new ArrayList();
587: signature.add("<clinit>");
588:
589: if (!methods.containsKey(signature)) {
590: methods.put(signature, signature);
591: instructionList.setPositions(true);
592: MethodGen mg = new MethodGen(Constants.ACC_STATIC,
593: Type.VOID, null, null, "<clinit>", cg
594: .getClassName(), instructionList, cg
595: .getConstantPool());
596:
597: if (exceptionHandlers != null) {
598: Iterator ehit = exceptionHandlers.iterator();
599: while (ehit.hasNext()) {
600: ExceptionHandler eh = (ExceptionHandler) ehit
601: .next();
602: mg.addExceptionHandler(eh.getFrom(), eh.getTo(), eh
603: .getHandler(),
604: (ObjectType) ClassGeneratorBase
605: .java2BcelType(eh.getType()));
606: }
607: }
608:
609: mg.setMaxStack();
610: mg.setMaxLocals();
611:
612: Method method = mg.getMethod();
613: cg.addMethod(method);
614: } else {
615: throw new GenerationException(
616: "Duplicate static initializer");
617: }
618: }
619:
620: private Map fields = new HashMap();
621:
622: /**
623: * This method shall be used only with fields added through addField()
624: * @param fieldName
625: * @return
626: */
627: public FieldInstruction createGetField(String fieldName) {
628: if (fields.containsKey(fieldName)) {
629: FieldDescriptor fieldDescriptor = (FieldDescriptor) fields
630: .get(fieldName);
631: if (fieldDescriptor.isStatic) {
632: return getInstructionFactory().createGetStatic(
633: cg.getClassName(), fieldName,
634: fieldDescriptor.type);
635: }
636:
637: return getInstructionFactory().createGetField(
638: cg.getClassName(), fieldName, fieldDescriptor.type);
639: }
640: throw new IllegalArgumentException("Invalid field name: "
641: + fieldName);
642: }
643:
644: /**
645: * This method shall be used only with fields added through addField()
646: * @param fieldName
647: * @return
648: */
649: public FieldInstruction createPutField(String fieldName) {
650: if (fields.containsKey(fieldName)) {
651: FieldDescriptor fieldDescriptor = (FieldDescriptor) fields
652: .get(fieldName);
653: if (fieldDescriptor.isStatic) {
654: return getInstructionFactory().createPutStatic(
655: cg.getClassName(), fieldName,
656: fieldDescriptor.type);
657: }
658: return getInstructionFactory().createPutField(
659: cg.getClassName(), fieldName, fieldDescriptor.type);
660: }
661: throw new IllegalArgumentException("Invalid field name: "
662: + fieldName);
663: }
664:
665: }
|