/******************************************************************************* * Copyright (c) 2005, 2010 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 Rational Software - Initial API and implementation *******************************************************************************/ package org.eclipse.cdt.ui.tests.DOMAST; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.ui.views.properties.IPropertyDescriptor; import org.eclipse.ui.views.properties.IPropertySource; import org.eclipse.ui.views.properties.TextPropertyDescriptor; import org.eclipse.cdt.core.dom.ast.ASTNodeProperty; import org.eclipse.cdt.core.dom.ast.ASTTypeUtil; import org.eclipse.cdt.core.dom.ast.DOMException; import org.eclipse.cdt.core.dom.ast.IASTArrayModifier; import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression; import org.eclipse.cdt.core.dom.ast.IASTCastExpression; import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier; import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier; import org.eclipse.cdt.core.dom.ast.IASTDeclarator; import org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier; import org.eclipse.cdt.core.dom.ast.IASTExpression; import org.eclipse.cdt.core.dom.ast.IASTFileLocation; import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition; import org.eclipse.cdt.core.dom.ast.IASTIdExpression; import org.eclipse.cdt.core.dom.ast.IASTLiteralExpression; import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTPointer; import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement; import org.eclipse.cdt.core.dom.ast.IASTPreprocessorObjectStyleMacroDefinition; import org.eclipse.cdt.core.dom.ast.IASTProblem; import org.eclipse.cdt.core.dom.ast.IASTProblemHolder; import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.IASTUnaryExpression; import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.dom.ast.IType; import org.eclipse.cdt.core.dom.ast.c.ICASTArrayDesignator; import org.eclipse.cdt.core.dom.ast.c.ICASTArrayModifier; import org.eclipse.cdt.core.dom.ast.c.ICASTDesignator; import org.eclipse.cdt.core.dom.ast.c.ICASTFieldDesignator; import org.eclipse.cdt.core.dom.ast.gnu.c.IGCCASTArrayRangeDesignator; import org.eclipse.cdt.core.parser.Keywords; import org.eclipse.cdt.core.parser.util.ArrayUtil; import org.eclipse.cdt.internal.core.dom.parser.ASTNode; import org.eclipse.cdt.internal.core.model.ASTStringUtil; /** * @author dsteffle */ public class DOMASTNodeLeaf implements IAdaptable { private static final String INTERNAL = "internal"; //$NON-NLS-1$ private static final String VARIABLE_SIZED_ = "* "; //$NON-NLS-1$ private static final String VOLATILE_ = "volatile "; //$NON-NLS-1$ private static final String STATIC_ = "static "; //$NON-NLS-1$ private static final String RESTRICT_ = "restrict "; //$NON-NLS-1$ private static final String CONST_ = "const "; //$NON-NLS-1$ private static final String DASH = "-"; //$NON-NLS-1$ private static final String FILE_SEPARATOR = "\\"; //$NON-NLS-1$ public static final String BLANK_STRING = ""; //$NON-NLS-1$ private static final String IGCCAST_PREFIX = "IGCCAST"; //$NON-NLS-1$ private static final String IGNUAST_PREFIX = "IGNUAST"; //$NON-NLS-1$ private static final String IGPPAST_PREFIX = "IGPPAST"; //$NON-NLS-1$ private static final String ICPPAST_PREFIX = "ICPPAST"; //$NON-NLS-1$ private static final String ICAST_PREFIX = "ICAST"; //$NON-NLS-1$ private static final String IAST_PREFIX = "IAST"; //$NON-NLS-1$ private static final String START_OF_LIST = ": "; //$NON-NLS-1$ private static final String LIST_SEPARATOR = ", "; //$NON-NLS-1$ private static final String PERIOD = "."; //$NON-NLS-1$ private IASTNode node = null; private DOMASTNodeParent parent; // used for applying filters to the tree, since it is lazily populated // all parents of the desired tree object to display need to have a flag as well private int filterFlag = 0; private static Set<String> ignoreInterfaces= new HashSet<String>(); public static final int FLAG_PROBLEM = 1<<0; public static final int FLAG_PREPROCESSOR = 1<<1; public static final int FLAG_INCLUDE_STATEMENTS = 1<<2; static { ignoreInterfaces.addAll(Arrays.asList(new String[] { "IASTCompletionContext", "ICPPASTCompletionContext", "IASTNode" })); } public DOMASTNodeLeaf(IASTNode node) { this.node = node; } public IASTNode getNode() { return node; } public void setParent(DOMASTNodeParent parent) { this.parent = parent; } public DOMASTNodeParent getParent() { return parent; } private boolean hasProperPrefix(String string) { if (string.startsWith(IAST_PREFIX) || string.startsWith(ICAST_PREFIX) || string.startsWith(ICPPAST_PREFIX) || string.startsWith(IGPPAST_PREFIX) || string.startsWith(IGNUAST_PREFIX) || string.startsWith(IGCCAST_PREFIX)) { if (!ignoreInterfaces.contains(string)) { return true; } } return false; } @Override public String toString() { if( node == null ) return BLANK_STRING; StringBuffer buffer = new StringBuffer(); List<Class<?>> search= new LinkedList<Class<?>>(); boolean done= false; boolean needComma= false; for (search.add(node.getClass()); !search.isEmpty(); ) { Class<?> clazz= search.remove(0); if (clazz.isInterface()) { if (clazz.getPackage().toString().indexOf(INTERNAL) < 0) { String interfaceName= clazz.getName(); interfaceName= interfaceName.substring(interfaceName.lastIndexOf(PERIOD) + 1); if (hasProperPrefix(interfaceName)) { if (needComma) buffer.append(LIST_SEPARATOR); buffer.append(interfaceName); needComma= true; done= true; } } } if (!done) { search.addAll(Arrays.asList((Class<?>[])clazz.getInterfaces())); final Class<?> superclass = clazz.getSuperclass(); if (superclass != null) search.add(superclass); } } if ( node instanceof IASTProblemHolder ) { buffer.append(START_OF_LIST); buffer.append(((IASTProblemHolder)node).getProblem().getMessageWithLocation()); } else if ( node instanceof IASTSimpleDeclaration ) { String name = null; IASTDeclarator[] decltors = ((IASTSimpleDeclaration)node).getDeclarators(); if ( decltors.length > 0 ) { buffer.append(START_OF_LIST); for (int i=0; i<decltors.length; i++) { name = getDeclaratorName(decltors[i]); buffer.append(name); if (i+1<decltors.length) buffer.append(LIST_SEPARATOR); } } return buffer.toString(); } else if ( node instanceof IASTFunctionDefinition ) { String name = getDeclaratorName( ((IASTFunctionDefinition)node).getDeclarator() ); if (name != null) { buffer.append(START_OF_LIST); buffer.append(name); } return buffer.toString(); } else if ( node instanceof IASTName ) { buffer.append(START_OF_LIST); buffer.append(node); return buffer.toString(); } else if ( node instanceof IASTTranslationUnit ) { String fileName = ((IASTTranslationUnit)node).getFilePath(); int lastSlash = fileName.lastIndexOf(FILE_SEPARATOR); if (lastSlash > 0) { buffer.append(START_OF_LIST); buffer.append(fileName.substring(lastSlash+1)); // TODO make path relative to project, i.e. /projectName/path/file.c } return buffer.toString(); } else if( node instanceof IASTDeclSpecifier ) { buffer.append( START_OF_LIST ); buffer.append( getSignature((IASTDeclSpecifier)node) ); return buffer.toString(); } else if ( node instanceof IASTPreprocessorIncludeStatement ) { String path = ((IASTPreprocessorIncludeStatement)node).getPath(); int lastSlash = path.lastIndexOf(FILE_SEPARATOR) + 1; buffer.append( START_OF_LIST ); buffer.append( path.substring(lastSlash) ); } else if ( node instanceof IASTPreprocessorObjectStyleMacroDefinition ) { String name = ((IASTPreprocessorObjectStyleMacroDefinition)node).getName().toString(); if (name != null) { buffer.append( START_OF_LIST ); buffer.append( name ); } } else if ( node instanceof IASTLiteralExpression ) { buffer.append(START_OF_LIST); buffer.append(node.toString()); } else if ( node instanceof IASTCastExpression ) { buffer.append(START_OF_LIST); buffer.append( ASTStringUtil.getCastOperatorString( (IASTCastExpression)node ) ); } else if ( node instanceof IASTUnaryExpression ) { buffer.append(START_OF_LIST); buffer.append( ASTStringUtil.getUnaryOperatorString( (IASTUnaryExpression)node ) ); } else if ( node instanceof IASTBinaryExpression ) { buffer.append(START_OF_LIST); buffer.append( ASTStringUtil.getBinaryOperatorString( (IASTBinaryExpression)node ) ); } else if ( node instanceof ICASTDesignator ) { if ( node instanceof ICASTArrayDesignator && ((ICASTArrayDesignator)node).getSubscriptExpression() != null ) { buffer.append(START_OF_LIST); buffer.append(((ICASTArrayDesignator)node).getSubscriptExpression()); } else if ( node instanceof ICASTFieldDesignator && ((ICASTFieldDesignator)node).getName() != null ) { buffer.append(START_OF_LIST); buffer.append(((ICASTFieldDesignator)node).getName()); } else if ( node instanceof IGCCASTArrayRangeDesignator && ((IGCCASTArrayRangeDesignator)node).getRangeCeiling() != null && ((IGCCASTArrayRangeDesignator)node).getRangeFloor() != null ) { buffer.append(START_OF_LIST); buffer.append(((IGCCASTArrayRangeDesignator)node).getRangeCeiling()); buffer.append(DASH); buffer.append(((IGCCASTArrayRangeDesignator)node).getRangeFloor()); } } else if ( node instanceof IASTArrayModifier ) { boolean started = false; if ( node instanceof ICASTArrayModifier ) { started = true; buffer.append(START_OF_LIST); if (((ICASTArrayModifier)node).isConst()) buffer.append(CONST_); if (((ICASTArrayModifier)node).isRestrict()) buffer.append(RESTRICT_); if (((ICASTArrayModifier)node).isStatic()) buffer.append(STATIC_); if (((ICASTArrayModifier)node).isVolatile()) buffer.append(VOLATILE_); if (((ICASTArrayModifier)node).isVariableSized()) buffer.append(VARIABLE_SIZED_); } IASTExpression constantExpression = ((IASTArrayModifier)node).getConstantExpression(); if ( constantExpression != null && constantExpression instanceof IASTIdExpression ) { if (!started) buffer.append(START_OF_LIST); buffer.append(((IASTIdExpression)constantExpression).getName().toString()); } } else if ( node instanceof IASTPointer ) { boolean started = false; if (((IASTPointer)node).isConst()) { if (!started) { started = true; buffer.append(START_OF_LIST); } buffer.append(CONST_); } if (((IASTPointer)node).isVolatile()) { if (!started) { started = true; buffer.append(START_OF_LIST); } buffer.append(VOLATILE_); } if (((IASTPointer)node).isRestrict()) { if (!started) { started = true; buffer.append(START_OF_LIST); } buffer.append(RESTRICT_); } } return buffer.toString(); } private String getSignature(IASTDeclSpecifier declSpec) { if (declSpec instanceof IASTCompositeTypeSpecifier) { StringBuilder buf= new StringBuilder(); IASTCompositeTypeSpecifier comp = (IASTCompositeTypeSpecifier) declSpec; switch(comp.getKey()) { case IASTCompositeTypeSpecifier.k_struct: buf.append(Keywords.cSTRUCT); break; case IASTCompositeTypeSpecifier.k_union: buf.append(Keywords.cUNION); break; default: buf.append(Keywords.cCLASS); break; } buf.append(' '); buf.append(comp.getName().toString()); return buf.toString(); } else if (declSpec instanceof IASTEnumerationSpecifier) { StringBuilder buf= new StringBuilder(); IASTEnumerationSpecifier comp = (IASTEnumerationSpecifier) declSpec; buf.append(Keywords.cENUM); buf.append(' '); buf.append(comp.getName().toString()); return buf.toString(); } String intermed= declSpec.getRawSignature(); return intermed.replaceAll("\\s+", " "); } private String getDeclaratorName(IASTDeclarator decltor) { String name = BLANK_STRING; while (decltor != null && decltor.getName() != null && decltor.getName().toString() == null) { decltor = decltor.getNestedDeclarator(); } if (decltor != null && decltor.getName() != null) { name = decltor.getName().toString(); } return name; } public Object getAdapter(@SuppressWarnings("rawtypes") Class key) { if (key == IPropertySource.class) return new ASTPropertySource(getNode()); return null; } public String getFilename() { if ( node == null ) return BLANK_STRING; IASTFileLocation f = node.getFileLocation(); if( f == null ) return BLANK_STRING; return f.getFileName(); } public int getOffset() { IASTFileLocation f = node.getFileLocation(); if( f == null ) return 0; return f.getNodeOffset(); } public int getLength() { IASTFileLocation f = node.getFileLocation(); if( f == null ) return 0; return f.getNodeLength(); } public void setFiltersFlag(int flag) { filterFlag |= flag; if (parent != null ) { parent.setFiltersFlag(flag); } } public int getFiltersFlag() { return filterFlag; } public int relativeNodePosition( IASTNode n ){ ASTNode astNode = (ASTNode) n; ASTNode thisNode = (ASTNode) getNode(); if( thisNode.getOffset() > astNode.getOffset() ) return -1; if( (thisNode.getOffset() + thisNode.getLength()) < (astNode.getOffset() + astNode.getLength()) ) return 1; return 0; } private static class ASTPropertySource implements IPropertySource { private static final String DOMEXCEPTION_THIS_METHOD_ISN_T_SUPPORTED_BY_THIS_OBJECT = "DOMException - this method isn't supported by this Object"; //$NON-NLS-1$ private static final String FLUSH_CACHE_METHOD_NAME = "flushCache"; //$NON-NLS-1$ private static final IPropertyDescriptor[] BLANK_DESCRIPTORS = new IPropertyDescriptor[0]; private static final String OPEN_PAREN = " ("; //$NON-NLS-1$ private static final String CLOSE_PAREN = ")"; //$NON-NLS-1$ private static final String L_BRACKET_STRING = "["; //$NON-NLS-1$ private static final String R_BRACKET_STRING = "]"; //$NON-NLS-1$ private static final String CLONE_METHOD_NAME = "clone"; //$NON-NLS-1$ private static final String NO_ELEMENT_STRING = "[0]"; //$NON-NLS-1$ private static final String SEMI = ";"; //$NON-NLS-1$ private static final String GETTYPE_METHOD_NAME = "getType"; //$NON-NLS-1$ private static final String EXCEPTION_ON = " on "; //$NON-NLS-1$ private static final String NULL_STRING = "null"; //$NON-NLS-1$ private static final String OBJECT_SEPARATOR = ", "; //$NON-NLS-1$ private static final String COLON_SEPARATOR = ": "; //$NON-NLS-1$ private static final String IBINDING_TAG = "IBinding: "; //$NON-NLS-1$ private static final String EMPTY_PARAMETER = "()"; //$NON-NLS-1$ private static final String NODE_PREFIX = "Node: "; //$NON-NLS-1$ private static final String BINDING_PREFIX = "Binding: "; //$NON-NLS-1$ private static final int DEFAULT_DESCRIPTOR_SIZE = 4; IASTNode node = null; public ASTPropertySource(IASTNode node) { this.node = node; } public Object getEditableValue() { return null; } public IPropertyDescriptor[] getPropertyDescriptors() { if (node instanceof IASTTranslationUnit) // skip the properties for the TU (too expensive) return BLANK_DESCRIPTORS; IPropertyDescriptor[] descriptors = new IPropertyDescriptor[DEFAULT_DESCRIPTOR_SIZE]; if (node instanceof IASTName) { IPropertyDescriptor[] desc = getPropertyDescriptors(((IASTName)node).resolveBinding()); if (desc != null) for (IPropertyDescriptor element : desc) descriptors = (IPropertyDescriptor[])ArrayUtil.append(IPropertyDescriptor.class, descriptors, element); desc = getPropertyDescriptors(node); if (desc != null) for (IPropertyDescriptor element : desc) descriptors = (IPropertyDescriptor[])ArrayUtil.append(IPropertyDescriptor.class, descriptors, element); } else { IPropertyDescriptor[] desc = getPropertyDescriptors(node); if (desc != null) for (IPropertyDescriptor element : desc) descriptors = (IPropertyDescriptor[])ArrayUtil.append(IPropertyDescriptor.class, descriptors, element); } return (IPropertyDescriptor[])ArrayUtil.trim(IPropertyDescriptor.class, descriptors); } private IPropertyDescriptor[] getPropertyDescriptors(Object obj) { IPropertyDescriptor[] desc = new IPropertyDescriptor[DEFAULT_DESCRIPTOR_SIZE]; if (obj==null) return BLANK_DESCRIPTORS; Class<?> objClass = obj.getClass(); Class<?>[] interfaces = objClass.getInterfaces(); for (Class<?> interface1 : interfaces) { Method[] methods = interface1.getMethods(); for(int j=0; j<methods.length; j++) { // multiple interfaces can have the same method, so don't duplicate that method in the property view // which causes an ArrayIndexOutOfBoundsException elsewhere if (alreadyEncountered(methods[j], desc)) continue; if (methods[j].getParameterTypes().length > 0 || (!shouldInvokeMethod(methods[j].getName()))) continue; // only do getters, that aren't in the bad list (like clone()) TextPropertyDescriptor text = null; if (obj instanceof IBinding) text = new TextPropertyDescriptor(BINDING_PREFIX + methods[j].getName(), methods[j].getName() + EMPTY_PARAMETER); else text = new TextPropertyDescriptor(NODE_PREFIX + methods[j].getName(), methods[j].getName() + EMPTY_PARAMETER); if (text != null) { if (obj instanceof IBinding) text.setCategory(IBINDING_TAG + ((IASTName)node).resolveBinding().getClass().getName().substring(((IASTName)node).resolveBinding().getClass().getName().lastIndexOf(PERIOD) + 1) + COLON_SEPARATOR + getValueString(((IASTName)node).resolveBinding())); else text.setCategory(objClass.getName().substring(objClass.getName().lastIndexOf(PERIOD) + 1) + COLON_SEPARATOR + getValueString(node)); desc = (IPropertyDescriptor[])ArrayUtil.append(IPropertyDescriptor.class, desc, text); } } } return (IPropertyDescriptor[])ArrayUtil.trim(IPropertyDescriptor.class, desc); } private boolean alreadyEncountered(Method method, IPropertyDescriptor[] desc) { StringBuffer name = null; for (IPropertyDescriptor element : desc) { if (element == null) // reached the end of the array break; name = new StringBuffer(); name.append(method.getName()); name.append(EMPTY_PARAMETER); if (name.toString().equals(element.getDisplayName())) return true; } return false; } public Object getPropertyValue(Object id) { if (!(id instanceof String)) return BLANK_STRING; Class<?> nodeClass = node.getClass(); String value = BLANK_STRING; try { Object result = null; if (node instanceof IASTName && ((String)id).startsWith(BINDING_PREFIX)) { String methodName = id.toString(); methodName = methodName.replaceAll(BINDING_PREFIX, BLANK_STRING); Method method = ((IASTName)node).resolveBinding().getClass().getMethod(methodName, new Class[0]); // only going to be getter methods... result = method.invoke(((IASTName)node).resolveBinding()); } else { String methodName = id.toString(); methodName = methodName.replaceAll(NODE_PREFIX, BLANK_STRING); Method method = nodeClass.getMethod(methodName, new Class[0]); // only going to be getter methods... method.setAccessible(true); result = method.invoke(node); } if (result == null) { value = NULL_STRING; } else if (result.getClass().isArray()) { // if it's an array if (result.getClass().getComponentType().equals(char.class)) // array of char value = String.valueOf((char[])result); else if (result.getClass().isPrimitive()) { value = trimObjectToString(result.toString()); } else value = getValueString((Object[])result); } else { value = getValueString(result); } } catch (Exception e) { if (e instanceof InvocationTargetException) { if (((InvocationTargetException)e).getTargetException() instanceof DOMException) return DOMEXCEPTION_THIS_METHOD_ISN_T_SUPPORTED_BY_THIS_OBJECT; e.printStackTrace(); // display all exceptions to developers return trimObjectToString(((InvocationTargetException)e).getTargetException().toString()) + EXCEPTION_ON + ((InvocationTargetException)e).getTargetException().getStackTrace()[0].toString(); } return e.toString(); } return value; } private String trimObjectToString(String str) { return str.substring(str.lastIndexOf(PERIOD) + 1); } private String getValueString(Object obj) { if (obj == null) return NULL_STRING; StringBuffer buffer = new StringBuffer(); if (obj.getClass().isPrimitive()) { buffer.append(trimObjectToString(obj.toString())); } else if (obj instanceof ASTNodeProperty) { buffer.append(((ASTNodeProperty)obj).getName()); } else if (obj instanceof IASTProblemHolder) { IASTProblemHolder ph= (IASTProblemHolder) obj; IASTProblem problem= ph.getProblem(); buffer.append(problem.getMessage()); } else if (obj instanceof IASTName) { final String toString = ((IASTName)obj).toString(); if( toString != null ) { buffer.append( trimObjectToString(toString) ); buffer.append(COLON_SEPARATOR); buffer.append( getType(((IASTName)obj).resolveBinding()) ); } } else if (obj instanceof IType) { buffer.append(getType(obj)); } else if (obj instanceof IBinding) { buffer.append(((IBinding)obj).getName()); buffer.append(COLON_SEPARATOR); buffer.append(getType(obj)); } else if (obj instanceof IASTExpression) { buffer.append(ASTStringUtil.getExpressionString((IASTExpression)obj)); } else if (obj instanceof IASTNode) { String utilString = DOMAST.getNodeSignature((IASTNode)obj); if (utilString != null && !utilString.equals(BLANK_STRING)) { buffer.append(trimObjectToString(obj.toString())); buffer.append(COLON_SEPARATOR); buffer.append(utilString); } else buffer.append(trimObjectToString(obj.toString())); } else buffer.append(obj.toString()); if( obj instanceof IBinding ){ buffer.append( OPEN_PAREN ); buffer.append( Integer.toHexString(obj.hashCode()) ); buffer.append( CLOSE_PAREN ); } return buffer.toString(); } private String getType(Object obj) { if (obj == null) return NULL_STRING; if (obj instanceof IType) return ASTTypeUtil.getType((IType)obj); Method[] methods = obj.getClass().getMethods(); boolean hasGetType = false; int i=0; for(; i<methods.length; i++) { if (methods[i].getName().equals(GETTYPE_METHOD_NAME)) { hasGetType = true; break; } } if (hasGetType) { try { Object result = methods[i].invoke(obj); if (result instanceof IType) { return ASTTypeUtil.getType((IType)result); } } catch (Exception e) { if (e instanceof InvocationTargetException) { if (((InvocationTargetException)e).getTargetException() instanceof DOMException) return DOMEXCEPTION_THIS_METHOD_ISN_T_SUPPORTED_BY_THIS_OBJECT; e.printStackTrace(); // display all exceptions to developers return trimObjectToString(((InvocationTargetException)e).getTargetException().toString()) + EXCEPTION_ON + ((InvocationTargetException)e).getTargetException().getStackTrace()[0].toString(); } return e.toString(); } } return BLANK_STRING; // if there is no type } private String getValueString(Object[] objs) { if (objs.length==0) return trimObjectToString(objs.getClass().getName()).replaceAll(SEMI, BLANK_STRING) + NO_ELEMENT_STRING; StringBuffer buffer = new StringBuffer(); buffer.append(L_BRACKET_STRING); for(int i=0; i<objs.length; i++) { buffer.append(getValueString(objs[i])); if (i<objs.length-1) buffer.append(OBJECT_SEPARATOR); } buffer.append(R_BRACKET_STRING); return buffer.toString(); } // used to determine if a getter method should be invoked or not, there may be a list of them in the future... private boolean shouldInvokeMethod(String method) { if (method.equals(CLONE_METHOD_NAME)) return false; if (method.equals(FLUSH_CACHE_METHOD_NAME)) return false; if (method.startsWith("set")) return false; return true; } public boolean isPropertySet(Object id) { return false; } public void resetPropertyValue(Object id) { } public void setPropertyValue(Object id, Object value) { } } }