/* gnu.classpath.tools.gjdoc.FieldDocImpl Copyright (C) 2001 Free Software Foundation, Inc. This file is part of GNU Classpath. GNU Classpath is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GNU Classpath is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Classpath; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ package gnu.classpath.tools.gjdoc; import java.util.*; import com.sun.javadoc.*; import java.lang.reflect.Modifier; import gnu.classpath.tools.gjdoc.expr.Evaluator; import gnu.classpath.tools.gjdoc.expr.CircularExpressionException; import gnu.classpath.tools.gjdoc.expr.IllegalExpressionException; public class FieldDocImpl extends MemberDocImpl implements FieldDoc, Cloneable { private boolean isTransient; private boolean isVolatile; private String valueLiteral; private Object constantValue; private boolean constantValueEvaluated; private FieldDocImpl(ClassDoc containingClass, PackageDoc containingPackage, SourcePosition position) { super(containingClass, containingPackage, position); } private static FieldDocImpl createFieldDoc(FieldDocImpl prototype, String fieldDef, String fieldValueLiteral) { if (null != fieldValueLiteral && fieldValueLiteral.length() == 0) { fieldValueLiteral = null; } try { FieldDocImpl fieldDoc=(FieldDocImpl)prototype.clone(); String dimSuffix=""; while (fieldDef.trim().endsWith("[") || fieldDef.trim().endsWith("]")) { fieldDef=fieldDef.trim(); dimSuffix=fieldDef.charAt(fieldDef.length()-1)+dimSuffix; fieldDef=fieldDef.substring(0,fieldDef.length()-1); } fieldDoc.setTypeName(fieldDoc.getTypeName()+dimSuffix); fieldDoc.setName(fieldDef.trim()); fieldDoc.setValueLiteral(fieldValueLiteral); return fieldDoc; } catch (CloneNotSupportedException e) { // should not happen e.printStackTrace(); return null; } } public static Collection createFromSource(ClassDoc containingClass, PackageDoc containingPackage, char[] source, int startIndex, int endIndex) { List rcList=new ArrayList(); FieldDocImpl fd=new FieldDocImpl(containingClass, containingPackage, DocImpl.getPosition(containingClass, source, startIndex)); int ndx=fd.parseModifiers(source, startIndex, endIndex); if (containingClass.isInterface()) { fd.accessLevel = ACCESS_PUBLIC; } final int STATE_FIELDNAME = 1; final int STATE_FIELDVALUE = 2; final int STATE_QUOTE = 3; final int STATE_QUOTEBS = 4; final int STATE_SQUOTE = 5; final int STATE_SQUOTEBS = 6; final int STATE_COMMENT = 7; final int STATE_LINECOMMENT = 8; int lastFieldDefStart = ndx; int state = STATE_FIELDNAME; int prevState = state; int bracketCount = 0; StringBuffer fieldNameBuf = new StringBuffer(); StringBuffer fieldValueLiteralBuf = new StringBuffer(); for (int i=ndx; i<endIndex; ++i) { char c = source[i]; char nextChar = '\0'; if (i + 1 < endIndex) { nextChar = source[i + 1]; } switch (state) { case STATE_FIELDNAME: if ('/' == c && '/' == nextChar) { prevState = state; state = STATE_LINECOMMENT; } else if ('/' == c && '*' == nextChar) { prevState = state; state = STATE_COMMENT; } else if (',' == c || ';' == c) { rcList.add(createFieldDoc(fd, fieldNameBuf.toString(), null)); fieldNameBuf.setLength(0); } else if ('=' == c) { state = STATE_FIELDVALUE; } else if (!(' ' == c || '\n' == c || '\r' == c || '\t' == c)) { fieldNameBuf.append(c); } break; case STATE_FIELDVALUE: if ('/' == c && '/' == nextChar) { prevState = state; state = STATE_LINECOMMENT; } else if ('/' == c && '*' == nextChar) { prevState = state; state = STATE_COMMENT; } else if ('\"' == c) { prevState = state; state = STATE_QUOTE; fieldValueLiteralBuf.append(c); } else if ('\'' == c) { prevState = state; state = STATE_SQUOTE; fieldValueLiteralBuf.append(c); } else if ('{' == c || '(' == c) { ++ bracketCount; fieldValueLiteralBuf.append(c); } else if ('}' == c || ')' == c) { -- bracketCount; fieldValueLiteralBuf.append(c); } else if (0 == bracketCount && (',' == c || ';' == c)) { rcList.add(createFieldDoc(fd, fieldNameBuf.toString(), fieldValueLiteralBuf.toString())); fieldNameBuf.setLength(0); fieldValueLiteralBuf.setLength(0); state = STATE_FIELDNAME; } else { fieldValueLiteralBuf.append(c); } break; case STATE_QUOTE: fieldValueLiteralBuf.append(c); if ('\\' == c) { state = STATE_QUOTEBS; } else if ('\"' == c) { state = prevState; } break; case STATE_SQUOTE: fieldValueLiteralBuf.append(c); if ('\\' == c) { state = STATE_SQUOTEBS; } else if ('\'' == c) { state = prevState; } break; case STATE_QUOTEBS: fieldValueLiteralBuf.append(c); state = STATE_QUOTE; break; case STATE_SQUOTEBS: fieldValueLiteralBuf.append(c); state = STATE_SQUOTE; break; case STATE_LINECOMMENT: if ('\n' == c) { state = prevState; } break; case STATE_COMMENT: if ('*' == c && '/' == nextChar) { ++ i; state = prevState; } break; } } if (fieldNameBuf.length() > 0) { rcList.add(createFieldDoc(fd, fieldNameBuf.toString(), fieldValueLiteralBuf.toString())); } return rcList; } public boolean isField() { return true; } public boolean isTransient() { return isTransient; } public boolean isVolatile() { return isVolatile; } public SerialFieldTag[] serialFieldTags() { return new SerialFieldTag[0]; } public int modifierSpecifier() { return super.modifierSpecifier() | (isVolatile()?Modifier.VOLATILE:0) | (isTransient()?Modifier.TRANSIENT:0) ; } protected boolean processModifier(String word) { if (super.processModifier(word)) { return true; } else if (word.equals("transient")) { isTransient=true; return true; } else if (word.equals("volatile")) { isVolatile=true; return true; } else { return false; } } void resolve() { resolveTags(); } public boolean hasSerialTag() { return true; //tagMap.get("serial")!=null; } public String toString() { return name(); } public Object constantValue() { return constantValue(new HashSet()); } public Object constantValue(Set visitedFields) { if (!isStatic() || !isFinal() || (!type().isPrimitive() && !"java.lang.String".equals(type().qualifiedTypeName())) || type.dimension().length()>0 || null == valueLiteral) { return null; } else { if (!constantValueEvaluated) { visitedFields.add(this); String expression = "(" + type().typeName() + ")(" + valueLiteral + ")"; try { this.constantValue = Evaluator.evaluate(expression, visitedFields, (ClassDocImpl)containingClass()); } catch (CircularExpressionException e) { // FIXME: This should use the error reporter System.err.println("WARNING: Cannot resolve expression for field " + containingClass.qualifiedTypeName() + "." + name() + ": " + e.getMessage()); } catch (IllegalExpressionException ignore) { } constantValueEvaluated = true; } return this.constantValue; } } private static void appendCharString(StringBuffer result, char c, boolean inSingleCuotes) { switch (c) { case '\b': result.append("\\b"); break; case '\t': result.append("\\t"); break; case '\n': result.append("\\n"); break; case '\f': result.append("\\f"); break; case '\r': result.append("\\r"); break; case '\"': result.append("\\\""); break; case '\'': result.append(inSingleCuotes ? "\\'" : "'"); break; default: if (c >= 32 && c <= 127) { result.append(c); } else { result.append("\\u"); String hexValue = Integer.toString((int)c, 16); int zeroCount = 4 - hexValue.length(); for (int i=0; i<zeroCount; ++i) { result.append('0'); } result.append(hexValue); } } } public String constantValueExpression() { Object value = constantValue(); if (null == value) { return "null"; } else if (value instanceof String) { StringBuffer result = new StringBuffer("\""); char[] chars = ((String)value).toCharArray(); for (int i=0; i<chars.length; ++i) { appendCharString(result, chars[i], false); } result.append("\""); return result.toString(); } else if (value instanceof Float) { return value.toString() + "f"; } else if (value instanceof Long) { return value.toString() + "L"; } else if (value instanceof Character) { StringBuffer result = new StringBuffer("'"); appendCharString(result, ((Character)value).charValue(), false); result.append("'"); return result.toString(); } else /* if (value instanceof Double || value instanceof Integer || value instanceof Short || value instanceof Byte) */ { return value.toString(); } } void setValueLiteral(String valueLiteral) { this.valueLiteral = valueLiteral; } public boolean isStatic() { return super.isStatic() || containingClass().isInterface(); } public boolean isFinal() { return super.isFinal() || containingClass().isInterface(); } }