| /******************************************************************************* |
| * Copyright (c) 2001, 2006 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jem.internal.proxy.initParser; |
| /* |
| |
| |
| */ |
| |
| |
| import java.util.*; |
| |
| import org.eclipse.jem.internal.proxy.common.AmbiguousMethodException; |
| import org.eclipse.jem.internal.proxy.common.MethodHelper; |
| /** |
| * Insert the type's description here. |
| * Creation date: (11/01/00 8:52:36 PM) |
| * @author: Administrator |
| */ |
| public class Constructor extends Expression { |
| public boolean argsOpened = false; |
| public boolean argsClosed = false; |
| public boolean trailingPeriod = false; |
| public ArrayList arguments = new ArrayList(2); |
| public Static type; |
| protected boolean fIsComplete; |
| public boolean insideArgsOpenedParen = false; |
| public boolean insideArgsClosedParen = false; |
| public boolean isString = false; |
| public java.lang.reflect.Constructor ctor; |
| /** |
| * Constructor constructor comment. |
| */ |
| public Constructor(ClassLoader aClassLoader) { |
| super(); |
| fClassLoader = aClassLoader; |
| } |
| |
| public boolean isComplete(){ |
| return fIsComplete; |
| } |
| |
| /** |
| * Invoke the construtor |
| */ |
| public Object evaluate() throws Exception { |
| |
| if ( type.isArray() ) { |
| // For arrays we get the array that is actually held inside the static |
| // To instantiate the array it is possible that the static knows the array size, e.g. new int[2] |
| // or else it must be given this from the number of arguments we have, e.g. new int[] { 2 , 3 } |
| if ( arguments.size() > 0 ) { |
| type.setArrayArguments(arguments); |
| } |
| Object result = type.evaluate(); |
| // Deal with array initialization arguments |
| return result; |
| } else { |
| // For non arrays we find the method and invoke it on the type |
| cacheCtor(); |
| // Get the array of arguments |
| Object[] args = new Object[arguments.size()]; |
| Iterator itr = arguments.iterator(); |
| for (int i = 0; i < arguments.size() ; i++){ |
| Expression anExpression = (Expression)itr.next(); |
| args[i] = anExpression.evaluate(); |
| } |
| |
| try { |
| return ctor.newInstance(args); |
| } catch (Exception e) { |
| // If we got this far, then we have a valid parse, so anything is an evaluation exception. |
| throw new EvaluationException(e); |
| } |
| } |
| } |
| /** A constructor can only return the class of its type |
| */ |
| public Class getTypeClass() throws Exception { |
| if ( type.isArray() ) { |
| // For arrays we get the array that is actually held inside the static |
| // To instantiate the array it is possible that the static knows the array size, e.g. new int[2] |
| // or else it must be given this from the number of arguments we have, e.g. new int[] { 2 , 3 } |
| if ( arguments.size() > 0 ) { |
| type.setArrayArguments(arguments); |
| } |
| } |
| return type.getTypeClass(); |
| } |
| |
| protected String getTypeClassName() { |
| return type.getTypeClassName(); |
| } |
| |
| private void cacheCtor() throws Exception { |
| |
| if (ctor == null) { |
| Class[] argTypes = new Class[arguments.size()]; |
| Iterator itr = arguments.iterator(); |
| for (int i=0; i<argTypes.length; i++) |
| argTypes[i] = getEvaluationTypeClass((Expression) itr.next()); |
| |
| try { |
| ctor = MethodHelper.findCompatibleConstructor(getEvaluationTypeClass(this), argTypes); |
| } catch (NoSuchMethodException e) { |
| throw new EvaluationException(e); |
| } catch (AmbiguousMethodException e) { |
| throw new EvaluationException(e); |
| } catch (IllegalAccessException e) { |
| throw new EvaluationException(e); |
| } |
| } |
| } |
| protected boolean isDelimiterOpened(char token){ |
| return type.isArray() ? token == DelimiterOpenElipse : token == DelimiterOpenParen; |
| } |
| protected boolean isDelimiterClosed(char token){ |
| return type.isArray() ? token == DelimiterCloseElipse : token == DelimiterCloseParen; |
| } |
| /** |
| * push method comment. |
| */ |
| public Expression push(char[] token, char tokenDelimiter) { |
| |
| // If we are closed and we receive a . with no token then remember this |
| if ( argsClosed && !trailingPeriod && tokenDelimiter == DelimiterPeriod && token.length == 0 ) { |
| trailingPeriod = true; |
| return this; |
| } |
| // If we have been closed with a . and we receive a . then we are a field |
| if ( trailingPeriod && tokenDelimiter == DelimiterPeriod ) { |
| return new Field(this,token,fClassLoader); |
| } |
| // If we have been closed with a . and we receive a ( we are a message |
| if ( trailingPeriod && tokenDelimiter == DelimiterOpenParen ) { |
| return new Message(this,token,fClassLoader); |
| } |
| |
| // Lazy initialize the type if required |
| if ( type == null ) { |
| switch ( tokenDelimiter ) { |
| case DelimiterPeriod: { |
| type = new Static(token,tokenDelimiter,fClassLoader); |
| type.setClassLoader(fClassLoader); |
| return this; |
| } |
| case DelimiterOpenParen: { |
| type = new Static(token,tokenDelimiter,fClassLoader); |
| type.setClassLoader(fClassLoader); |
| argsOpened = true; |
| return this; |
| } |
| case DelimiterOpenSquareBrace: { |
| // throw new CannotProcessArrayTypesException(); |
| // Array dimenions are created as staements in the arrayDimensions |
| // This is for expressions like new String[] or new String[3], where the Statement represents the 3, i.e. |
| // the code that declares the size of the array |
| type = new Static(token,tokenDelimiter,fClassLoader,true); |
| return this; |
| } |
| // If we get a { and our static is an array then this is the array initialization parameters |
| case DelimiterOpenElipse: { |
| if ( type != null && type.isArray() ) { |
| argsOpened = true; |
| } |
| } |
| default: { |
| return null; |
| } |
| } |
| } |
| |
| // If we have a static that is an array then it consumes token until it is complete |
| if ( type != null && type.isArray() ) { |
| // The type consumes token as long as it is still in the array declaration section |
| if ( type.isProcessingArrayDimension || tokenDelimiter == DelimiterOpenSquareBrace ) { |
| type.push(token,tokenDelimiter); |
| return this; |
| } |
| } |
| |
| // If the type is incomplete and the token is a . ( for another class ) or a [ for an array then push it onto the type |
| if (!type.isArray() && type.getTypeClass() == null && (tokenDelimiter == DelimiterPeriod || tokenDelimiter == DelimiterOpenSquareBrace)) { |
| type.push(token , tokenDelimiter); |
| return this; |
| } |
| // If the type is incomplete and the token is a ( then push it onto the type and open the parens |
| if (!type.isArray()){ |
| if (type.getTypeClass() == null && !argsOpened && isDelimiterOpened(tokenDelimiter)) { |
| argsOpened = true; |
| insideArgsOpenedParen = true; |
| type.push(token , DelimiterSpace ); |
| return this; |
| } |
| } else { |
| if ( !argsOpened && isDelimiterOpened(tokenDelimiter)) { |
| argsOpened = true; |
| insideArgsOpenedParen = true; |
| return this; |
| } |
| } |
| |
| // If the args are already closed and we get another close expression, then just return ourselves |
| // This occurs in the case of nested array arguments within constructors |
| if ( argsClosed && (tokenDelimiter == DelimiterCloseParen)){ |
| fIsComplete = true; |
| return this; |
| } |
| |
| // If the arguments are closed and we receive a { then this is an inner class declaration, e.g. |
| // new javax.swing.table.DefaultTableModel(){} |
| // This can't be processed by us so we need to throw an exception |
| if (argsClosed && tokenDelimiter == DelimiterOpenElipse ) { |
| throw new CannotProcessInnerClassesException(); |
| } |
| |
| // If the args are opened and a token was supplied then it must be an argument |
| if (argsOpened){ |
| // If we already have an incomplete argument then this may be a constructor, a static reference, etc... |
| // and we should forward the request to the argument |
| Expression openArgument = null; |
| if ( arguments.size() > 0 ){ |
| openArgument = (Expression)arguments.get(arguments.size()-1); |
| if ( !openArgument.isComplete() ) { |
| openArgument.push(token,tokenDelimiter); |
| // If the argument is complete and we received a ) then the constructor is complete |
| // or if we receive a } and are an array we are complete |
| if ( openArgument.isComplete() && isDelimiterClosed(tokenDelimiter) ){ |
| argsClosed = true; |
| } |
| return this; |
| } |
| } |
| |
| Expression newArgument = null; |
| // If we are not already inside the open arg and we get another open paren this is probably a cast |
| // or some kind of statement, and OperMinus is probably the start of a number |
| // If the args are not already open we must be opened with either a { or a ( depending on whether we are an array or not |
| // however if the args are opened already and we receive a ( then this is the start of a new statement |
| // if ((!insideArgsOpenedParen && isDelimiterOpened(tokenDelimiter)) || tokenDelimiter == DelimiterOpenParen){ |
| if (tokenDelimiter == DelimiterOpenElipse || tokenDelimiter == DelimiterOpenParen){ |
| insideArgsOpenedParen = true; |
| newArgument = new Statement(fClassLoader).push(token,tokenDelimiter); |
| if ( newArgument instanceof ArrayArguments ) { |
| ((ArrayArguments)newArgument).setType(type); |
| } |
| newArgument = new MessageArgument(newArgument); |
| arguments.add(newArgument); |
| |
| } |
| |
| // Start of a possible string or number or character |
| if ( ( token.length > 0 || tokenDelimiter == DelimiterQuote || tokenDelimiter == DelimiterSingleQuote) || tokenDelimiter == OperMinus ){ |
| newArgument = new Statement(fClassLoader).push(token,tokenDelimiter); |
| newArgument = new MessageArgument(newArgument); |
| arguments.add(newArgument); |
| } |
| // If the token after the argument is a ) then the message is being closed |
| if ( !insideArgsOpenedParen || argumentsAreComplete() && isDelimiterClosed(tokenDelimiter) ) { |
| argsClosed = true; |
| return this; |
| } |
| if ( insideArgsOpenedParen && isDelimiterClosed(tokenDelimiter) ) { |
| insideArgsClosedParen = true; |
| return this; |
| } |
| |
| // If the token after the argument is a , or a ' ' then the argument is being closed |
| if ( tokenDelimiter == DelimiterComma || tokenDelimiter == DelimiterSpace ) { |
| // If our arguments are closed then we must be complete. We need to signal the fact we are closed |
| // otherwise we will not be processed correctly if we are part of a stack |
| if ( argsClosed ) { |
| fIsComplete = true; |
| } |
| return this; |
| } |
| |
| // If we receive a close bracket then we are closed as long as the last argument is closed |
| if(argsOpened && isDelimiterClosed(tokenDelimiter)){ |
| // No parms - we are a closed constructor |
| if(arguments.size() == 0){ |
| argsClosed = true; |
| return this; |
| } |
| } |
| |
| // If our last argument is closed and receive a ) and we have no new argument then we are closed |
| if (tokenDelimiter == DelimiterCloseParen && newArgument == null){ |
| Expression lastArgument = (Expression)arguments.get(arguments.size()-1); |
| if ( lastArgument.isComplete() ) { |
| argsClosed = true; |
| return this; |
| } |
| } |
| |
| // Otherwise the new argument is stil processing. Return it |
| return newArgument; |
| } |
| |
| return this; |
| } |
| protected boolean argumentsAreComplete(){ |
| if ( arguments.size() == 0) return true; |
| return ((Expression)arguments.get(arguments.size()-1)).isComplete(); |
| } |
| public String toString(){ |
| |
| java.io.StringWriter writer = new java.io.StringWriter(); |
| writer.write("Constructor \""); //$NON-NLS-1$ |
| if ( type != null ) { |
| writer.write(type.typeWriter.toString()); |
| } else { |
| writer.write("UNTYPED"); //$NON-NLS-1$ |
| } |
| for (int i = 0; i < arguments.size() ; i++){ |
| writer.write(" Arg("); //$NON-NLS-1$ |
| writer.write("" + (i+1)); //$NON-NLS-1$ |
| writer.write(") = "); //$NON-NLS-1$ |
| writer.write(arguments.get(i).toString()); |
| } |
| |
| return writer.toString(); |
| |
| } |
| |
| /** |
| * This is never primitive |
| */ |
| public boolean isPrimitive() { |
| return false; |
| } |
| } |