package org.jetbrains.plugins.clojure.psi.util;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiFileFactory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiErrorElement;
import com.intellij.openapi.project.Project;
import com.intellij.psi.impl.source.tree.LeafPsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.clojure.psi.api.ClList;
import org.jetbrains.plugins.clojure.psi.api.ClListLike;
import org.jetbrains.plugins.clojure.psi.api.ClVector;
import org.jetbrains.plugins.clojure.psi.api.ClojureFile;
import org.jetbrains.plugins.clojure.file.ClojureFileType;
/**
* @author ilyas
*/
public class ClojurePsiElementFactoryImpl extends ClojurePsiFactory {
private final Project myProject;
public ClojurePsiElementFactoryImpl(Project project) {
myProject = project;
}
private static final String DUMMY = "DUMMY.";
public ASTNode createSymbolNodeFromText(@NotNull String newName) {
final String text = "(" + newName + ")";
final ClojureFile dummyFile = createClojureFileFromText(text);
return dummyFile.getFirstChild().getFirstChild().getNextSibling().getNode();
}
@Override
public boolean hasSyntacticalErrors(@NotNull String text) {
final ClojureFile clojureFile = (ClojureFile) PsiFileFactory.getInstance(getProject()).createFileFromText(DUMMY + ClojureFileType.CLOJURE_FILE_TYPE.getDefaultExtension(), text);
return hasErrorElement(clojureFile);
}
public String getErrorMessage(@NotNull String text) {
if (!hasSyntacticalErrors(text)) return null;
final ClojureFile clojureFile = (ClojureFile) PsiFileFactory.getInstance(getProject()).createFileFromText(DUMMY + ClojureFileType.CLOJURE_FILE_TYPE.getDefaultExtension(), text);
return getErrorMessageInner(clojureFile);
}
private static String getErrorMessageInner(PsiElement element) {
if (element instanceof PsiErrorElement) {
return ((PsiErrorElement) element).getErrorDescription();
}
for (PsiElement child : element.getChildren()) {
final String msg = getErrorMessageInner(child);
if (msg != null) return msg;
}
return null;
}
private static boolean hasErrorElement(PsiElement element) {
if (element instanceof PsiErrorElement) return true;
for (PsiElement child : element.getChildren()) {
if (hasErrorElement(child)) return true;
}
return false;
}
@NotNull
public ClojureFile createClojureFileFromText(@NotNull String text) {
return (ClojureFile) PsiFileFactory.getInstance(getProject()).createFileFromText(DUMMY + ClojureFileType.CLOJURE_FILE_TYPE.getDefaultExtension(), text);
}
@Override
public ClList createListFromText(@NotNull String text) {
return (ClList)createSymbolNodeFromText("(" + text + ")").getPsi();
}
@Override
public ClVector createVectorFromText(@NotNull String text) {
return (ClVector)createSymbolNodeFromText("[" + text + "]").getPsi();
}
@Override
@Nullable
public ClListLike findOrCreateJavaImportForClass(PsiClass clazz, ClList importClause) {
final String name = clazz.getQualifiedName();
if (name == null) return null;
final int lastDot = name.lastIndexOf('.');
if (lastDot <= 0) return null;
final String prefix = name.substring(0, lastDot);
final String suffix = name.substring(lastDot + 1);
final ClListLike[] imported = PsiTreeUtil.getChildrenOfType(importClause, ClListLike.class);
// Find or create an import member
ClListLike importMember = null;
if (imported == null) {
importMember = addFreshImportToMember(importClause, prefix);
} else {
importMember = ContainerUtil.find(imported, new Condition<ClListLike>() {
public boolean value(ClListLike elem) {
return prefix.equals(elem.getHeadText());
}
});
if (importMember == null) {
importMember = addFreshImportToMember(importClause, prefix);
}
}
assert importMember != null;
// Insert a new class into it
final PsiElement lastChild = importMember.getLastChild();
final PsiElement newClass = createSymbolNodeFromText(suffix).getPsi();
assert newClass != null;
if (lastChild instanceof LeafPsiElement) {
importMember.addBefore(newClass, lastChild);
} else {
importMember.add(newClass);
}
return importMember;
}
private ClListLike addFreshImportToMember(ClList importClause, String prefix) {
final ClListLike vector = createVectorFromText(prefix);
final PsiElement lastChild = importClause.getLastChild();
if (lastChild instanceof LeafPsiElement) {
return (ClListLike) importClause.addBefore(vector, lastChild);
} else {
return (ClListLike) importClause.add(vector);
}
}
public Project getProject() {
return myProject;
}
}