static final String VIRTUAL_FILE = "<ClosureRewriteClass.java>";
private JSDocInfo mergeJsDocFor(ClassDefinition cls, Node associatedNode) {
// avoid null checks
JSDocInfo classInfo = (cls.classInfo != null)
? cls.classInfo
: new JSDocInfo(true);
JSDocInfo ctorInfo = (cls.constructor.info != null)
? cls.constructor.info
: new JSDocInfo(true);
Node superNode = cls.superClass;
// Start with a clone of the constructor info if there is one.
JSDocInfoBuilder mergedInfo = cls.constructor.info != null
? JSDocInfoBuilder.copyFrom(ctorInfo)
: new JSDocInfoBuilder(true);
// merge block description
String blockDescription = Joiner.on("\n").skipNulls().join(
classInfo.getBlockDescription(),
ctorInfo.getBlockDescription());
if (!blockDescription.isEmpty()) {
mergedInfo.recordBlockDescription(blockDescription);
}
// merge suppressions
Set<String> suppressions = Sets.newHashSet();
suppressions.addAll(classInfo.getSuppressions());
suppressions.addAll(ctorInfo.getSuppressions());
if (!suppressions.isEmpty()) {
mergedInfo.recordSuppressions(suppressions);
}
// Use class deprecation if set.
if (classInfo.isDeprecated()) {
mergedInfo.recordDeprecated();
}
String deprecationReason = null;
if (classInfo.getDeprecationReason() != null) {
deprecationReason = classInfo.getDeprecationReason();
mergedInfo.recordDeprecationReason(deprecationReason);
}
// Use class visibility if specifically set
Visibility visibility = classInfo.getVisibility();
if (visibility != null && visibility != JSDocInfo.Visibility.INHERITED) {
mergedInfo.recordVisibility(classInfo.getVisibility());
}
if (classInfo.isConstant()) {
mergedInfo.recordConstancy();
}
if (classInfo.isExport()) {
mergedInfo.recordExport();
}
// If @ngInject is on the ctor, it's already been copied above.
if (classInfo.isNgInject()) {
compiler.report(JSError.make(associatedNode, GOOG_CLASS_NG_INJECT_ON_CLASS));
mergedInfo.recordNgInject(true);
}
// @constructor is implied, @interface must be explicit
boolean isInterface = classInfo.isInterface() || ctorInfo.isInterface();
if (isInterface) {
mergedInfo.recordInterface();
List<JSTypeExpression> extendedInterfaces = null;
if (classInfo.getExtendedInterfacesCount() > 0) {
extendedInterfaces = classInfo.getExtendedInterfaces();
} else if (ctorInfo.getExtendedInterfacesCount() == 0
&& superNode != null) {
extendedInterfaces = ImmutableList.of(new JSTypeExpression(
new Node(Token.BANG,
IR.string(superNode.getQualifiedName())),
VIRTUAL_FILE));
}
if (extendedInterfaces != null) {
for (JSTypeExpression extend : extendedInterfaces) {
mergedInfo.recordExtendedInterface(extend);
}
}
} else {
// @constructor by default
mergedInfo.recordConstructor();
if (classInfo.makesUnrestricted() || ctorInfo.makesUnrestricted()) {
mergedInfo.recordUnrestricted();
} else if (classInfo.makesDicts() || ctorInfo.makesDicts()) {
mergedInfo.recordDict();
} else {
// @struct by default
mergedInfo.recordStruct();
}
if (classInfo.getBaseType() != null) {
mergedInfo.recordBaseType(classInfo.getBaseType());
} else if (superNode != null) {
// a "super" implies @extends, build a default.
JSTypeExpression baseType = new JSTypeExpression(
new Node(Token.BANG,
IR.string(superNode.getQualifiedName())),
VIRTUAL_FILE);
mergedInfo.recordBaseType(baseType);
}
// @implements from the class if they exist
List<JSTypeExpression> interfaces = classInfo.getImplementedInterfaces();
for (JSTypeExpression implemented : interfaces) {
mergedInfo.recordImplementedInterface(implemented);
}
}
// merge @template types if they exist
List<String> templateNames = new ArrayList<>();
templateNames.addAll(classInfo.getTemplateTypeNames());
templateNames.addAll(ctorInfo.getTemplateTypeNames());
for (String typeName : templateNames) {
mergedInfo.recordTemplateTypeName(typeName);
}
return mergedInfo.build(associatedNode);
}