package xapi.dev.ui;
import com.github.javaparser.ASTHelper;
import com.github.javaparser.JavaParser;
import com.github.javaparser.ParseException;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.expr.UiAttrExpr;
import com.github.javaparser.ast.expr.UiContainerExpr;
import com.github.javaparser.ast.visitor.ModifierVisitorAdapter;
import xapi.collect.X_Collect;
import xapi.collect.api.IntTo;
import xapi.dev.ui.ContainerMetadata.MetadataRoot;
import xapi.fu.Do;
import xapi.log.X_Log;
import xapi.source.X_Source;
import xapi.util.X_Debug;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.tools.FileObject;
import javax.tools.JavaFileManager;
import javax.tools.StandardLocation;
import java.io.IOException;
import java.util.Objects;
import java.util.Optional;
/**
* Created by james on 6/17/16.
*/
public abstract class AbstractUiGeneratorService extends UiGeneratorTools implements UiGeneratorService {
protected String phase;
protected final IntTo<Do> onDone;
public AbstractUiGeneratorService() {
onDone = X_Collect.newList(Do.class);
}
@Override
public UiComponentGenerator getComponentGenerator(UiContainerExpr container, ContainerMetadata metadata) {
final UiComponentGenerator generator = super.getComponentGenerator(container, metadata);
if (ignoreMissingComponents()) {
warn(getClass(), "Ignoring missing component ", container);
} else {
Objects.requireNonNull(generator, "Null component for " + container.getName());
}
return generator;
}
protected boolean ignoreMissingFeatures() {
return true;
}
protected boolean ignoreMissingComponents() {
return true;
}
protected void warn(Object ... logMe) {
X_Log.warn(logMe);
}
@Override
public UiFeatureGenerator getFeatureGenerator(UiAttrExpr container, UiComponentGenerator componentGenerator) {
final UiFeatureGenerator generator = super.getFeatureGenerator(container, componentGenerator);
if (ignoreMissingFeatures()) {
warn(getClass(), "Ignoring missing feature ", container);
} else {
Objects.requireNonNull(generator, "Null feature for " + container.getName());
}
return generator;
}
@Override
public ContainerMetadata createMetadata(MetadataRoot root, UiContainerExpr n) {
final ContainerMetadata component = new ContainerMetadata(n);
component.setRoot(root == null ? createMetadataRoot() : root);
return component;
}
protected MetadataRoot createMetadataRoot() {
return new MetadataRoot();
}
@Override
public ComponentBuffer runPhase(String id, ComponentBuffer component) {
this.phase = id;
return component;
}
protected UiContainerExpr resolveImports(Elements elements, JavaFileManager filer, TypeElement element, UiContainerExpr container) {
return (UiContainerExpr) new ModifierVisitorAdapter<Object>(){
@Override
public Node visit(
UiContainerExpr n, Object arg
) {
if ("import".equals(n.getName())) {
final Optional<UiAttrExpr> file = n.getAttribute("file");
if (!file.isPresent()) {
throw new IllegalArgumentException("import tags must specify a file feature");
}
String loc = ASTHelper.extractAttrValue(file.get());
final FileObject resource;
String src = null;
try {
if (loc.indexOf('/') == -1) {
// This file is relative to our source file
final PackageElement pkg = elements.getPackageOf(element);
String pkgName;
if (pkg == null) {
pkgName = "";
} else {
pkgName = pkg.getQualifiedName().toString();
}
resource = findFile(filer, pkgName, loc);
} else {
// Treat the file as absolute classpath uri
resource = findFile(filer, "", loc);
}
src = resource.getCharContent(true).toString();
final UiContainerExpr newContainer = JavaParser.parseUiContainer(src);
return newContainer;
} catch (IOException | ParseException e) {
X_Log.error(getClass(), "Error trying to resolve import", n, "with source:\n",
X_Source.addLineNumbers(src), e);
}
}
return super.visit(n, arg);
}
}.visit(container, null);
}
protected FileObject findFile(JavaFileManager filer, String pkgName, String loc) {
try {
return filer.getFileForInput(
StandardLocation.SOURCE_PATH,
pkgName,
loc
);
} catch (IOException e) {
throw X_Debug.rethrow(e);
}
}
@Override
public UiGeneratorVisitor createVisitor(ContainerMetadata metadata) {
return new UiGeneratorVisitor(scope->{
scopes.add(scope);
return ()->{
UiVisitScope popped = scopes.pop();
assert popped == scope : "Scope stack inconsistent; " +
"expected " + popped + " to be the same reference as " + scope;
};
}, metadata);
}
@Override
public UiGeneratorService getGenerator() {
return this;
}
@Override
public void finish() {
onDone.removeAll(Do::done);
}
}