package jetbrains.mps.ide.java.newparser;
/*Generated by MPS */
import org.apache.log4j.Logger;
import org.apache.log4j.LogManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.model.SNode;
import org.eclipse.jdt.internal.core.util.CodeSnippetParsingUtil;
import java.util.Map;
import java.util.HashMap;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import java.util.List;
import java.util.ArrayList;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SConceptOperations;
import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SLinkOperations;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations;
import org.eclipse.jdt.internal.core.util.RecordedParsingInformation;
import jetbrains.mps.baseLanguage.closures.runtime.Wrappers;
import jetbrains.mps.internal.collections.runtime.MapSequence;
import jetbrains.mps.internal.collections.runtime.Sequence;
import java.util.Comparator;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SPropertyOperations;
import jetbrains.mps.internal.collections.runtime.IWhereFilter;
import org.eclipse.jdt.internal.compiler.ast.ImportReference;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.util.ProgressMonitor;
import jetbrains.mps.progress.EmptyProgressMonitor;
import org.jetbrains.mps.openapi.language.SAbstractConcept;
import jetbrains.mps.baseLanguage.closures.runtime._FunctionTypes;
import jetbrains.mps.smodel.behaviour.BHReflection;
import jetbrains.mps.core.aspects.behaviour.SMethodTrimmedId;
import jetbrains.mps.internal.collections.runtime.IVisitor;
import jetbrains.mps.internal.collections.runtime.IMapping;
import org.jetbrains.mps.openapi.model.SModel;
import jetbrains.mps.smodel.StaticReference;
import org.jetbrains.mps.openapi.model.SModelReference;
import jetbrains.mps.smodel.SModelInternal;
import java.util.Deque;
import jetbrains.mps.internal.collections.runtime.DequeSequence;
import java.util.LinkedList;
import org.jetbrains.mps.openapi.model.SReference;
import jetbrains.mps.smodel.DynamicReference;
public class JavaParser {
private static Logger LOG = LogManager.getLogger(JavaParser.class);
public JavaParser() {
}
@NotNull
public JavaParser.JavaParseResult parseCompilationUnit(String code) throws JavaParseException {
return parse(code, FeatureKind.CLASS, null, true);
}
@NotNull
public JavaParser.JavaParseResult parse(String code, FeatureKind what, SNode context, boolean recovery) throws JavaParseException {
// in eclipse there is full recovery and statement recovery
// TODO use full recovery
boolean stubsMode = FeatureKind.CLASS_STUB.equals(what);
CodeSnippetParsingUtil util = new CodeSnippetParsingUtil(stubsMode);
Map<String, String> settings = new HashMap<String, String>();
settings.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_1_8);
settings.put(CompilerOptions.OPTION_DocCommentSupport, "enabled");
ASTConverter converter = (FeatureKind.CLASS_STUB.equals(what) ? new ASTConverterWithExpressions(stubsMode) : new FullASTConverter(null));
List<SNode> resultNodes = new ArrayList<SNode>();
String resultPackageName = null;
char[] source = code.toCharArray();
switch (what) {
case CLASS:
// fall-through
case CLASS_STUB:
CompilationUnitDeclaration compRes = util.parseCompilationUnit(source, settings, true);
if (compRes == null) {
return JavaParser.JavaParseResult.UNKNOWN_ERROR;
}
ASTNode[] astTypes = compRes.types;
if (astTypes != null && astTypes.length > 0) {
List<SNode> roots = new ArrayList<SNode>();
for (ASTNode astNode : astTypes) {
SNode root = converter.convertRoot(astNode);
annotateWithmports(compRes, root);
ListSequence.fromList(roots).addElement(root);
}
resultNodes = roots;
}
attachComments(source, converter, util.recordedParsingInformation);
// there may be no types and still no compilation errors
// e.g. package-info.java only includes 'package pkg';
// getting package out of the parsed source code
if (compRes.currentPackage != null) {
StringBuffer sb = new StringBuffer();
compRes.currentPackage.print(0, sb, false);
resultPackageName = sb.toString();
}
break;
case CLASS_CONTENT:
ASTNode[] astNodes = util.parseClassBodyDeclarations(source, 0, source.length, settings, true, recovery);
// type decl (inner), field, method
if (astNodes != null && astNodes.length > 0) {
resultNodes = converter.convertClassContents(astNodes, context);
}
attachComments(source, converter, util.recordedParsingInformation);
break;
case STATEMENTS:
AbstractMethodDeclaration absMethod = util.parseStatements(source, settings, true, recovery);
if (absMethod == null) {
return JavaParser.JavaParseResult.UNKNOWN_ERROR;
}
Statement[] stmts = absMethod.statements;
if (stmts != null && stmts.length > 0) {
// TODO construct typeResolver from parent node context
SNode stmtList = SConceptOperations.createNewNode(MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8cc56b200L, "jetbrains.mps.baseLanguage.structure.StatementList"));
((FullASTConverter) converter).convertStatementsInto(absMethod, stmtList);
attachComments(source, converter, util.recordedParsingInformation);
resultNodes = ListSequence.fromList(new ArrayList<SNode>());
// stmtList may have new statements (comments) by now, after attachComments
for (SNode stmt : ListSequence.fromList(SLinkOperations.getChildren(stmtList, MetaAdapterFactory.getContainmentLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8cc56b200L, 0xf8cc6bf961L, "statement")))) {
SNodeOperations.deleteNode(stmt);
ListSequence.fromList(resultNodes).addElement(stmt);
}
}
break;
default:
throw new IllegalArgumentException("Parsing other than class and statements is not supported yet ");
}
return new JavaParser.JavaParseResult(resultNodes, resultPackageName, problemDescription(util.recordedParsingInformation));
}
public void attachComments(char[] source, ASTConverter converter, RecordedParsingInformation parseInfo) {
char[] content = source;
int[][] comments = parseInfo.commentPositions;
int[] lineends = parseInfo.lineEnds;
final Wrappers._T<Map<SNode, Integer>> positions = new Wrappers._T<Map<SNode, Integer>>(MapSequence.fromMap(new HashMap<SNode, Integer>()));
Iterable<FullASTConverter.CodeBlock> blocks = ListSequence.fromList(new ArrayList<FullASTConverter.CodeBlock>());
Map<Integer, SNode> javadocs = converter.getJavadocs();
if (converter instanceof FullASTConverter) {
blocks = ((FullASTConverter) converter).getCodeBlocks();
positions.value = ((FullASTConverter) converter).getPositions();
}
Iterable<FullASTConverter.CodeBlock> blcks = Sequence.fromIterable(blocks).sort(new Comparator<FullASTConverter.CodeBlock>() {
public int compare(FullASTConverter.CodeBlock a, FullASTConverter.CodeBlock b) {
return a.getEndPos() - b.getEndPos();
}
}, true);
for (int[] comment : comments) {
if (comment[1] > 0) {
// javadoc
SNode doc = MapSequence.fromMap(javadocs).get(comment[0]);
if (doc == null) {
continue;
}
List<String> lines = CommentHelper.processJavadoc(CommentHelper.splitString(content, lineends, comment[0], comment[1]));
for (String text : lines) {
SNode commentLine = SConceptOperations.createNewNode(MetaAdapterFactory.getConcept(0xf280165065d5424eL, 0xbb1b463a8781b786L, 0x757ba20a4c87f96cL, "jetbrains.mps.baseLanguage.javadoc.structure.CommentLine"));
SNode part = SConceptOperations.createNewNode(MetaAdapterFactory.getConcept(0xf280165065d5424eL, 0xbb1b463a8781b786L, 0x7c7f5b2f31990287L, "jetbrains.mps.baseLanguage.javadoc.structure.TextCommentLinePart"));
SPropertyOperations.set(part, MetaAdapterFactory.getProperty(0xf280165065d5424eL, 0xbb1b463a8781b786L, 0x7c7f5b2f31990287L, 0x7c7f5b2f31990288L, "text"), text);
ListSequence.fromList(SLinkOperations.getChildren(commentLine, MetaAdapterFactory.getContainmentLink(0xf280165065d5424eL, 0xbb1b463a8781b786L, 0x757ba20a4c87f96cL, 0x7c7f5b2f3199028dL, "part"))).addElement(part);
ListSequence.fromList(SLinkOperations.getChildren(doc, MetaAdapterFactory.getContainmentLink(0xf280165065d5424eL, 0xbb1b463a8781b786L, 0x4a3c146b7fae70d3L, 0x757ba20a4c87f96eL, "body"))).addElement(commentLine);
}
continue;
}
final int linestart = Math.abs(comment[0]);
// find appropriate block
SNode block = null;
for (FullASTConverter.CodeBlock blk : Sequence.fromIterable(blcks)) {
if (blk.getStartPos() <= linestart && linestart <= blk.getEndPos()) {
block = blk.getStatementList();
break;
}
}
if ((block != null)) {
int pos = ListSequence.fromList(SLinkOperations.getChildren(block, MetaAdapterFactory.getContainmentLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8cc56b200L, 0xf8cc6bf961L, "statement"))).where(new IWhereFilter<SNode>() {
public boolean accept(SNode it) {
return !(MapSequence.fromMap(positions.value).containsKey(it)) || Math.abs(MapSequence.fromMap(positions.value).get(it)) <= linestart;
}
}).count();
for (String line : CommentHelper.processComment(CommentHelper.splitString(content, lineends, linestart, Math.abs(comment[1])))) {
String line_ = line;
if (line.startsWith(" ")) {
line_ = line.substring(1);
}
SNode commentText = SConceptOperations.createNewNode(MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x57d533a7af15ed3dL, "jetbrains.mps.baseLanguage.structure.TextCommentPart"));
SPropertyOperations.set(commentText, MetaAdapterFactory.getProperty(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x57d533a7af15ed3dL, 0x57d533a7af15ed3eL, "text"), line_);
SNode commentLine = SConceptOperations.createNewNode(MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x57d533a7af15ed3aL, "jetbrains.mps.baseLanguage.structure.SingleLineComment"));
ListSequence.fromList(SLinkOperations.getChildren(commentLine, MetaAdapterFactory.getContainmentLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x57d533a7af15ed3aL, 0x57d533a7af16ff73L, "commentPart"))).addElement(commentText);
ListSequence.fromList(SLinkOperations.getChildren(block, MetaAdapterFactory.getContainmentLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8cc56b200L, 0xf8cc6bf961L, "statement"))).insertElement(pos++, commentLine);
}
} else {
// no place to insert comment
// this is most likely because the comment is not a javadoc and is between declarations
// (not inside statement list)
// we could issue a warning...
}
}
}
public void annotateWithmports(CompilationUnitDeclaration compResult, SNode clas) {
SNode imports = SConceptOperations.createNewNode(MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x53f7c33f069862f2L, "jetbrains.mps.baseLanguage.structure.JavaImports"));
// putting first: current package in terms of source code
if (compResult.currentPackage != null) {
SNode currPkg = makeImport(compResult.currentPackage);
SPropertyOperations.set(currPkg, MetaAdapterFactory.getProperty(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x64c0181e603bcfL, 0x64c0181e603bd0L, "onDemand"), "" + (true));
ListSequence.fromList(SLinkOperations.getChildren(imports, MetaAdapterFactory.getContainmentLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x53f7c33f069862f2L, 0x64c0181e6020a7L, "entries"))).addElement(currPkg);
}
if (compResult.imports != null) {
for (ImportReference imprt : compResult.imports) {
ListSequence.fromList(SLinkOperations.getChildren(imports, MetaAdapterFactory.getContainmentLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x53f7c33f069862f2L, 0x64c0181e6020a7L, "entries"))).addElement(makeImport(imprt));
}
}
// inserting it in the beginning
clas.addChild(MetaAdapterFactory.getContainmentLink(0xceab519525ea4f22L, 0x9b92103b95ca8c0cL, 0x10802efe25aL, 0x47bf8397520e5942L, "smodelAttribute"), imports);
// we want to insert imports section before any javadoc
// because javadoc is data while imports section is meta-data for assisting class resolving
}
private SNode makeImport(ImportReference impRef) {
SNode imp = SConceptOperations.createNewNode(MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x64c0181e603bcfL, "jetbrains.mps.baseLanguage.structure.JavaImport"));
boolean onDemand = (impRef.bits & ASTNode.OnDemand) != 0;
boolean isStatic = impRef.isStatic();
SPropertyOperations.set(imp, MetaAdapterFactory.getProperty(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x64c0181e603bcfL, 0x64c0181e603bd0L, "onDemand"), "" + (onDemand));
SPropertyOperations.set(imp, MetaAdapterFactory.getProperty(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x64c0181e603bcfL, 0x4d5c30eb30af1572L, "static"), "" + (isStatic));
char[][] toks = impRef.getImportName();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < toks.length; i++) {
sb.append(toks[i]);
sb.append('.');
}
sb.deleteCharAt(sb.length() - 1);
SPropertyOperations.set(imp, MetaAdapterFactory.getProperty(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x5a98df4004080866L, 0x1996ec29712bdd92L, "tokens"), sb.toString());
return imp;
}
private String problemDescription(RecordedParsingInformation info) {
if (info == null) {
return null;
}
if (info.problems != null && info.problems.length > 0) {
return "There were some problems";
} else {
return null;
}
}
@Nullable
public static String peekPackage(String source) {
// WILL GO AWAY COMPLETELY
final String str = "package ";
StringBuilder packageName = new StringBuilder();
for (int i = source.indexOf(str) + str.length(); i < source.length(); i++) {
char c = source.charAt(i);
if (Character.isWhitespace(c) || c == ';') {
break;
}
packageName.append(c);
}
return (packageName.length() == 0 ? null : packageName.toString());
}
public static class JavaParseResult {
public static final JavaParser.JavaParseResult UNKNOWN_ERROR = new JavaParser.JavaParseResult(new ArrayList<SNode>(), "Parse failed and return no errors");
@NotNull
private List<SNode> nodes;
private String pakage;
private String errorMsg;
public JavaParseResult(List<SNode> ns, String error) {
nodes = ns;
errorMsg = error;
}
public JavaParseResult(List<SNode> ns, String pkg, String error) {
this(ns, error);
pakage = pkg;
}
@NotNull
public List<SNode> getNodes() {
return nodes;
}
public String getPackage() {
return pakage;
}
public String getErrorMsg() {
return errorMsg;
}
}
/**
* Must be called from a context where 1) nodes are attached to a model 2) model modification is allowed.
* E.g. either inside a command or during smodel.SModel.isUpdateMode() == true
*/
public static void tryResolveUnknowns(Iterable<SNode> roots, ProgressMonitor progress) {
tryResolveUnknowns(roots, progress, IncrementalModelAccess.INSIDE_COMMAND_OR_UPDATE_MODE);
}
/*package*/ static void tryResolveUnknowns(Iterable<SNode> roots, ProgressMonitor progress, IncrementalModelAccess modelAccess) {
// make room for this many passes, and just don't advance the progress afterwards
final int PASSES_TO_SHOW_UNDER_PROGRESS = 5;
Iterable<SNode> nextPass = roots;
int k = 0;
while (Sequence.fromIterable(nextPass).isNotEmpty()) {
ProgressMonitor progressForThisPass;
if (k++ < PASSES_TO_SHOW_UNDER_PROGRESS) {
progressForThisPass = progress.subTask(Sequence.fromIterable(nextPass).count());
} else {
progressForThisPass = new EmptyProgressMonitor();
}
nextPass = tryResolveUnknowns0(nextPass, progressForThisPass, modelAccess);
}
}
private static Iterable<SNode> tryResolveUnknowns0(final Iterable<SNode> roots, final ProgressMonitor progress, IncrementalModelAccess modelAccess) {
progress.start("Ambiguous concepts...", Sequence.fromIterable(roots).count());
final Map<SNode, SNode> resolutionMap = MapSequence.fromMap(new HashMap<SNode, SNode>());
modelAccess.accessModel(new Runnable() {
public void run() {
for (SNode node : Sequence.fromIterable(roots)) {
progress.step((SNodeOperations.isInstanceOf(node, MetaAdapterFactory.getInterfaceConcept(0xceab519525ea4f22L, 0x9b92103b95ca8c0cL, 0x110396eaaa4L, "jetbrains.mps.lang.core.structure.INamedConcept")) ? ("node: " + SPropertyOperations.getString(SNodeOperations.cast(node, MetaAdapterFactory.getInterfaceConcept(0xceab519525ea4f22L, 0x9b92103b95ca8c0cL, 0x110396eaaa4L, "jetbrains.mps.lang.core.structure.INamedConcept")), MetaAdapterFactory.getProperty(0xceab519525ea4f22L, 0x9b92103b95ca8c0cL, 0x110396eaaa4L, 0x110396ec041L, "name"))) : ""));
List<SNode> unknowns = SNodeOperations.getNodeDescendants(node, MetaAdapterFactory.getInterfaceConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x70ea1dc4c5721865L, "jetbrains.mps.baseLanguage.structure.IYetUnresolved"), false, new SAbstractConcept[]{MetaAdapterFactory.getInterfaceConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x70ea1dc4c5721865L, "jetbrains.mps.baseLanguage.structure.IYetUnresolved")});
for (SNode unk : ListSequence.fromList(unknowns)) {
final SNode unkNode = unk;
final _FunctionTypes._return_P0_E0<? extends SNode> subst = ((_FunctionTypes._return_P0_E0<? extends SNode>) BHReflection.invoke(unk, SMethodTrimmedId.create("evaluateSubst", null, "73E7sj5sxxG")));
if (subst == null) {
continue;
}
SNode theRightNode = subst.invoke();
MapSequence.fromMap(resolutionMap).put(unkNode, theRightNode);
}
progress.advance(1);
}
}
});
progress.step("replacing nodes");
modelAccess.replaceNodes(new Runnable() {
public void run() {
MapSequence.fromMap(resolutionMap).visitAll(new IVisitor<IMapping<SNode, SNode>>() {
public void visit(IMapping<SNode, SNode> it) {
SNode unresolved = it.key();
SNode resolved = it.value();
SNodeOperations.replaceWithAnother(unresolved, resolved);
// FIXME maybe it's better to re-use auto model import
final SModel sourceModel = resolved.getModel();
Sequence.fromIterable(JavaToMpsConverter.deepReferences(resolved)).ofType(StaticReference.class).visitAll(new IVisitor<StaticReference>() {
public void visit(StaticReference it) {
SModelReference targetModel = it.getTargetSModelReference();
if (!(sourceModel.getReference().equals(targetModel))) {
((SModelInternal) sourceModel).addModelImport(targetModel, true);
}
}
});
}
});
}
});
progress.advance(1);
progress.done();
return MapSequence.fromMap(resolutionMap).values();
}
public static void tryResolveDynamicRefs(Iterable<SNode> nodes) {
Deque<SNode> stack = DequeSequence.fromDequeNew(new LinkedList<SNode>());
DequeSequence.fromDequeNew(stack).addSequence(Sequence.fromIterable(nodes));
while (DequeSequence.fromDequeNew(stack).isNotEmpty()) {
SNode node = DequeSequence.fromDequeNew(stack).popElement();
SModel ourModel = node.getModel();
DequeSequence.fromDequeNew(stack).addSequence(ListSequence.fromList(SNodeOperations.getChildren(node)));
Iterable<? extends SReference> refs = node.getReferences();
for (SReference ref : Sequence.fromIterable(refs)) {
if (!(ref instanceof DynamicReference)) {
continue;
}
// FIXME temp hack around typesystem looping when resolving certain dyn.references
SNode target = ref.getTargetNode();
if (target == null) {
continue;
}
node.setReferenceTarget(ref.getRole(), target);
SModel targetModel = target.getModel();
if (targetModel != null && !(ourModel.getReference().equals(targetModel.getReference()))) {
((SModelInternal) ourModel).addModelImport(targetModel.getReference(), true);
}
}
}
}
}