blob: 83d748a36678cc5fc6238a04f19d72b4083c6bef [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
Joe Onorato04099252011-03-09 13:34:18 -080019import com.google.doclava.apicheck.ApiParseException;
Ben Dodson920dbbb2010-08-04 15:21:06 -070020import com.google.clearsilver.jsilver.data.Data;
21import java.util.Comparator;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070022import java.util.ArrayList;
Ben Dodson920dbbb2010-08-04 15:21:06 -070023
24public class FieldInfo extends MemberInfo {
25 public static final Comparator<FieldInfo> comparator = new Comparator<FieldInfo>() {
26 public int compare(FieldInfo a, FieldInfo b) {
27 return a.name().compareTo(b.name());
28 }
29 };
30
31 public FieldInfo(String name, ClassInfo containingClass, ClassInfo realContainingClass,
32 boolean isPublic, boolean isProtected, boolean isPackagePrivate, boolean isPrivate,
33 boolean isFinal, boolean isStatic, boolean isTransient, boolean isVolatile,
34 boolean isSynthetic, TypeInfo type, String rawCommentText, Object constantValue,
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070035 SourcePositionInfo position, ArrayList<AnnotationInstanceInfo> annotations) {
Ben Dodson920dbbb2010-08-04 15:21:06 -070036 super(rawCommentText, name, null, containingClass, realContainingClass, isPublic, isProtected,
Dan Bornstein62aaca82010-11-29 13:35:43 -080037 isPackagePrivate, isPrivate, isFinal, isStatic, isSynthetic, chooseKind(isFinal, isStatic, constantValue),
Ben Dodson920dbbb2010-08-04 15:21:06 -070038 position, annotations);
39 mIsTransient = isTransient;
40 mIsVolatile = isVolatile;
41 mType = type;
42 mConstantValue = constantValue;
43 }
44
45 public FieldInfo cloneForClass(ClassInfo newContainingClass) {
46 return new FieldInfo(name(), newContainingClass, realContainingClass(), isPublic(),
47 isProtected(), isPackagePrivate(), isPrivate(), isFinal(), isStatic(), isTransient(),
48 isVolatile(), isSynthetic(), mType, getRawCommentText(), mConstantValue, position(),
49 annotations());
50 }
51
Dan Bornstein62aaca82010-11-29 13:35:43 -080052 static String chooseKind(boolean isFinal, boolean isStatic, Object constantValue)
53 {
54 return isConstant(isFinal, isStatic, constantValue) ? "constant" : "field";
Ben Dodson920dbbb2010-08-04 15:21:06 -070055 }
56
57 public String qualifiedName() {
58 String parentQName
59 = (containingClass() != null) ? (containingClass().qualifiedName() + ".") : "";
60 return parentQName + name();
61 }
62
63 public TypeInfo type() {
64 return mType;
65 }
66
Dan Bornstein62aaca82010-11-29 13:35:43 -080067 static boolean isConstant(boolean isFinal, boolean isStatic, Object constantValue)
68 {
69 /*
70 * Note: There is an ambiguity in the doc API that prevents us
71 * from distinguishing a constant-null from the lack of a
72 * constant at all. We err on the side of treating all null
73 * constantValues as meaning that the field is not a constant,
74 * since having a static final field assigned to null is both
75 * unusual and generally pretty useless.
76 */
77 return isFinal && isStatic && (constantValue != null);
78 }
79
Ben Dodson920dbbb2010-08-04 15:21:06 -070080 public boolean isConstant() {
Dan Bornstein62aaca82010-11-29 13:35:43 -080081 return isConstant(isFinal(), isStatic(), mConstantValue);
Ben Dodson920dbbb2010-08-04 15:21:06 -070082 }
83
84 public TagInfo[] firstSentenceTags() {
85 return comment().briefTags();
86 }
87
88 public TagInfo[] inlineTags() {
89 return comment().tags();
90 }
91
92 public Object constantValue() {
93 return mConstantValue;
94 }
95
96 public String constantLiteralValue() {
97 return constantLiteralValue(mConstantValue);
98 }
99
100 public void setDeprecated(boolean deprecated) {
101 mDeprecatedKnown = true;
102 mIsDeprecated = deprecated;
103 }
104
105 public boolean isDeprecated() {
106 if (!mDeprecatedKnown) {
107 boolean commentDeprecated = comment().isDeprecated();
108 boolean annotationDeprecated = false;
109 for (AnnotationInstanceInfo annotation : annotations()) {
110 if (annotation.type().qualifiedName().equals("java.lang.Deprecated")) {
111 annotationDeprecated = true;
112 break;
113 }
114 }
115
116 if (commentDeprecated != annotationDeprecated) {
117 Errors.error(Errors.DEPRECATION_MISMATCH, position(), "Field "
118 + mContainingClass.qualifiedName() + "." + name()
119 + ": @Deprecated annotation and @deprecated comment do not match");
120 }
121
122 mIsDeprecated = commentDeprecated | annotationDeprecated;
123 mDeprecatedKnown = true;
124 }
125 return mIsDeprecated;
126 }
127
128 public static String constantLiteralValue(Object val) {
129 String str = null;
130 if (val != null) {
131 if (val instanceof Boolean || val instanceof Byte || val instanceof Short
132 || val instanceof Integer) {
133 str = val.toString();
134 }
135 // catch all special values
136 else if (val instanceof Double) {
Ben Dodsonf22ba052010-08-12 11:47:06 -0700137 str = canonicalizeFloatingPoint(val.toString(), "");
138 } else if (val instanceof Float) {
139 str = canonicalizeFloatingPoint(val.toString(), "f");
Ben Dodson920dbbb2010-08-04 15:21:06 -0700140 } else if (val instanceof Long) {
141 str = val.toString() + "L";
Ben Dodson920dbbb2010-08-04 15:21:06 -0700142 } else if (val instanceof Character) {
143 str = String.format("\'\\u%04x\'", val);
Joe Onorato04099252011-03-09 13:34:18 -0800144 System.out.println("str=" + str);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700145 } else if (val instanceof String) {
146 str = "\"" + javaEscapeString((String) val) + "\"";
147 } else {
148 str = "<<<<" + val.toString() + ">>>>";
149 }
150 }
151 if (str == null) {
152 str = "null";
153 }
154 return str;
155 }
156
Ben Dodsonf22ba052010-08-12 11:47:06 -0700157 /**
158 * Returns a canonical string representation of a floating point
159 * number. The representation is suitable for use as Java source
160 * code. This method also addresses bug #4428022 in the Sun JDK.
161 */
162 private static String canonicalizeFloatingPoint(String val, String suffix) {
163 if (val.equals("Infinity")) {
Joe Onorato04099252011-03-09 13:34:18 -0800164 return "(1.0" + suffix + "/0.0" + suffix + ")";
Ben Dodsonf22ba052010-08-12 11:47:06 -0700165 } else if (val.equals("-Infinity")) {
Joe Onorato04099252011-03-09 13:34:18 -0800166 return "(-1.0" + suffix + "/0.0" + suffix + ")";
Ben Dodsonf22ba052010-08-12 11:47:06 -0700167 } else if (val.equals("NaN")) {
Joe Onorato04099252011-03-09 13:34:18 -0800168 return "(0.0" + suffix + "/0.0" + suffix + ")";
Ben Dodsonf22ba052010-08-12 11:47:06 -0700169 }
170
171 String str = val.toString();
172 if (str.indexOf('E') != -1) {
173 return str + suffix;
174 }
175
176 // 1.0 is the only case where a trailing "0" is allowed.
177 // 1.00 is canonicalized as 1.0.
178 int i = str.length() - 1;
179 int d = str.indexOf('.');
180 while (i >= d + 2 && str.charAt(i) == '0') {
181 str = str.substring(0, i--);
182 }
183 return str + suffix;
184 }
185
Ben Dodson920dbbb2010-08-04 15:21:06 -0700186 public static String javaEscapeString(String str) {
187 String result = "";
188 final int N = str.length();
189 for (int i = 0; i < N; i++) {
190 char c = str.charAt(i);
191 if (c == '\\') {
192 result += "\\\\";
193 } else if (c == '\t') {
194 result += "\\t";
195 } else if (c == '\b') {
196 result += "\\b";
197 } else if (c == '\r') {
198 result += "\\r";
199 } else if (c == '\n') {
200 result += "\\n";
201 } else if (c == '\f') {
202 result += "\\f";
203 } else if (c == '\'') {
204 result += "\\'";
205 } else if (c == '\"') {
206 result += "\\\"";
207 } else if (c >= ' ' && c <= '~') {
208 result += c;
209 } else {
210 result += String.format("\\u%04x", new Integer((int) c));
211 }
212 }
213 return result;
214 }
215
Joe Onorato04099252011-03-09 13:34:18 -0800216 public static String javaUnescapeString(String str) throws ApiParseException {
217 final int N = str.length();
218 check: {
219 for (int i=0; i<N; i++) {
220 final char c = str.charAt(i);
221 if (c == '\\') {
222 break check;
223 }
224 }
225 return str;
226 }
227
228 final StringBuilder buf = new StringBuilder(str.length());
229 char escaped = 0;
230 final int START = 0;
231 final int CHAR1 = 1;
232 final int CHAR2 = 2;
233 final int CHAR3 = 3;
234 final int CHAR4 = 4;
235 final int ESCAPE = 5;
236 int state = START;
237
238 for (int i=0; i<N; i++) {
239 final char c = str.charAt(i);
240 switch (state) {
241 case START:
242 if (c == '\\') {
243 state = ESCAPE;
244 } else {
245 buf.append(c);
246 }
247 break;
248 case ESCAPE:
249 switch (c) {
250 case '\\':
251 buf.append('\\');
252 state = START;
253 break;
254 case 't':
255 buf.append('\t');
256 state = START;
257 break;
258 case 'b':
259 buf.append('\b');
260 state = START;
261 break;
262 case 'r':
263 buf.append('\r');
264 state = START;
265 break;
266 case 'n':
267 buf.append('\n');
268 state = START;
269 break;
270 case 'f':
271 buf.append('\f');
272 state = START;
273 break;
274 case '\'':
275 buf.append('\'');
276 state = START;
277 break;
278 case '\"':
279 buf.append('\"');
280 state = START;
281 break;
282 case 'u':
283 state = CHAR1;
284 escaped = 0;
285 break;
286 }
287 break;
288 case CHAR1:
289 case CHAR2:
290 case CHAR3:
291 case CHAR4:
292 escaped <<= 4;
293 if (c >= '0' && c <= '9') {
294 escaped |= c - '0';
295 } else if (c >= 'a' && c <= 'f') {
296 escaped |= 10 + (c - 'a');
297 } else if (c >= 'A' && c <= 'F') {
298 escaped |= 10 + (c - 'A');
299 } else {
300 throw new ApiParseException("bad escape sequence: '" + c + "' at pos " + i + " in: \""
301 + str + "\"");
302 }
303 if (state == CHAR4) {
304 buf.append(escaped);
305 state = START;
306 } else {
307 state++;
308 }
309 break;
310 }
311 }
312 if (state != START) {
313 throw new ApiParseException("unfinished escape sequence: " + str);
314 }
315 return buf.toString();
316 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700317
318 public void makeHDF(Data data, String base) {
319 data.setValue(base + ".kind", kind());
320 type().makeHDF(data, base + ".type");
321 data.setValue(base + ".name", name());
322 data.setValue(base + ".href", htmlPage());
323 data.setValue(base + ".anchor", anchor());
324 TagInfo.makeHDF(data, base + ".shortDescr", firstSentenceTags());
325 TagInfo.makeHDF(data, base + ".descr", inlineTags());
326 TagInfo.makeHDF(data, base + ".deprecated", comment().deprecatedTags());
327 TagInfo.makeHDF(data, base + ".seeAlso", comment().seeTags());
328 data.setValue(base + ".since", getSince());
Scott Main40ad1472012-10-24 18:19:17 -0700329 if (isDeprecated()) {
330 data.setValue(base + ".deprecatedsince", getDeprecatedSince());
331 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700332 data.setValue(base + ".final", isFinal() ? "final" : "");
333 data.setValue(base + ".static", isStatic() ? "static" : "");
334 if (isPublic()) {
335 data.setValue(base + ".scope", "public");
336 } else if (isProtected()) {
337 data.setValue(base + ".scope", "protected");
338 } else if (isPackagePrivate()) {
339 data.setValue(base + ".scope", "");
340 } else if (isPrivate()) {
341 data.setValue(base + ".scope", "private");
342 }
343 Object val = mConstantValue;
344 if (val != null) {
345 String dec = null;
346 String hex = null;
347 String str = null;
348
349 if (val instanceof Boolean) {
350 str = ((Boolean) val).toString();
351 } else if (val instanceof Byte) {
352 dec = String.format("%d", val);
353 hex = String.format("0x%02x", val);
354 } else if (val instanceof Character) {
355 dec = String.format("\'%c\'", val);
356 hex = String.format("0x%04x", val);
357 } else if (val instanceof Double) {
358 str = ((Double) val).toString();
359 } else if (val instanceof Float) {
360 str = ((Float) val).toString();
361 } else if (val instanceof Integer) {
362 dec = String.format("%d", val);
363 hex = String.format("0x%08x", val);
364 } else if (val instanceof Long) {
365 dec = String.format("%d", val);
366 hex = String.format("0x%016x", val);
367 } else if (val instanceof Short) {
368 dec = String.format("%d", val);
369 hex = String.format("0x%04x", val);
370 } else if (val instanceof String) {
371 str = "\"" + ((String) val) + "\"";
372 } else {
373 str = "";
374 }
375
376 if (dec != null && hex != null) {
377 data.setValue(base + ".constantValue.dec", Doclava.escape(dec));
378 data.setValue(base + ".constantValue.hex", Doclava.escape(hex));
379 } else {
380 data.setValue(base + ".constantValue.str", Doclava.escape(str));
381 data.setValue(base + ".constantValue.isString", "1");
382 }
383 }
Jeff Arneson4e67d822014-09-10 17:16:02 -0700384
385 AnnotationInstanceInfo.makeLinkListHDF(
386 data,
387 base + ".showAnnotations",
388 showAnnotations().toArray(new AnnotationInstanceInfo[showAnnotations().size()]));
389
Ben Dodson920dbbb2010-08-04 15:21:06 -0700390 setFederatedReferences(data, base);
391 }
392
393 @Override
394 public boolean isExecutable() {
395 return false;
396 }
397
398 public boolean isTransient() {
399 return mIsTransient;
400 }
401
402 public boolean isVolatile() {
403 return mIsVolatile;
404 }
405
406 // Check the declared value with a typed comparison, not a string comparison,
407 // to accommodate toolchains with different fp -> string conversions.
408 private boolean valueEquals(FieldInfo other) {
409 if ((mConstantValue == null) != (other.mConstantValue == null)) {
410 return false;
411 }
412
413 // Null values are considered equal
414 if (mConstantValue == null) {
415 return true;
416 }
Joe Onorato04099252011-03-09 13:34:18 -0800417
Ben Dodsonf22ba052010-08-12 11:47:06 -0700418 return mType.equals(other.mType)
419 && mConstantValue.equals(other.mConstantValue);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700420 }
421
422 public boolean isConsistent(FieldInfo fInfo) {
423 boolean consistent = true;
424 if (!mType.equals(fInfo.mType)) {
425 Errors.error(Errors.CHANGED_TYPE, fInfo.position(), "Field " + fInfo.qualifiedName()
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500426 + " has changed type from " + mType + " to " + fInfo.mType);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700427 consistent = false;
428 } else if (!this.valueEquals(fInfo)) {
429 Errors.error(Errors.CHANGED_VALUE, fInfo.position(), "Field " + fInfo.qualifiedName()
430 + " has changed value from " + mConstantValue + " to " + fInfo.mConstantValue);
431 consistent = false;
432 }
433
434 if (!scope().equals(fInfo.scope())) {
435 Errors.error(Errors.CHANGED_SCOPE, fInfo.position(), "Method " + fInfo.qualifiedName()
436 + " changed scope from " + this.scope() + " to " + fInfo.scope());
437 consistent = false;
438 }
439
440 if (mIsStatic != fInfo.mIsStatic) {
441 Errors.error(Errors.CHANGED_STATIC, fInfo.position(), "Field " + fInfo.qualifiedName()
442 + " has changed 'static' qualifier");
443 consistent = false;
444 }
445
Jeff Brown43d2ac82013-04-01 13:40:01 -0700446 if (!mIsFinal && fInfo.mIsFinal) {
447 Errors.error(Errors.ADDED_FINAL, fInfo.position(), "Field " + fInfo.qualifiedName()
448 + " has added 'final' qualifier");
449 consistent = false;
450 } else if (mIsFinal && !fInfo.mIsFinal) {
451 Errors.error(Errors.REMOVED_FINAL, fInfo.position(), "Field " + fInfo.qualifiedName()
452 + " has removed 'final' qualifier");
Ben Dodson920dbbb2010-08-04 15:21:06 -0700453 consistent = false;
454 }
455
456 if (mIsTransient != fInfo.mIsTransient) {
457 Errors.error(Errors.CHANGED_TRANSIENT, fInfo.position(), "Field " + fInfo.qualifiedName()
458 + " has changed 'transient' qualifier");
459 consistent = false;
460 }
461
462 if (mIsVolatile != fInfo.mIsVolatile) {
463 Errors.error(Errors.CHANGED_VOLATILE, fInfo.position(), "Field " + fInfo.qualifiedName()
464 + " has changed 'volatile' qualifier");
465 consistent = false;
466 }
467
468 if (isDeprecated() != fInfo.isDeprecated()) {
469 Errors.error(Errors.CHANGED_DEPRECATED, fInfo.position(), "Field " + fInfo.qualifiedName()
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500470 + " has changed deprecation state " + isDeprecated() + " --> " + fInfo.isDeprecated());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700471 consistent = false;
472 }
473
474 return consistent;
475 }
476
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700477 public boolean hasValue() {
478 return mHasValue;
479 }
480
481 public void setHasValue(boolean hasValue) {
482 mHasValue = hasValue;
483 }
484
Ben Dodson920dbbb2010-08-04 15:21:06 -0700485 boolean mIsTransient;
486 boolean mIsVolatile;
487 boolean mDeprecatedKnown;
488 boolean mIsDeprecated;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700489 boolean mHasValue;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700490 TypeInfo mType;
491 Object mConstantValue;
492}