/******************************************************************************* * Copyright (c) 2001, 2005 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.io.*; import java.lang.reflect.Array; import java.util.*; public class Static extends Expression { protected static final HashMap sPrimitiveTypes; static { sPrimitiveTypes = new HashMap(10); sPrimitiveTypes.put("byte", Byte.TYPE); //$NON-NLS-1$ sPrimitiveTypes.put("char", Character.TYPE); //$NON-NLS-1$ sPrimitiveTypes.put("short", Short.TYPE); //$NON-NLS-1$ sPrimitiveTypes.put("int", Integer.TYPE); //$NON-NLS-1$ sPrimitiveTypes.put("long", Long.TYPE); //$NON-NLS-1$ sPrimitiveTypes.put("float", Float.TYPE); //$NON-NLS-1$ sPrimitiveTypes.put("double", Double.TYPE); //$NON-NLS-1$ } protected ArrayList arrayDimensions; protected List fArrayArguments; protected StringWriter typeWriter = new StringWriter(); protected boolean completedWithPeriod = false; protected char[] completionToken; public Class type; protected boolean isComplete; public boolean isProcessingArrayDimension; /** * Create an unknown expression */ public Static(ClassLoader aClassLoader){ fClassLoader = aClassLoader; } public Static(char[] aToken,char delimiter,ClassLoader aClassLoader){ fClassLoader = aClassLoader; try { typeWriter.write(aToken); } catch ( IOException exc ) {}; checkForValidType(); if ( type == null && delimiter == DelimiterPeriod ){ typeWriter.write(DelimiterPeriod); } } public Static(char[] aToken,char delimiter,ClassLoader aClassLoader,boolean isArray){ this(aToken,delimiter,aClassLoader); isProcessingArrayDimension = true; arrayDimensions = new ArrayList(1); arrayDimensions.add(new Statement(aClassLoader)); } public boolean isComplete(){ return isComplete; } public boolean isArray(){ return arrayDimensions != null; } /** * See whether or not we have a valid type * This uses the class loader given to us if one specified. The reason for this * is that Eclipse uses a special plugin class loader and when we are running * in a VM inside eclipse ( e.g. for IDEVM proxy stuff ) we must have * reference to the plugin that is using us otherwise we can't load its classes */ protected void checkForValidType(){ type = (Class) sPrimitiveTypes.get(typeWriter.toString()); if (type == null) try { if ( fClassLoader == null ) { type = Class.forName(typeWriter.toString()); } else { type = fClassLoader.loadClass(typeWriter.toString()); } } catch ( ClassNotFoundException exc ) { try { type = Class.forName("java.lang." + typeWriter.toString()); //$NON-NLS-1$ StringWriter writer = new StringWriter(); writer.write(type.getName()); typeWriter = writer; } catch ( ClassNotFoundException exc1 ) {} } catch ( NoClassDefFoundError exc ) { // A mismatch in some way. Found a class, probably different case. One possibility // is found a class, but this was really a package. So class and package with same name // but different case caused this to occur. [46376]. } } /** * If we have any static methods return the result of evaluating them against the type */ public Object evaluate() { // If we are an array but haven't created it do so if ( isArray() ) { if ( array == null ) { evaluateArray(); } return array; } else { if ( type != null ) { return type; } else { // If we have no type then we are some kind incomplete expression that cannot be evaluted throw new RuntimeException(); } } } /** * The type of us is either simply the class, or if we are an array then we must create the java.lang.reflect.Array * using the reflective APIs */ protected Object array; public Class getTypeClass() { if ( isArray() ) { if ( array == null ) { // Do not evaluate the array and return its class. Instead just work out the class by our type // and number of dimensions Object result = Array.newInstance(type,getNumberOfDimensions()); return result.getClass(); } else { return array.getClass(); } } else { return type; } } protected String getTypeClassName() { return typeWriter.toString(); } public Class getPrimitiveTypeClass(){ return type; } protected int[] getNumberOfDimensions(){ List dimensions = new ArrayList(1); dimensions.add(new Integer(fArrayArguments.size())); ((MessageArgument)fArrayArguments.get(0)).contributeArgumentNumber(dimensions); // The total number of arguments is a set of Integer objects in the dimensions list // convert this to an int[] int[] intDimensions = new int[dimensions.size()]; for (int i = 0; i < dimensions.size(); i++) { intDimensions[i] = ((Integer)dimensions.get(i)).intValue(); } return intDimensions; } /** * Evaluate the array */ protected void evaluateArray(){ if ( fArrayArguments != null ) { // If the array isn't declared with a size but this is supplied with argument these will be in the fArrayArguments // new int[] { 2 ,3 } will have the constructor arguments supplied as two message arguments with if ( array == null ) { // The size of the array arguments is our array size, however for a multi arg array we need // to find the size of any nested arrays within arguments themselves, e.g. // new int[][] { {2,3} , {3,4} } then we have two array arguments, each of which is a MessageArgument // whose statement is an ArrayArguments that has two arguments, etc... // To find the number of arguments we create a list that we add our number of arguments to, and then // pass this all the way up the arguments chain using contributeArgumentNumber(List) so that each element // can add to the list the number of arguments ( if any ) that they have. array = Array.newInstance(type,getNumberOfDimensions()); // Set the elements in the array to be the result of evaluating the constructor arguments for (int i = 0; i < fArrayArguments.size(); i++) { Expression expression = (Expression)fArrayArguments.get(i); try { Object element = expression.evaluate(); Array.set(array,i,element); } catch ( Exception exc ) { // Any evaluation exceptions should be thrown back throw new RuntimeException(); } } } } else if ( arrayDimensions != null ) { // To get the class of a reflective array we must actually first create it and then ask it for its class // The array Dimensions are present if the array was explicitly declared with a size, e.g. new int[2][3] // will have to arrayDimensions that represent the expressions 2 and 3 ( as NumberLiteral instances ) if ( array == null ) { // Evaluate all of the arrayDimensions. These should be integers int[] dimensionSizes = new int[arrayDimensions.size()]; for (int i = 0; i < dimensionSizes.length; i++) { try { Integer dimensionSize = (Integer) ((Expression)arrayDimensions.get(i)).evaluate(); dimensionSizes[i] = dimensionSize.intValue(); } catch ( Exception exc ) { throw new RuntimeException(); } } // For a multi arg array we need to create using the static method on array that takes an int[] that represents // the number of dimensions, e.g. for new String[2][3] we do Array.newInstance(String.class,new int[] { 2 , 3 }; array = Array.newInstance(type,dimensionSizes); } } } /** * If the token is a period then it could either be part of the type or else a static method call */ public Expression push(char[] token , char delimiter ) { // If we don't yet have a valid type then see if we now do if (type == null){ try { typeWriter.write(token); } catch ( IOException exc ) {}; checkForValidType(); // If we got a valid type and a period then remember it if ( delimiter == DelimiterPeriod ) { if (type != null) { completedWithPeriod = true; return this; } } } if ( arrayDimensions != null && isProcessingArrayDimension ) { Expression lastArrayDimension = (Expression)arrayDimensions.get(arrayDimensions.size()-1); lastArrayDimension.push(token,delimiter); if ( delimiter == DelimiterCloseSquareBrace && isProcessingArrayDimension ) { isProcessingArrayDimension = false; } return this; } if ( delimiter == DelimiterOpenSquareBrace && !isProcessingArrayDimension ) { if ( arrayDimensions == null ) arrayDimensions = new ArrayList(1); Statement statement = new Statement(fClassLoader); arrayDimensions.add(statement); isProcessingArrayDimension = true; return this; } // If we have a type and the delimiter is a ( then it must be a message beginning if ( type != null ) { if (delimiter == DelimiterOpenParen) { isComplete = true; return new Message( this , token , fClassLoader ); } else if (completedWithPeriod){ isComplete = true; Field field = new Field(this,token,fClassLoader); // If our token is a ), ' ' or , then the field is completed, // otherwise leave it open so it will process the remaining tokens // if (delimiter == DelimiterCloseParen || delimiter == DelimiterSpace || delimiter == DelimiterComma) { if (delimiter == DelimiterCloseParen || delimiter == DelimiterComma) { field.isComplete = true; } return field; } } // We are still looking for a type so append a . if ( type == null ) { typeWriter.write('.'); } return this; } public String toString(){ StringWriter writer = new StringWriter(); writer.write("Static "); //$NON-NLS-1$ if ( type == null ) { writer.write("(Incomplete) {"); //$NON-NLS-1$ } else { writer.write("(Complete) {"); //$NON-NLS-1$ } writer.write(typeWriter.toString()); writer.write("}"); //$NON-NLS-1$ if ( arrayDimensions != null ) { writer.write(" array dimensions="); //$NON-NLS-1$ writer.write(new Integer(arrayDimensions.size()).toString()); } if ( fArrayArguments != null ) { writer.write(" array dimensions="); //$NON-NLS-1$ writer.write(new Integer(fArrayArguments.size()).toString()); } return writer.toString(); } public boolean isPrimitive() { return getTypeClass().isPrimitive(); } public void setArrayArguments(List arguments) { fArrayArguments = arguments; } }