/**
* Copyright 2011-2017 Asakusa Framework Team.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.asakusafw.utils.java.parser.javadoc;
import java.util.ArrayList;
import java.util.List;
import com.asakusafw.utils.java.internal.parser.javadoc.ir.IrBasicTypeKind;
import com.asakusafw.utils.java.internal.parser.javadoc.ir.IrDocArrayType;
import com.asakusafw.utils.java.internal.parser.javadoc.ir.IrDocBasicType;
import com.asakusafw.utils.java.internal.parser.javadoc.ir.IrDocBlock;
import com.asakusafw.utils.java.internal.parser.javadoc.ir.IrDocComment;
import com.asakusafw.utils.java.internal.parser.javadoc.ir.IrDocElementVisitor;
import com.asakusafw.utils.java.internal.parser.javadoc.ir.IrDocField;
import com.asakusafw.utils.java.internal.parser.javadoc.ir.IrDocFragment;
import com.asakusafw.utils.java.internal.parser.javadoc.ir.IrDocMethod;
import com.asakusafw.utils.java.internal.parser.javadoc.ir.IrDocMethodParameter;
import com.asakusafw.utils.java.internal.parser.javadoc.ir.IrDocNamedType;
import com.asakusafw.utils.java.internal.parser.javadoc.ir.IrDocQualifiedName;
import com.asakusafw.utils.java.internal.parser.javadoc.ir.IrDocSimpleName;
import com.asakusafw.utils.java.internal.parser.javadoc.ir.IrDocText;
import com.asakusafw.utils.java.model.syntax.BasicTypeKind;
import com.asakusafw.utils.java.model.syntax.DocBlock;
import com.asakusafw.utils.java.model.syntax.DocElement;
import com.asakusafw.utils.java.model.syntax.DocMethodParameter;
import com.asakusafw.utils.java.model.syntax.Javadoc;
import com.asakusafw.utils.java.model.syntax.ModelFactory;
import com.asakusafw.utils.java.model.syntax.Name;
import com.asakusafw.utils.java.model.syntax.NamedType;
import com.asakusafw.utils.java.model.syntax.SimpleName;
import com.asakusafw.utils.java.model.syntax.Type;
/**
* Converts {@link IrDocComment} models into Java DOM models.
*/
public class JavadocConverter {
private final ModelFactory factory;
private final JavadocParser parser;
/**
* Creates a new instance.
* @param factory the Java DOM factory
* @throws IllegalArgumentException if the parameter is {@code null}
*/
public JavadocConverter(ModelFactory factory) {
this.factory = factory;
JavadocParserBuilder builder = new JavadocParserBuilder();
builder.addSpecialStandAloneBlockParser(new FollowsNamedTypeBlockParser(
"exception", //$NON-NLS-1$
"throws" //$NON-NLS-1$
));
builder.addSpecialStandAloneBlockParser(new FollowsReferenceBlockParser(
"see" //$NON-NLS-1$
));
builder.addSpecialStandAloneBlockParser(new ParamBlockParser(
"param" //$NON-NLS-1$
));
builder.addSpecialStandAloneBlockParser(new SerialFieldBlockParser(
"serialField" //$NON-NLS-1$
));
builder.addSpecialInlineBlockParser(new FollowsReferenceBlockParser(
"link", //$NON-NLS-1$
"linkplain" //$NON-NLS-1$
));
this.parser = builder.build();
}
/**
* Analyzes the documentation comment string and returns the corresponded {@link Javadoc} object.
* @param content the documentation content
* @param offset the starting character offset of the comment string in the compilation unit
* @return the analyzed object
* @throws JavadocParseException if the documentation comment is malformed
* @throws IllegalArgumentException if the parameter is {@code null}
*/
public Javadoc convert(String content, int offset) throws JavadocParseException {
if (content == null) {
throw new IllegalArgumentException("content must not be null"); //$NON-NLS-1$
}
JavadocScanner scanner = DefaultJavadocScanner.newInstance(content);
IrDocComment ir = parser.parse(scanner);
return convert(ir, offset);
}
private Javadoc convert(IrDocComment comment, int offset) {
assert comment != null;
Mapper mapper = new Mapper(factory, offset);
List<DocBlock> blocks = new ArrayList<>();
for (IrDocBlock block : comment.getBlocks()) {
blocks.add((DocBlock) block.accept(mapper, null));
}
return factory.newJavadoc(blocks);
}
private static class Mapper
extends IrDocElementVisitor<DocElement, Void> {
final ModelFactory factory;
private final TypeMapper types;
Mapper(ModelFactory factory, int offset) {
assert factory != null;
this.factory = factory;
this.types = new TypeMapper();
}
@Override
public DocElement visitBlock(IrDocBlock elem, Void context) {
String tag = elem.getTag();
List<DocElement> elements = new ArrayList<>();
for (IrDocFragment f : elem.getFragments()) {
elements.add(f.accept(this, null));
}
return factory.newDocBlock(
tag == null ? "" : tag, //$NON-NLS-1$
elements);
}
@Override
public DocElement visitText(IrDocText elem, Void context) {
return factory.newDocText(elem.getContent());
}
@Override
public DocElement visitSimpleName(IrDocSimpleName elem, Void context) {
return factory.newSimpleName(elem.getIdentifier());
}
@Override
public DocElement visitQualifiedName(IrDocQualifiedName elem, Void context) {
Name qualifier = (Name) elem.getQualifier().accept(this, null);
SimpleName simple = (SimpleName) elem.getName().accept(this, null);
return factory.newQualifiedName(qualifier, simple);
}
@Override
public DocElement visitField(IrDocField elem, Void context) {
Type type = declaring(elem.getDeclaringType());
SimpleName name = (SimpleName) elem.getName().accept(this, null);
return factory.newDocField(type, name);
}
@Override
public DocElement visitMethod(IrDocMethod elem, Void context) {
Type type = declaring(elem.getDeclaringType());
SimpleName name = (SimpleName) elem.getName().accept(this, null);
List<DocMethodParameter> params = new ArrayList<>();
for (IrDocMethodParameter p : elem.getParameters()) {
params.add(convert(p));
}
return factory.newDocMethod(type, name, params);
}
private DocMethodParameter convert(IrDocMethodParameter elem) {
Type type = elem.getType().accept(types, this);
SimpleName name;
if (elem.getName() != null) {
name = (SimpleName) elem.getName().accept(this, null);
} else {
name = null;
}
return factory.newDocMethodParameter(
type,
name,
elem.isVariableArity());
}
@Override
public DocElement visitNamedType(IrDocNamedType elem, Void context) {
Name name = (Name) elem.getName().accept(this, null);
return factory.newNamedType(name);
}
private NamedType declaring(IrDocNamedType declaringType) {
if (declaringType == null) {
return null;
}
return (NamedType) visitNamedType(declaringType, null);
}
}
private static class TypeMapper extends IrDocElementVisitor<Type, Mapper> {
TypeMapper() {
return;
}
@Override
public Type visitArrayType(IrDocArrayType elem, Mapper context) {
Type component = elem.getComponentType().accept(this, context);
return context.factory.newArrayType(component);
}
@Override
public Type visitBasicType(IrDocBasicType elem, Mapper context) {
BasicTypeKind kind = convert(elem.getTypeKind());
return context.factory.newBasicType(kind);
}
@Override
public Type visitNamedType(IrDocNamedType elem, Mapper context) {
Name name = (Name) elem.getName().accept(context, null);
return context.factory.newNamedType(name);
}
private static BasicTypeKind convert(IrBasicTypeKind kind) {
switch (kind) {
case BOOLEAN:
return BasicTypeKind.BOOLEAN;
case BYTE:
return BasicTypeKind.BYTE;
case CHAR:
return BasicTypeKind.CHAR;
case DOUBLE:
return BasicTypeKind.DOUBLE;
case FLOAT:
return BasicTypeKind.FLOAT;
case INT:
return BasicTypeKind.INT;
case LONG:
return BasicTypeKind.LONG;
case SHORT:
return BasicTypeKind.SHORT;
case VOID:
return BasicTypeKind.VOID;
default:
throw new AssertionError(kind);
}
}
}
}