blob: f0ed7c07eb5c77a715e2e4b9acee8e699e65bc19 [file] [log] [blame]
Ben Dodson920dbbb2010-08-04 15:21:06 -07001/*
2 * Copyright (C) 2010 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.doclava;
18
19import com.google.clearsilver.jsilver.data.Data;
20import java.util.Comparator;
21
22public class FieldInfo extends MemberInfo {
23 public static final Comparator<FieldInfo> comparator = new Comparator<FieldInfo>() {
24 public int compare(FieldInfo a, FieldInfo b) {
25 return a.name().compareTo(b.name());
26 }
27 };
28
29 public FieldInfo(String name, ClassInfo containingClass, ClassInfo realContainingClass,
30 boolean isPublic, boolean isProtected, boolean isPackagePrivate, boolean isPrivate,
31 boolean isFinal, boolean isStatic, boolean isTransient, boolean isVolatile,
32 boolean isSynthetic, TypeInfo type, String rawCommentText, Object constantValue,
33 SourcePositionInfo position, AnnotationInstanceInfo[] annotations) {
34 super(rawCommentText, name, null, containingClass, realContainingClass, isPublic, isProtected,
35 isPackagePrivate, isPrivate, isFinal, isStatic, isSynthetic, chooseKind(isFinal, isStatic),
36 position, annotations);
37 mIsTransient = isTransient;
38 mIsVolatile = isVolatile;
39 mType = type;
40 mConstantValue = constantValue;
41 }
42
43 public FieldInfo cloneForClass(ClassInfo newContainingClass) {
44 return new FieldInfo(name(), newContainingClass, realContainingClass(), isPublic(),
45 isProtected(), isPackagePrivate(), isPrivate(), isFinal(), isStatic(), isTransient(),
46 isVolatile(), isSynthetic(), mType, getRawCommentText(), mConstantValue, position(),
47 annotations());
48 }
49
50 static String chooseKind(boolean isFinal, boolean isStatic) {
51 if (isStatic && isFinal) {
52 return "constant";
53 } else {
54 return "field";
55 }
56 }
57
58 public String qualifiedName() {
59 String parentQName
60 = (containingClass() != null) ? (containingClass().qualifiedName() + ".") : "";
61 return parentQName + name();
62 }
63
64 public TypeInfo type() {
65 return mType;
66 }
67
68 public boolean isConstant() {
69 return isStatic() && isFinal();
70 }
71
72 public TagInfo[] firstSentenceTags() {
73 return comment().briefTags();
74 }
75
76 public TagInfo[] inlineTags() {
77 return comment().tags();
78 }
79
80 public Object constantValue() {
81 return mConstantValue;
82 }
83
84 public String constantLiteralValue() {
85 return constantLiteralValue(mConstantValue);
86 }
87
88 public void setDeprecated(boolean deprecated) {
89 mDeprecatedKnown = true;
90 mIsDeprecated = deprecated;
91 }
92
93 public boolean isDeprecated() {
94 if (!mDeprecatedKnown) {
95 boolean commentDeprecated = comment().isDeprecated();
96 boolean annotationDeprecated = false;
97 for (AnnotationInstanceInfo annotation : annotations()) {
98 if (annotation.type().qualifiedName().equals("java.lang.Deprecated")) {
99 annotationDeprecated = true;
100 break;
101 }
102 }
103
104 if (commentDeprecated != annotationDeprecated) {
105 Errors.error(Errors.DEPRECATION_MISMATCH, position(), "Field "
106 + mContainingClass.qualifiedName() + "." + name()
107 + ": @Deprecated annotation and @deprecated comment do not match");
108 }
109
110 mIsDeprecated = commentDeprecated | annotationDeprecated;
111 mDeprecatedKnown = true;
112 }
113 return mIsDeprecated;
114 }
115
116 public static String constantLiteralValue(Object val) {
117 String str = null;
118 if (val != null) {
119 if (val instanceof Boolean || val instanceof Byte || val instanceof Short
120 || val instanceof Integer) {
121 str = val.toString();
122 }
123 // catch all special values
124 else if (val instanceof Double) {
Ben Dodsonf22ba052010-08-12 11:47:06 -0700125 str = canonicalizeFloatingPoint(val.toString(), "");
126 } else if (val instanceof Float) {
127 str = canonicalizeFloatingPoint(val.toString(), "f");
Ben Dodson920dbbb2010-08-04 15:21:06 -0700128 } else if (val instanceof Long) {
129 str = val.toString() + "L";
Ben Dodson920dbbb2010-08-04 15:21:06 -0700130 } else if (val instanceof Character) {
131 str = String.format("\'\\u%04x\'", val);
132 } else if (val instanceof String) {
133 str = "\"" + javaEscapeString((String) val) + "\"";
134 } else {
135 str = "<<<<" + val.toString() + ">>>>";
136 }
137 }
138 if (str == null) {
139 str = "null";
140 }
141 return str;
142 }
143
Ben Dodsonf22ba052010-08-12 11:47:06 -0700144 /**
145 * Returns a canonical string representation of a floating point
146 * number. The representation is suitable for use as Java source
147 * code. This method also addresses bug #4428022 in the Sun JDK.
148 */
149 private static String canonicalizeFloatingPoint(String val, String suffix) {
150 if (val.equals("Infinity")) {
151 return "(1.0" + suffix + " / 0.0" + suffix + ")";
152 } else if (val.equals("-Infinity")) {
153 return "(-1.0" + suffix + " / 0.0" + suffix + ")";
154 } else if (val.equals("NaN")) {
155 return "(0.0" + suffix + " / 0.0" + suffix + ")";
156 }
157
158 String str = val.toString();
159 if (str.indexOf('E') != -1) {
160 return str + suffix;
161 }
162
163 // 1.0 is the only case where a trailing "0" is allowed.
164 // 1.00 is canonicalized as 1.0.
165 int i = str.length() - 1;
166 int d = str.indexOf('.');
167 while (i >= d + 2 && str.charAt(i) == '0') {
168 str = str.substring(0, i--);
169 }
170 return str + suffix;
171 }
172
Ben Dodson920dbbb2010-08-04 15:21:06 -0700173 public static String javaEscapeString(String str) {
174 String result = "";
175 final int N = str.length();
176 for (int i = 0; i < N; i++) {
177 char c = str.charAt(i);
178 if (c == '\\') {
179 result += "\\\\";
180 } else if (c == '\t') {
181 result += "\\t";
182 } else if (c == '\b') {
183 result += "\\b";
184 } else if (c == '\r') {
185 result += "\\r";
186 } else if (c == '\n') {
187 result += "\\n";
188 } else if (c == '\f') {
189 result += "\\f";
190 } else if (c == '\'') {
191 result += "\\'";
192 } else if (c == '\"') {
193 result += "\\\"";
194 } else if (c >= ' ' && c <= '~') {
195 result += c;
196 } else {
197 result += String.format("\\u%04x", new Integer((int) c));
198 }
199 }
200 return result;
201 }
202
203
204 public void makeHDF(Data data, String base) {
205 data.setValue(base + ".kind", kind());
206 type().makeHDF(data, base + ".type");
207 data.setValue(base + ".name", name());
208 data.setValue(base + ".href", htmlPage());
209 data.setValue(base + ".anchor", anchor());
210 TagInfo.makeHDF(data, base + ".shortDescr", firstSentenceTags());
211 TagInfo.makeHDF(data, base + ".descr", inlineTags());
212 TagInfo.makeHDF(data, base + ".deprecated", comment().deprecatedTags());
213 TagInfo.makeHDF(data, base + ".seeAlso", comment().seeTags());
214 data.setValue(base + ".since", getSince());
215 data.setValue(base + ".final", isFinal() ? "final" : "");
216 data.setValue(base + ".static", isStatic() ? "static" : "");
217 if (isPublic()) {
218 data.setValue(base + ".scope", "public");
219 } else if (isProtected()) {
220 data.setValue(base + ".scope", "protected");
221 } else if (isPackagePrivate()) {
222 data.setValue(base + ".scope", "");
223 } else if (isPrivate()) {
224 data.setValue(base + ".scope", "private");
225 }
226 Object val = mConstantValue;
227 if (val != null) {
228 String dec = null;
229 String hex = null;
230 String str = null;
231
232 if (val instanceof Boolean) {
233 str = ((Boolean) val).toString();
234 } else if (val instanceof Byte) {
235 dec = String.format("%d", val);
236 hex = String.format("0x%02x", val);
237 } else if (val instanceof Character) {
238 dec = String.format("\'%c\'", val);
239 hex = String.format("0x%04x", val);
240 } else if (val instanceof Double) {
241 str = ((Double) val).toString();
242 } else if (val instanceof Float) {
243 str = ((Float) val).toString();
244 } else if (val instanceof Integer) {
245 dec = String.format("%d", val);
246 hex = String.format("0x%08x", val);
247 } else if (val instanceof Long) {
248 dec = String.format("%d", val);
249 hex = String.format("0x%016x", val);
250 } else if (val instanceof Short) {
251 dec = String.format("%d", val);
252 hex = String.format("0x%04x", val);
253 } else if (val instanceof String) {
254 str = "\"" + ((String) val) + "\"";
255 } else {
256 str = "";
257 }
258
259 if (dec != null && hex != null) {
260 data.setValue(base + ".constantValue.dec", Doclava.escape(dec));
261 data.setValue(base + ".constantValue.hex", Doclava.escape(hex));
262 } else {
263 data.setValue(base + ".constantValue.str", Doclava.escape(str));
264 data.setValue(base + ".constantValue.isString", "1");
265 }
266 }
267
268 setFederatedReferences(data, base);
269 }
270
271 @Override
272 public boolean isExecutable() {
273 return false;
274 }
275
276 public boolean isTransient() {
277 return mIsTransient;
278 }
279
280 public boolean isVolatile() {
281 return mIsVolatile;
282 }
283
284 // Check the declared value with a typed comparison, not a string comparison,
285 // to accommodate toolchains with different fp -> string conversions.
286 private boolean valueEquals(FieldInfo other) {
287 if ((mConstantValue == null) != (other.mConstantValue == null)) {
288 return false;
289 }
290
291 // Null values are considered equal
292 if (mConstantValue == null) {
293 return true;
294 }
295
296 // TODO: This method is called through from an XML comparison only right now,
297 // and mConstantValue is always a String. Get rid of this assertion.
298 if (!(mConstantValue instanceof String && other.mConstantValue instanceof String)) {
299 throw new AssertionError("Bad type for field value");
300 }
301
Ben Dodsonf22ba052010-08-12 11:47:06 -0700302 return mType.equals(other.mType)
303 && mConstantValue.equals(other.mConstantValue);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700304 }
305
306 public boolean isConsistent(FieldInfo fInfo) {
307 boolean consistent = true;
308 if (!mType.equals(fInfo.mType)) {
309 Errors.error(Errors.CHANGED_TYPE, fInfo.position(), "Field " + fInfo.qualifiedName()
310 + " has changed type");
311 consistent = false;
312 } else if (!this.valueEquals(fInfo)) {
313 Errors.error(Errors.CHANGED_VALUE, fInfo.position(), "Field " + fInfo.qualifiedName()
314 + " has changed value from " + mConstantValue + " to " + fInfo.mConstantValue);
315 consistent = false;
316 }
317
318 if (!scope().equals(fInfo.scope())) {
319 Errors.error(Errors.CHANGED_SCOPE, fInfo.position(), "Method " + fInfo.qualifiedName()
320 + " changed scope from " + this.scope() + " to " + fInfo.scope());
321 consistent = false;
322 }
323
324 if (mIsStatic != fInfo.mIsStatic) {
325 Errors.error(Errors.CHANGED_STATIC, fInfo.position(), "Field " + fInfo.qualifiedName()
326 + " has changed 'static' qualifier");
327 consistent = false;
328 }
329
330 if (mIsFinal != fInfo.mIsFinal) {
331 Errors.error(Errors.CHANGED_FINAL, fInfo.position(), "Field " + fInfo.qualifiedName()
332 + " has changed 'final' qualifier");
333 consistent = false;
334 }
335
336 if (mIsTransient != fInfo.mIsTransient) {
337 Errors.error(Errors.CHANGED_TRANSIENT, fInfo.position(), "Field " + fInfo.qualifiedName()
338 + " has changed 'transient' qualifier");
339 consistent = false;
340 }
341
342 if (mIsVolatile != fInfo.mIsVolatile) {
343 Errors.error(Errors.CHANGED_VOLATILE, fInfo.position(), "Field " + fInfo.qualifiedName()
344 + " has changed 'volatile' qualifier");
345 consistent = false;
346 }
347
348 if (isDeprecated() != fInfo.isDeprecated()) {
349 Errors.error(Errors.CHANGED_DEPRECATED, fInfo.position(), "Field " + fInfo.qualifiedName()
350 + " has changed deprecation state");
351 consistent = false;
352 }
353
354 return consistent;
355 }
356
357 boolean mIsTransient;
358 boolean mIsVolatile;
359 boolean mDeprecatedKnown;
360 boolean mIsDeprecated;
361 TypeInfo mType;
362 Object mConstantValue;
363}