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.util.ArrayList;
026: import java.util.Collection;
027: import java.util.HashMap;
028: import java.util.HashSet;
029: import java.util.Iterator;
030: import java.util.Map;
031: import java.util.Properties;
032: import java.util.Set;
033:
034: import org.apache.bcel.classfile.Method;
035: import org.apache.bcel.classfile.Utility;
036: import org.apache.bcel.generic.InstructionFactory;
037: import org.apache.bcel.generic.InstructionList;
038: import org.apache.bcel.generic.LocalVariableInstruction;
039: import org.apache.bcel.generic.MethodGen;
040: import org.apache.bcel.generic.ObjectType;
041: import org.apache.bcel.generic.ReturnInstruction;
042: import org.apache.bcel.generic.Type;
043:
044: import biz.hammurapi.codegen.JavaTokenTypes;
045:
046: import antlr.collections.AST;
047: import biz.hammurapi.util.Parameter;
048:
049: /**
050: * @author Pavel Vlasov
051: * @version $Revision: 1.4 $
052: */
053: public class MethodPrototype {
054: private ClassGeneratorBase owner;
055: private Map variables = new HashMap();
056: private int pos = 1;
057: private Set modifiers = new HashSet();
058: private String name;
059: private String returnType;
060: private ArrayList throwsList = new ArrayList();
061: private ArrayList allParameters = new ArrayList();
062: private Properties attributes = new Properties();
063:
064: private class VariableDescriptor {
065: Type type;
066: int position;
067:
068: /**
069: * @param type
070: * @param position
071: */
072: public VariableDescriptor(Type type, int position) {
073: this .type = type;
074: this .position = position;
075: }
076: }
077:
078: private void addParameter(final String name, final String type) {
079: allParameters.add(new Parameter() {
080: public String getName() {
081: return name;
082: }
083:
084: public String getType() {
085: return type;
086: }
087: });
088: }
089:
090: /**
091: * @param definition E.g. <code>public java.lang.String valueOf()</code>
092: * @param parameters parameters to be appended to parameters in definition
093: * @throws GenerationException
094: */
095: public MethodPrototype(ClassGeneratorBase owner, String definition,
096: Collection parameters) throws GenerationException {
097: this .owner = owner;
098: AST ast = ClassGeneratorBase.field(definition);
099: if (ast.getType() == JavaTokenTypes.METHOD_DEF) {
100: for (AST node = ast.getFirstChild(); node != null; node = node
101: .getNextSibling()) {
102: switch (node.getType()) {
103: case JavaTokenTypes.MODIFIERS:
104: for (AST child = node.getFirstChild(); child != null; child = child
105: .getNextSibling()) {
106: modifiers.add(child.getText());
107: }
108: break;
109: case JavaTokenTypes.IDENT:
110: case JavaTokenTypes.INIT:
111: name = node.getText();
112: break;
113: case JavaTokenTypes.TYPE:
114: returnType = ClassGeneratorBase.toString(node
115: .getFirstChild());
116: break;
117: case JavaTokenTypes.PARAMETERS:
118: for (AST pnode = node.getFirstChild(); pnode != null; pnode = pnode
119: .getNextSibling()) {
120: String pname = null;
121: String ptype = null;
122: for (AST ppnode = pnode.getFirstChild(); ppnode != null; ppnode = ppnode
123: .getNextSibling()) {
124: switch (ppnode.getType()) {
125: case JavaTokenTypes.IDENT:
126: pname = ppnode.getText();
127: break;
128: case JavaTokenTypes.TYPE:
129: ptype = ClassGeneratorBase
130: .toString(ppnode
131: .getFirstChild());
132: break;
133: default:
134: throw new GenerationException(
135: "Unexpected node: "
136: + ppnode.getText());
137: }
138: }
139: declareVariable(pname, ptype);
140: addParameter(pname, ptype);
141: }
142: break;
143: case JavaTokenTypes.LITERAL_throws:
144: for (AST tnode = node.getFirstChild(); tnode != null; tnode = tnode
145: .getNextSibling()) {
146: throwsList.add(ClassGeneratorBase
147: .toString(tnode));
148: }
149: break;
150:
151: default:
152: throw new GenerationException("Unexpected node: "
153: + node);
154: }
155: }
156:
157: // System.out.println(definition);
158:
159: if (parameters != null) {
160: Iterator pit = parameters.iterator();
161: while (pit.hasNext()) {
162: Parameter p = (Parameter) pit.next();
163: declareVariable(p.getName(), p.getType());
164: // System.out.println("\t"+p.getName() +" -> "+p.getType());
165: addParameter(p.getName(), p.getType());
166: }
167: }
168: } else {
169: throw new GenerationException("Invalid node type "
170: + ast.getType() + " in definition '" + definition
171: + "'");
172: }
173: }
174:
175: public void declareVariable(String name, String type) {
176: if (name == null) {
177: throw new NullPointerException("name==null");
178: }
179: if (type == null) {
180: throw new NullPointerException("type==null");
181: }
182: if (variables.containsKey(name)) {
183: throw new IllegalArgumentException(
184: "Duplicate variable name");
185: }
186:
187: Type bcelType = ClassGeneratorBase.java2BcelType(type);
188: variables.put(name, new VariableDescriptor(bcelType, pos));
189: pos += bcelType.getSize();
190: }
191:
192: /**
193: * Adds the method to the owner
194: * @param il Instruction list. Can be null.
195: * @param exceptionHandlers Collection of {@link biz.hammurapi.codegen.ExceptionHandler} objects. Can be null.
196: * @throws GenerationException
197: * @throws StringIndexOutOfBoundsException
198: */
199: public void addMethod(InstructionList il,
200: Collection exceptionHandlers, String description)
201: throws GenerationException {
202: Collection signature = new ArrayList();
203: signature.add(name);
204: Iterator pit = allParameters.iterator();
205: while (pit.hasNext()) {
206: signature.add(((Parameter) pit.next()).getType());
207: }
208:
209: if (owner.checkMethod(signature, returnType, throwsList)) {
210: Type[] args = new Type[allParameters.size()];
211: String[] argNames = new String[args.length];
212: pit = allParameters.iterator();
213: for (int i = 0; pit.hasNext(); i++) {
214: Parameter p = (Parameter) pit.next();
215: args[i] = Type.getType(Utility
216: .getSignature(p.getType()));
217: argNames[i] = p.getName();
218: }
219:
220: if (il == null) {
221: il = new InstructionList();
222: }
223:
224: il.setPositions(true);
225:
226: MethodGen mg = new MethodGen(ClassGeneratorBase
227: .modifiers(modifiers), Type.getType(Utility
228: .getSignature(returnType)), args, argNames, name,
229: owner.cg.getClassName(), il, owner.cg
230: .getConstantPool());
231:
232: if (exceptionHandlers != null) {
233: Iterator ehit = exceptionHandlers.iterator();
234: while (ehit.hasNext()) {
235: ExceptionHandler eh = (ExceptionHandler) ehit
236: .next();
237: mg.addExceptionHandler(eh.getFrom(), eh.getTo(), eh
238: .getHandler(),
239: (ObjectType) ClassGeneratorBase
240: .java2BcelType(eh.getType()));
241: }
242: }
243:
244: mg.setMaxStack();
245: mg.setMaxLocals();
246:
247: Iterator thit = throwsList.iterator();
248: while (thit.hasNext()) {
249: mg.addException((String) thit.next());
250: }
251:
252: Method method = mg.getMethod();
253: owner.cg.addMethod(method);
254:
255: //ClassGeneratorBase.printMethod(mg.getMethod());
256:
257: if (owner.listener != null
258: && (owner.cg.isPublic() || owner.cg.isProtected())
259: && (modifiers.contains("public") || modifiers
260: .contains("protected"))) {
261: owner.listener.onMethod(owner.cg.getClassName(), mg
262: .toString(), description, attributes);
263: }
264:
265: il.dispose();
266: } else {
267: throw new DuplicateMethodException("Duplicate method '"
268: + signature + "' in class "
269: + owner.cg.getClassName());
270: }
271: }
272:
273: public Type getReturnType() {
274: return Type.getType(Utility.getSignature(returnType));
275: }
276:
277: public ReturnInstruction createReturn() {
278: return InstructionFactory.createReturn(getReturnType());
279: }
280:
281: public LocalVariableInstruction createVariableLoad(
282: String variableName) {
283: if (variables.containsKey(variableName)) {
284: VariableDescriptor vd = (VariableDescriptor) variables
285: .get(variableName);
286: return InstructionFactory.createLoad(vd.type, vd.position);
287: }
288: throw new IllegalArgumentException("Undeclared name: "
289: + variableName);
290: }
291:
292: public LocalVariableInstruction createVariableStore(
293: String variableName) {
294: if (variables.containsKey(variableName)) {
295: VariableDescriptor vd = (VariableDescriptor) variables
296: .get(variableName);
297: return InstructionFactory.createStore(vd.type, vd.position);
298: }
299: throw new IllegalArgumentException("Undeclared name: "
300: + variableName);
301: }
302:
303: /**
304: * Method attribute which appears in generated documentation.
305: * Maybe useful if documentation is used for further code
306: * generation
307: *
308: * @param name
309: * @param value
310: */
311: public void setAttribute(String name, String value) {
312: attributes.setProperty(name, value);
313: }
314: }
|