/******************************************************************************* * 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; } }