/*
* Copyright 2014 Google Inc. All rights reserved.
*
* 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 org.inferred.freebuilder.processor.util;
import static com.google.common.base.Preconditions.checkArgument;
import static org.inferred.freebuilder.processor.util.AnnotationSource.addSource;
import org.inferred.freebuilder.processor.util.feature.Feature;
import org.inferred.freebuilder.processor.util.feature.FeatureSet;
import org.inferred.freebuilder.processor.util.feature.FeatureType;
import org.inferred.freebuilder.processor.util.feature.StaticFeatureSet;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
/**
* A {@link SourceBuilder} that writes to a {@link StringBuilder}.
*/
public final class SourceStringBuilder implements SourceBuilder {
private final TypeShortener shortener;
private final StringBuilder destination = new StringBuilder();
private final FeatureSet features;
/**
* Returns a {@link SourceStringBuilder} that always shortens types, even if that causes
* conflicts.
*/
public static SourceBuilder simple(Feature<?>... features) {
return new SourceStringBuilder(
new TypeShortener.AlwaysShorten(), new StaticFeatureSet(features));
}
/**
* Returns a {@link SourceStringBuilder} that returns compilable code.
*/
public static SourceBuilder compilable(FeatureSet features) {
return new SourceStringBuilder(
new TypeShortener.NeverShorten(), features);
}
SourceStringBuilder(TypeShortener shortener, FeatureSet features) {
this.shortener = shortener;
this.features = features;
}
@Override
public SourceBuilder add(Excerpt excerpt) {
excerpt.addTo(this);
return this;
}
@Override
public SourceBuilder add(String fmt, Object... args) {
Object[] substituteArgs = new Object[args.length];
for (int i = 0; i < args.length; i++) {
substituteArgs[i] = substitute(args[i]);
}
destination.append(String.format(fmt, substituteArgs));
return this;
}
@Override
public SourceBuilder addLine(String fmt, Object... args) {
return add(fmt + "\n", args);
}
@Override
public SourceStringBuilder subBuilder() {
return new SourceStringBuilder(shortener, features);
}
@Override
public <T extends Feature<T>> T feature(FeatureType<T> feature) {
return features.get(feature);
}
/** Returns the source code written so far. */
@Override
public String toString() {
return destination.toString();
}
private Object substitute(Object arg) {
if (arg instanceof Excerpt) {
SourceBuilder excerptBuilder = subBuilder();
((Excerpt) arg).addTo(excerptBuilder);
return excerptBuilder.toString();
} else if (arg instanceof Package) {
return ((Package) arg).getName();
} else if (arg instanceof Element) {
ElementKind kind = ((Element) arg).getKind();
if (kind == ElementKind.PACKAGE) {
return ((PackageElement) arg).getQualifiedName();
} else if (kind.isClass() || kind.isInterface()) {
return shortener.shorten(QualifiedName.of((TypeElement) arg));
} else {
return arg;
}
} else if (arg instanceof Class<?>) {
return shortener.shorten(QualifiedName.of((Class<?>) arg));
} else if ((arg instanceof TypeMirror) && (((TypeMirror) arg).getKind() == TypeKind.DECLARED)) {
DeclaredType mirror = (DeclaredType) arg;
checkArgument(isLegalType(mirror), "Cannot write unknown type %s", mirror);
return shortener.shorten(mirror);
} else if (arg instanceof QualifiedName) {
return shortener.shorten((QualifiedName) arg);
} else if (arg instanceof AnnotationMirror) {
SourceBuilder excerptBuilder = subBuilder();
addSource(excerptBuilder, (AnnotationMirror) arg);
return excerptBuilder.toString();
} else {
return arg;
}
}
private static boolean isLegalType(TypeMirror mirror) {
return !(new IsInvalidTypeVisitor().visit(mirror));
}
}