/*
* Copyright 2011-2011 Gregory Shrago
*
* 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 peg;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.containers.MultiMap;
import org.antlr.runtime.tree.Tree;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
import static peg.GeneratorUtil.*;
import static peg.GrammarParser.*;
/*
Known attributes:
parserClass - inheritable, with splitting support
<token> = <token-text>
extends
implements
mixin (rule)
pin (rule)
recoverRoot (rule)
external (rule)
*/
/*
* todo ?? Grammar preprocessing: Left recursion -> suffix
*
* todo tree parsing - for big files support
* todo rewrite generator on BnfPsi, get rid of antlr, support "external" modifier
* todo build test plugin
*/
public class Generator {
public static final String IELEMENTTYPE_CLASS = "com.intellij.psi.tree.IElementType";
private final Map<String, Tree> ruleMap = new TreeMap<String, Tree>();
private final Map<String, String> ruleParserClasses = new TreeMap<String, String>();
private final Set<String> simpleTokens = new HashSet<String>();
private final Tree treeRoot;
private String rootPath;
private final String grammarRoot;
private PrintWriter out;
public Generator(Tree tree, String path) {
treeRoot = tree;
rootPath = path;
final List<Tree> rules = Rule.list(tree);
grammarRoot = rules.isEmpty()? null : Rule.name(rules.get(0));
for (Tree r : rules) {
ruleMap.put(Rule.name(r), r);
ruleParserClasses.put(Rule.name(r), getAttribute(r, "parserClass", "generated.Parser"));
}
}
public void out(int level, String s) {
if (level > 0) {
out.print(StringUtil.repeat(" ", level));
}
out.println(s);
}
private void newLine() {
out(0, "");
}
public void generate() throws IOException {
{
for (String className : ruleParserClasses.values()) {
TreeMap<String, Tree> map = new TreeMap<String, Tree>();
for (String ruleName : ruleMap.keySet()) {
if (className.equals(ruleParserClasses.get(ruleName))) {
map.put(ruleName, ruleMap.get(ruleName));
}
}
File parserFile = new File(rootPath + File.separatorChar + className.replace('.', File.separatorChar) + ".java");
parserFile.getParentFile().mkdirs();
out = new PrintWriter(new FileOutputStream(parserFile));
try {
generateParser(className, map);
} finally {
out.close();
}
}
}
{
String className = getAttribute(treeRoot, "elementTypeHolderClass", "generated.ParserTypes");
File parserFile = new File(rootPath+File.separatorChar +className.replace('.', File.separatorChar)+".java");
parserFile.getParentFile().mkdirs();
out = new PrintWriter(new FileOutputStream(parserFile));
try {
generateElementTypesHolder(className);
}
finally {
out.close();
}
}
Map<String, String> infClasses = new HashMap<String, String>();
GraphHelper graphHelper = new GraphHelper(ruleMap);
for (String ruleName : ruleMap.keySet()) {
Tree rule = ruleMap.get(ruleName);
if (Rule.isPrivate(rule)) continue;
String psiPackage = getAttribute(treeRoot, "psiPackage", "generated.psi");
String psiClass = psiPackage +"."+getRulePsiClassName(rule, ruleName, true);
infClasses.put(ruleName, psiClass);
File psiFile = new File(rootPath + File.separatorChar + psiClass.replace('.', File.separatorChar) + ".java");
psiFile.getParentFile().mkdirs();
out = new PrintWriter(new FileOutputStream(psiFile));
try {
generatePsiIntf(rule, psiClass, getSuperClassName(rule, true, psiPackage, ""), graphHelper);
} finally {
out.close();
}
}
for (String ruleName : ruleMap.keySet()) {
Tree rule = ruleMap.get(ruleName);
if (Rule.isPrivate(rule)) continue;
String psiPackage = getAttribute(treeRoot, "psiImplPackage", "generated.psi.impl");
String suffix = getAttribute(treeRoot, "psiImplClassSuffix", "Impl");
String psiClass = psiPackage +"."+getRulePsiClassName(rule, ruleName, true) + suffix;
File psiFile = new File(rootPath + File.separatorChar + psiClass.replace('.', File.separatorChar) + ".java");
psiFile.getParentFile().mkdirs();
out = new PrintWriter(new FileOutputStream(psiFile));
try {
generatePsiImpl(rule, psiClass, infClasses.get(ruleName), getSuperClassName(rule, false, psiPackage, suffix), graphHelper);
} finally {
out.close();
}
}
}
@Nullable
private String getSuperClassName(Tree rule, boolean intf, String psiPackage, String suffix) {
String superRuleName = getAttribute(rule, "extends", null);
Tree superRule = superRuleName == null? null : ruleMap.get(superRuleName);
if (superRule == null) return intf? getAttribute(rule, "implements", (String)null) : superRuleName;
return psiPackage +"." +getRulePsiClassName(superRule, Rule.name(superRule), true) + suffix;
}
private static String getRulePsiClassName(Tree rule, String ruleName, boolean withPrefix) {
StringBuilder sb = new StringBuilder();
if (withPrefix) {
sb.append(getAttribute(rule, "psiClassPrefix", ""));
}
for (String s : ruleName.split("_")) {
if (s.length() == 0) continue;
sb.append(Character.toUpperCase(s.charAt(0))).append(s.substring(1).toLowerCase());
}
return sb.toString();
}
public void generateParser(String parserClass, Map<String, Tree> ruleMap) {
final String elementTypeHolderClass = getAttribute(treeRoot, "elementTypeHolderClass", "generated.ParserTypes");
final String stubParser = getAttribute(treeRoot, "stubParserClass", "generated.ParserUtil");
int offset = 1;
String rootParserClass = ruleParserClasses.get(grammarRoot);
boolean rootParser = parserClass.equals(rootParserClass);
generateClassHeader(parserClass,
"org.jetbrains.annotations.*;" +
"com.intellij.lang.LighterASTNode;" +
"com.intellij.lang.PsiBuilder;" +
"com.intellij.lang.PsiBuilder.Marker;" +
"com.intellij.openapi.diagnostic.Logger;" +
"static " + elementTypeHolderClass+".*;" +
"static " + stubParser+".*;"+
(!rootParser? "static "+rootParserClass+".recursion_guard_;" +
"static " + rootParserClass + ".type_extends_;":
IELEMENTTYPE_CLASS + ";" +
"com.intellij.lang.ASTNode;" +
"com.intellij.psi.tree.TokenSet;" +
"com.intellij.lang.PsiParser;")
, "@SuppressWarnings({\"SimplifiableIfStatement\", \"UnusedAssignment\"})",
false, "", rootParser? "PsiParser": "");
out(offset, "public static Logger LOG_ = Logger.getInstance(\""+parserClass+"\");");
newLine();
if (rootParser) {
out(offset, "@NotNull");
out(offset++, "public ASTNode parse(final IElementType root_, final PsiBuilder builder_) {");
out(offset, "boolean result_;");
boolean first = true;
for (String ruleName : ruleMap.keySet()) {
Tree rule = ruleMap.get(ruleName);
if (Rule.isPrivate(rule) || grammarRoot.equals(ruleName)) continue;
String elementType = getElementType(rule);
out(offset++, (first?"" : "else ") + "if (root_ == "+elementType +") {");
out(offset, "result_ = "+ruleName+"(builder_, false, 0);");
out(--offset, "}");
if (first) first = false;
}
if (first) {
out(offset, "result_ = " + grammarRoot+"(builder_, false, 0);");
}
else {
out(offset++, "else {");
out(offset, "Marker marker_ = builder_.mark();");
out(offset++, "try {");
out(offset, "result_ = " + grammarRoot + "(builder_, false, 0);");
out(offset++, "while (builder_.getTokenType() != null) {");
out(offset, "builder_.advanceLexer();");
out(--offset, "}");
out(--offset, "}");
out(offset++, "finally {");
out(offset, "marker_.done(root_);");
out(--offset, "}");
out(--offset, "}");
}
out(offset, "return builder_.getTreeBuilt();");
out(--offset, "}");
newLine();
int maxRecursionLevel = getAttribute(treeRoot, "maxRecursionLevel", 100);
out(offset++, "public static boolean recursion_guard_(PsiBuilder builder_, int level_, String funcName_) {");
out(offset++, "if (level_ > " + maxRecursionLevel + ") {");
out(offset, "builder_.error(\"Maximum recursion level (\"+"+maxRecursionLevel+"+\") reached in\"+funcName_);");
out(offset, "return false;");
out(--offset, "}");
out(offset, "return true;");
out(--offset, "}");
newLine();
MultiMap<String, String> extendsMap = new MultiMap<String, String>();
for (String ruleName : ruleMap.keySet()) {
Tree rule = ruleMap.get(ruleName);
if (Rule.isPrivate(rule)) continue;
Tree superRule = ruleMap.get(Rule.attribute(rule, "extends", ""));
if (superRule == null) continue;
extendsMap.putValue(getElementType(superRule), getElementType(rule));
}
out(offset++, "private static final TokenSet[] EXTENDS_SETS_ = new TokenSet[] {");
for (String ruleName : extendsMap.keySet()) {
extendsMap.putValue(ruleName, ruleName); // add super to itself
out(offset, "TokenSet.create("+StringUtil.join(extendsMap.get(ruleName), ",")+"),");
}
out(--offset, "};");
out(offset++, "public static boolean type_extends_(IElementType child_, IElementType parent_) {");
out(offset++, "for (TokenSet set : EXTENDS_SETS_) {");
out(offset, "if (set.contains(child_) && set.contains(parent_)) return true;");
out(--offset, "}");
out(offset, "return false;");
out(--offset, "}");
newLine();
}
for (String ruleName : ruleMap.keySet()) {
Tree rule = ruleMap.get(ruleName);
out(offset, "/* ********************************************************** */");
generateNode(offset, rule, Rule.body(rule), Rule.isPrivate(rule), ruleName, new HashSet<Tree>());
newLine();
}
out(--offset, "}");
}
private void generateClassHeader(String parserClass, String imports, String annos, boolean intf, String... supers) {
out(0, "package " + StringUtil.getPackageName(parserClass) +";");
newLine();
for (String s : imports.split(";")) {
out(0, "import "+s+";");
}
newLine();
StringBuilder sb = new StringBuilder();
for (int i = 0, supersLength = supers.length; i < supersLength; i++) {
String aSuper = supers[i];
if (StringUtil.isEmpty(aSuper)) continue;
if (i == 0) sb.append(" extends ").append(aSuper);
else if (i == 1) sb.append(" implements ").append(aSuper);
else sb.append(", ").append(aSuper);
}
if (StringUtil.isNotEmpty(annos)) {
out(0, annos);
}
out(0, "public "+(intf?"interface ":"class ")+StringUtil.getShortName(parserClass)+sb.toString() +" {");
newLine();
}
private void generateNode(int offset, Tree rule, Tree node, boolean shouldBePrivate, String funcName, HashSet<Tree> visited) {
int type = node.getType();
if (type == STRING || type == NUMBER || type == ID || !visited.add(node)) return;
boolean isPrivate = shouldBePrivate || grammarRoot.equals(Rule.name(rule));
out(offset, "// " +(isPrivate?"private":"")+ node.toStringTree());
boolean isRule = node.getParent() == rule;
boolean firstNonTrivial = node == Rule.firstNotTrivial(rule);
final boolean recoverRoot = firstNonTrivial && Rule.attribute(rule, "recoverRoot", false);
out(offset++, (isPrivate || !isRule ? "private " : "public ") + "static boolean " + funcName + "(PsiBuilder builder_, boolean optional_, int level_) {");
if (node.getChildCount() == 0 && type == SEQ) {
out(offset, "return true;");
out(--offset, "}");
return;
}
String debugFuncName = funcName; // + ":" + node.toStringTree();
out(offset, "if (!recursion_guard_(builder_, level_, \"" + debugFuncName +"\")) return false;");
List<Tree> children = getChildren(node);
if (isTrivialNode(node)) {
Tree child = node.getChild(0);
out(offset, "return " + generateNodeCall(rule, child, getNextName(funcName, 0), "optional_") + ";");
out(--offset, "}");
newLine();
generateNode(offset, rule, child, shouldBePrivate, getNextName(funcName, 0), visited);
return;
}
final int pin = firstNonTrivial && type == SEQ? Rule.attribute(rule, "pin", children.size()) : children.size();
if (!children.isEmpty() && (pin <= 0 || pin > children.size())) {
throw new IllegalArgumentException("wrong pin in "+funcName+": "+ pin + " [1, "+children.size()+"]");
}
out(offset, "boolean result_ = " + (type == ZEROMORE || type == OPT) + ";");
if (type == SEQ && pin > 0 && pin < children.size()) {
out(offset, "boolean pinned_;");
}
if (!isPrivate && (type == SEQ || type == CHOICE || type == ONEMORE || type == ZEROMORE)) {
out(offset, "final int start_ = builder_.getCurrentOffset();");
}
out(offset, "final Marker marker_ = builder_.mark();");
out(offset++, "try {");
if (type == CHOICE || recoverRoot) {
out(offset, "enterErrorRecording(builder_, level_);");
}
for (int i = 0, childrenSize = children.size(); i < childrenSize; i++) {
Tree child = children.get(i);
boolean optional = type == OPT || type == ZEROMORE;
String nodeCall = generateNodeCall(rule, child, getNextName(funcName, i), Boolean.toString(optional));
switch (type) {
case CHOICE: {
out(offset++, (i > 0? "if (!result_) ":"")+"{");
out(offset, "Marker m_ = builder_.mark();");
out(offset++, "try {");
out(offset, "result_ = " + nodeCall+";");
out(--offset, "}");
out(offset++, "finally {");
out(offset++, "if (!result_) {");
out(offset, "m_.rollbackTo();");
out(--offset, "}");
out(offset++, "else {");
out(offset, "m_.drop();");
out(--offset, "}");
out(--offset, "}");
out(--offset, "}");
break;
}
case SEQ: {
if (i == pin) out(offset, "pinned_ = result_;");
if (i >= pin) out(offset, "result_ = result_ && (" +nodeCall + " || pinned_);");
else if (i > 0) out(offset, "result_ = result_ && " + nodeCall + ";");
else out(offset, "result_ = "+nodeCall + ";");
break;
}
case OPT: {
out(offset, nodeCall + ";");
break;
}
case ONEMORE:
out(offset, "result_ = " + nodeCall + ";");
nodeCall = generateNodeCall(rule, child, getNextName(funcName, i), Boolean.toString(true));
// fall through
case ZEROMORE: {
out(offset, "int offset_ = builder_.getCurrentOffset();");
out(offset++, "while (!builder_.eof() && " + nodeCall +") {");
out(offset++, "if (offset_ == builder_.getCurrentOffset()) {");
out(offset, "builder_.error(\"Empty element parsed in "+debugFuncName+"\");");
out(offset, "break;");
out(--offset, "}");
out(offset, "offset_ = builder_.getCurrentOffset();");
out(--offset, "}");
break;
}
case AND: {
out(offset, "result_ = " + nodeCall+";");
break;
}
case NOT: {
out(offset, "result_ = !" + nodeCall+";");
break;
}
default: throw new AssertionError(GrammarParser.tokenNames[type]);
}
}
out(--offset, "}");
out(offset++, "finally {");
if (type == CHOICE && !recoverRoot) {
out(offset, "result_ = exitErrorRecording(builder_, result_, level_, " + recoverRoot + ");");
}
if (!isPrivate) {
String elementType = getElementType(rule);
if (type == SEQ || type == CHOICE || type == ONEMORE || type == ZEROMORE) {
out(offset, "LighterASTNode last_ = result_? builder_.getLatestDoneMarker() : null;");
out(offset++, "if (last_ != null && last_.getStartOffset() == start_ && type_extends_(last_.getTokenType(), "+elementType+")) {");
out(offset, "marker_.drop();");
out(--offset, "}");
out(offset++, "else if (result_) {");
}
else {
out(offset++, "if (result_) {");
}
out(offset, "marker_.done("+ elementType +");");
out(--offset, "}");
if (recoverRoot) {
out(offset++, "else {");
out(offset, "marker_.rollbackTo();");
out(--offset, "}");
}
else {
out(offset++, "else if (optional_) {");
out(offset, "marker_.rollbackTo();");
out(--offset, "}");
out(offset++, "else {");
out(offset, "marker_.drop();");
out(--offset, "}");
}
}
else {
if (type == AND || type == NOT) {
out(offset, "marker_.rollbackTo();");
}
else if (type != OPT && type != ZEROMORE) {
out(offset++, "if (!result_ && optional_) {");
out(offset, "marker_.rollbackTo();");
out(--offset, "}");
out(offset++, "else {");
out(offset, "marker_.drop();");
out(--offset, "}");
}
else {
out(offset, "marker_.drop();");
}
}
if (recoverRoot) {
out(offset, "result_ = exitErrorRecording(builder_, result_, level_, " + recoverRoot + ");");
}
out(--offset, "}");
out(offset, "return result_;");
out(--offset, "}");
newLine();
for (int i = 0, childrenSize = children.size(); i < childrenSize; i++) {
generateNode(offset, rule, children.get(i), true, getNextName(funcName, i), visited);
}
}
private String generateNodeCall(Tree rule, Tree node, String nextName, String optional) {
int type = node.getType();
String text = node.getText();
if (type == STRING) {
String value = StringUtil.stripQuotesAroundValue(text);
String attributeName = getAttributeName(rule, value);
if (attributeName != null) {
return generateConsumeToken(rule, attributeName, optional);
}
return generateConsumeTextToken(value, optional);
}
else if (type == NUMBER) {
return generateConsumeTextToken(text, optional);
}
else if (type == ID) {
Tree subRule = ruleMap.get(text);
if (subRule != null) {
String external = Rule.attribute(subRule, "external", null);
String method;
if (StringUtil.isNotEmpty(external)) {
method = external;
}
else {
method = Rule.name(subRule);
String parserClass = ruleParserClasses.get(method);
if (!parserClass.equals(ruleParserClasses.get(Rule.name(rule)))) {
method = StringUtil.getShortName(parserClass) + "." + method;
}
}
return method+"(builder_, " + optional+", level_ + 1)";
}
return generateConsumeToken(rule, text, optional);
}
else {
return nextName+"(builder_, " + optional+", level_ + 1)";
}
}
private String generateConsumeTextToken(String tokenText, String optional) {
return "consumeToken(builder_, "+optional+", \""+tokenText+"\")";
}
private String generateConsumeToken(Tree rule, String tokenName, String optional) {
simpleTokens.add(tokenName);
return "consumeToken(builder_, " + optional + ", "+getElementType(rule, tokenName)+")";
}
private String getNextName(String funcName, int i) {
return funcName + "_" + i;
}
private String getElementType(Tree rule) {
return getElementType(rule, Rule.name(rule));
}
private String getElementType(Tree rule, String token) {
return getAttribute(rule, "elementTypePrefix", "") + token.toUpperCase();
}
/*ElementTypes******************************************************************/
private void generateElementTypesHolder(String className) {
String implPackage = getAttribute(treeRoot, "psiImplPackage", "generated.psi.impl");
final String elementTypeClass = getAttribute(treeRoot, "elementTypeClass", IELEMENTTYPE_CLASS);
final String tokenTypeClass = getAttribute(treeRoot, "tokenTypeClass", IELEMENTTYPE_CLASS);
generateClassHeader(className,
IELEMENTTYPE_CLASS+";" +
"com.intellij.psi.PsiElement;" +
"com.intellij.lang.ASTNode;" +
elementTypeClass+";" +
tokenTypeClass+";" +
implPackage + ".*;", "", true);
for (String ruleName : ruleMap.keySet()) {
final Tree rule = ruleMap.get(ruleName);
final boolean isPrivate = Rule.isPrivate(rule);
if (isPrivate || grammarRoot.equals(ruleName)) continue;
final String elementType = getElementType(rule);
out(1, "public static final IElementType " + elementType + " = new "
+ StringUtil.getShortName(elementTypeClass) + "(\"" + elementType + "\");");
}
newLine();
for (String token : simpleTokens) {
String name = getAttribute(treeRoot, token, token);
out(1, "public static final IElementType " + getElementType(treeRoot, token) + " = new "
+ StringUtil.getShortName(tokenTypeClass) + "(\"" + name + "\");");
}
newLine();
out(1, "public static class Factory {");
out(2, "public static PsiElement createElement(ASTNode node) {");
out(3, "IElementType type = node.getElementType();");
String suffix = getAttribute(treeRoot, "psiImplClassSuffix", "Impl");
boolean first = true;
for (String ruleName : ruleMap.keySet()) {
final Tree rule = ruleMap.get(ruleName);
final boolean isPrivate = Rule.isPrivate(rule);
if (isPrivate || grammarRoot.equals(ruleName)) continue;
String psiClass = getRulePsiClassName(rule, ruleName, true) + suffix;
out(3, (!first?"else ":"")+" if (type == "+ getElementType(rule)+") {");
out(4, "return new "+psiClass+"(node);");
first = false;
out(3, "}");
}
out(3, "throw new AssertionError(\"Unknown element type: \" + type);");
out(2, "}");
out(1, "}");
out(0, "}");
}
/*PSI******************************************************************/
private void generatePsiIntf(Tree rule, String psiClass, String superRuleClass, GraphHelper helper) {
String psiSuper = superRuleClass;
generateClassHeader(psiClass, "org.jetbrains.annotations.*;" +
"java.util.List;" +
"com.intellij.psi.PsiElement;" +
psiSuper, "", true, StringUtil.getShortName(psiSuper));
Map<Tree, GraphHelper.Cardinality> accessors = helper.getFor(rule);
for (Tree tree : getSortedPublicRules(accessors.keySet())) {
generatePsiAccessor(rule, tree, accessors.get(tree), true);
}
for (Tree tree : getSortedSimpleTokens(accessors.keySet())) {
generatePsiAccessor(rule, tree, accessors.get(tree), true);
}
out(0, "}");
}
private void generatePsiImpl(Tree rule, String psiClass, String superInterface, String superRuleClass, GraphHelper helper) {
String typeHolderClass = getAttribute(treeRoot, "elementTypeHolderClass", "generated.ParserTypes");
// direct mixin attribute overrides "extends":
String implSuper = Rule.attribute(rule, "mixin", null);
if (implSuper == null) implSuper = superRuleClass;
generateClassHeader(psiClass,
"java.util.List;" +
"org.jetbrains.annotations.*;" +
"com.intellij.lang.ASTNode;" +
"com.intellij.psi.PsiElement;" +
"com.intellij.psi.util.PsiTreeUtil;" +
"static " + typeHolderClass +".*;" +
(StringUtil.isNotEmpty(implSuper)? implSuper+";":"") +
StringUtil.getPackageName(superInterface)+".*", "", false, StringUtil.getShortName(implSuper),
StringUtil.getShortName(superInterface));
out(1, "public " + StringUtil.getShortName(psiClass) + "(ASTNode node) {");
out(2, "super(node);");
out(1, "}");
newLine();
Map<Tree, GraphHelper.Cardinality> accessors = helper.getFor(rule);
for (Tree tree : getSortedPublicRules(accessors.keySet())) {
generatePsiAccessor(rule, tree, accessors.get(tree), false);
}
for (Tree tree : getSortedSimpleTokens(accessors.keySet())) {
generatePsiAccessor(rule, tree, accessors.get(tree), false);
}
out(0, "}");
}
private void generatePsiAccessor(Tree rule, Tree tree, GraphHelper.Cardinality type, boolean intf) {
boolean token = !Rule.is(tree);
boolean many = type == GraphHelper.Cardinality.AT_LEAST_ONE || type == GraphHelper.Cardinality.ANY_NUMBER;
if (token && many) return;
String ruleName;
if (token) {
if (tree.getType() == STRING) {
String value = StringUtil.stripQuotesAroundValue(tree.getText());
ruleName = getAttributeName(rule, value);
}
else if (tree.getType() == ID) {
ruleName = tree.getText();
}
else ruleName = null; // do not bother generate numbers & simple literals
}
else {
ruleName = Rule.name(tree);
}
if (ruleName == null) return;
String getterNameBody = getRulePsiClassName(rule, ruleName, false);
String getterName = "get" + getterNameBody + (many ? "List" : "");
if (!intf) out(1, "@Override");
if (type == GraphHelper.Cardinality.REQUIRED) {
out(1, "@NotNull");
} else if (type == GraphHelper.Cardinality.OPTIONAL) {
out(1, "@Nullable");
}
else {
out(1, "@NotNull");
}
String className = token? "PsiElement" : getRulePsiClassName(rule, Rule.name(tree), true);
String tail = intf? "();" : "() {";
out(1, "public "+(many?"List<":"") + className + (many ? "> " : " ") + getterName + tail);
if (!intf) {
if (token) {
out(2, "ASTNode child = getNode().findChildByType("+getElementType(rule, ruleName)+");");
out(2, "return child == null? null : child.getPsi();");
}
else {
if (many) {
out(2, "return PsiTreeUtil.getChildrenOfTypeAsList(this, " + className + ".class);");
}
else {
out(2, "return PsiTreeUtil.getChildOfType(this, " + className + ".class);");
}
}
out(1, "}");
}
newLine();
}
private Collection<Tree> getSortedPublicRules(Collection<Tree> accessors) {
TreeMap<String, Tree> result = new TreeMap<String, Tree>();
for (Tree tree : accessors) {
if (!Rule.is(tree) || Rule.isPrivate(tree)) continue;
result.put(Rule.name(tree), tree);
}
return result.values();
}
private Collection<Tree> getSortedSimpleTokens(Collection<Tree> accessors) {
TreeMap<String, Tree> result = new TreeMap<String, Tree>();
for (Tree tree : accessors) {
int type = tree.getType();
if (type == ID && simpleTokens.contains(tree.getText()) /*|| type == STRING || type == NUMBER*/) {
result.put(tree.getText(), tree);
}
}
return result.values();
}
}