package org.sugarj;
import static org.sugarj.common.ATermCommands.getApplicationSubterm;
import static org.sugarj.common.ATermCommands.isApplication;
import static org.sugarj.common.Log.log;
import java.io.IOException;
import java.io.Serializable;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.spoofax.interpreter.terms.IStrategoTerm;
import org.spoofax.terms.Term;
import org.strategoxt.HybridInterpreter;
import org.sugarj.common.ATermCommands;
import org.sugarj.common.Environment;
import org.sugarj.common.FileCommands;
import org.sugarj.common.JavaCommands;
import org.sugarj.common.Log;
import org.sugarj.common.StringCommands;
import org.sugarj.common.errors.SourceCodeException;
import org.sugarj.common.path.Path;
import org.sugarj.common.path.RelativePath;
public class JavaProcessor extends AbstractBaseProcessor implements Serializable {
private static final long serialVersionUID = 1817193221140795776L;
private String moduleHeader;
private List<String> imports = new LinkedList<String>();
private List<String> body = new LinkedList<String>();
private Environment environment;
private RelativePath sourceFile;
private Path javaOutFile;
private String relPackageName;
private Path sourcePath;
private void checkPackageName(IStrategoTerm toplevelDecl, RelativePath sourceFile) {
if (sourceFile != null) {
String packageName = relPackageName == null ? "" : relPackageName.replace('/', '.');
String rel = FileCommands.dropExtension(sourceFile.getRelativePath());
int i = rel.lastIndexOf('/');
String expectedPackage = i >= 0 ? rel.substring(0, i) : rel;
expectedPackage = expectedPackage.replace('/', '.');
if (!packageName.equals(expectedPackage))
throw new RuntimeException("The declared package '" + packageName + "'" + " does not match the expected package '" + expectedPackage + "'.");
}
}
public String extractImportedModuleName(IStrategoTerm toplevelDecl) {
String name = null;
if (isApplication(toplevelDecl, "TypeImportDec"))
name = prettyPrint(toplevelDecl.getSubterm(0));
else if (isApplication(toplevelDecl, "TypeImportOnDemandDec"))
name = prettyPrint(toplevelDecl.getSubterm(0)) + ".*";
else if (isApplication(toplevelDecl, "TypeImportAsDec"))
name = prettyPrint(toplevelDecl.getSubterm(1));
else if (isApplication(toplevelDecl, "TransImportDec"))
name = getTransformedModulePath(toplevelDecl.getSubterm(1));
return name;
}
public String extractNamespaceName(IStrategoTerm toplevelDecl, HybridInterpreter interp) throws IOException {
String packageName = prettyPrint(getApplicationSubterm(toplevelDecl, "PackageDec", 1));
return packageName;
}
@Override
public Path getGeneratedSourceFile() {
return javaOutFile;
}
@Override
public String getNamespace() {
return relPackageName;
}
@Override
public String getGeneratedSource() {
if (body.isEmpty())
return "";
return moduleHeader + "\n"
+ StringCommands.printListSeparated(imports, "\n") + "\n"
+ StringCommands.printListSeparated(body, "\n");
}
@Override
public List<String> processBaseDecl(IStrategoTerm toplevelDecl) throws IOException {
if (getLanguage().isNamespaceDec(toplevelDecl)) {
processNamespaceDecl(toplevelDecl);
return Collections.emptyList();
}
IStrategoTerm dec = isApplication(toplevelDecl, "JavaTypeDec") ? getApplicationSubterm(toplevelDecl, "JavaTypeDec", 0) : toplevelDecl;
String decName = Term.asJavaString(dec.getSubterm(0).getSubterm(1).getSubterm(0));
String expectedDecName = FileCommands.fileName(javaOutFile);
if (expectedDecName != null && !expectedDecName.equals(decName))
throw new RuntimeException("Declaration name '" + decName + "'" + " does not match the file name '" + expectedDecName + "'.");
body.add(prettyPrint(dec));
// TODO return list of qualified types that occur in the decl, e.g., java.util.String
return Collections.emptyList();
}
private void processNamespaceDecl(IStrategoTerm toplevelDecl) throws IOException {
String packageName = extractNamespaceName(toplevelDecl, interp);
relPackageName = getRelativeModulePath(packageName);
log.log("The SDF / Stratego package name is '" + relPackageName + "'.", Log.DETAIL);
checkPackageName(toplevelDecl, sourceFile);
if (javaOutFile == null)
javaOutFile = environment.createOutPath(getRelativeNamespaceSep() + FileCommands.fileName(sourceFile) + "." + JavaLanguage.getInstance().getBaseFileExtension()); // XXX:
// moved here before depOutFile==null check
moduleHeader = prettyPrint(toplevelDecl);
}
public void setJavaOutFile(Path javaOutFile) {
this.javaOutFile = javaOutFile;
}
@Override
public void init(RelativePath sourceFile, Environment environment) {
this.environment = environment;
this.sourceFile = sourceFile;
javaOutFile = environment.createOutPath(FileCommands.dropExtension(sourceFile.getRelativePath()) + "." + JavaLanguage.getInstance().getBaseFileExtension());
for (Path dir : environment.getSourcePath())
if (sourceFile.getBasePath().equals(dir))
sourcePath = dir;
}
@Override
public JavaLanguage getLanguage() {
return JavaLanguage.getInstance();
}
@Override
public List<Path> compile(List<Path> javaOutFiles, Path bin, List<Path> path) throws IOException, SourceCodeException {
return JavaCommands.javac(javaOutFiles, sourcePath, bin, path);
}
@Override
public String getModulePathOfImport(IStrategoTerm toplevelDecl) {
String importModule = extractImportedModuleName(toplevelDecl);
String modulePath = getRelativeModulePath(importModule);
return modulePath;
}
@Override
public String getImportLocalName(IStrategoTerm decl) {
IStrategoTerm opt = null;
if (isApplication(decl, "TransImportDec"))
opt = getApplicationSubterm(decl, "TransImportDec", 0);
else if (isApplication(decl, "TypeImportAsDec"))
opt = getApplicationSubterm(decl, "TypeImportAsDec", 0);
if (opt != null && isApplication(opt, "Some"))
return getModulePath(getApplicationSubterm(getApplicationSubterm(opt, "Some", 0), "ImportAs", 0));
return null;
}
@Override
public IStrategoTerm reconstructImport(String modulePath, IStrategoTerm decl) {
IStrategoTerm localName = null;
if (isApplication(decl, "TransImportDec"))
localName = getApplicationSubterm(decl, "TransImportDec", 0);
else if (isApplication(decl, "TypeImportAsDec"))
localName = getApplicationSubterm(decl, "TypeImportAsDec", 0);
if (localName == null || isApplication(localName, "None"))
return
ATermCommands.makeAppl("TypeImportDec", "TypeImportDec", 1,
ATermCommands.makeAppl("Id", "Id", 1, ATermCommands.makeString(modulePath)));
return
ATermCommands.makeAppl("TypeImportAsDec", "TypeImportAsDec", 2,
localName,
ATermCommands.makeAppl("Id", "Id", 1, ATermCommands.makeString(modulePath)));
}
@Override
public String getModulePath(IStrategoTerm decl) {
return getRelativeModulePath(prettyPrint(decl));
}
private String getRelativeModulePath(String module) {
if (module == null)
return null;
return module.replace(".", Environment.sep);
}
@Override
public void processModuleImport(IStrategoTerm toplevelDecl) throws IOException {
imports.add(prettyPrint(toplevelDecl));
}
@Override
public String getExtensionName(IStrategoTerm decl) throws IOException {
IStrategoTerm head = getApplicationSubterm(decl, "ExtensionDec", 0);
String extName = prettyPrint(getApplicationSubterm(head, "ExtensionDecHead", 1));
return extName;
}
private String prettyPrint(IStrategoTerm term) {
return getLanguage().prettyPrint(term);
}
@Override
public boolean isModuleExternallyResolvable(String relModulePath) {
if (relModulePath.endsWith("*"))
return true;
try {
return getClass().getClassLoader().loadClass(relModulePath.replace('/', '.')) != null;
} catch (ClassNotFoundException e) {
return false;
}
}
@Override
public IStrategoTerm getExtensionBody(IStrategoTerm decl) {
IStrategoTerm body = getApplicationSubterm(decl, "ExtensionDec", 1);
IStrategoTerm sugarBody = getApplicationSubterm(body, "ExtensionBody", 0);
return sugarBody;
}
}