/******************************************************************************* * Copyright (c) 2007, 2008 Edgar Espina. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * *******************************************************************************/ package org.deved.antlride.internal.core.model; import java.util.Collection; import java.util.List; import java.util.regex.Pattern; import org.deved.antlride.core.model.ASTSuffix; import org.deved.antlride.core.model.ElementKind; import org.deved.antlride.core.model.GrammarType; import org.deved.antlride.core.model.IAlternative; import org.deved.antlride.core.model.IBlock; import org.deved.antlride.core.model.ICallExpression; import org.deved.antlride.core.model.IGrammar; import org.deved.antlride.core.model.IModelElement; import org.deved.antlride.core.model.IOption; import org.deved.antlride.core.model.IOptions; import org.deved.antlride.core.model.IParameter; import org.deved.antlride.core.model.IParameters; import org.deved.antlride.core.model.IReturn; import org.deved.antlride.core.model.IReturns; import org.deved.antlride.core.model.IRule; import org.deved.antlride.core.model.IRuleAction; import org.deved.antlride.core.model.IRuleCatch; import org.deved.antlride.core.model.IRuleFinally; import org.deved.antlride.core.model.IRuleScope; import org.deved.antlride.core.model.IRuleThrows; import org.deved.antlride.core.model.IScope; import org.deved.antlride.core.model.IScopeReference; import org.deved.antlride.core.model.ISourceElement; import org.deved.antlride.core.model.IStatement; import org.deved.antlride.core.model.IStatementAction; import org.deved.antlride.core.model.RuleType; import org.deved.antlride.core.model.ast.IModelElementVisitor; import org.deved.antlride.core.model.ast.ModelElementQuery; import org.deved.antlride.core.model.ast.ToEbnfVisitor; import org.deved.antlride.core.model.ast.criteria.ModelElementCriteriaFactory; import org.deved.antlride.core.model.dltk.ast.DASTRule; import org.deved.antlride.core.util.AntlrTextHelper; import org.eclipse.dltk.ast.ASTNode; import org.eclipse.dltk.ast.Modifiers; import org.eclipse.dltk.ast.declarations.Argument; import org.eclipse.dltk.ast.statements.Block; public class ARule extends AAbstractModelElement implements IRule { private static final IRuleAction[] EMPTY_ACTIONS = new IRuleAction[0]; private static final IRuleScope[] NO_RULE_SCOPES = new IRuleScope[0]; private static final IRuleCatch[] NO_CATCHS = new IRuleCatch[0]; private static final IScopeReference[] NO_SCOPES_REFERENCES = new IScopeReference[0]; private static final Pattern IGNORED_RULES = Pattern .compile(".*\\$channel\\s*=\\s*HIDDEN.*"); private IOptions options; private RuleAccessModifier accessModifier; private String documentation; private ISourceElement name; private IParameters parameters; private IReturns returns; private IRuleScope[] scopes = NO_RULE_SCOPES; private IScopeReference[] usesScopes = NO_SCOPES_REFERENCES; private IRuleAction actions[]; private boolean ignored; private IBlock body; private ASTSuffix astSuffix; private DASTRule ruleNode; private String declaration; private String stringList; private String stringTree; private IRuleCatch[] catchs = NO_CATCHS; private ARuleFinally ruleFinally; private ARuleThrows ruleThrows; public ARule(IGrammar parent) { super(parent); accessModifier = RuleAccessModifier.PUBLIC; setAstSuffix(ASTSuffix.NONE); } public ASTSuffix getAstSuffix() { return astSuffix; } public void setAccessModifier(RuleAccessModifier accessModifier) { this.accessModifier = accessModifier; } public void setAstSuffix(ASTSuffix astSuffix) { this.astSuffix = astSuffix; } public void setName(ISourceElement name) { this.name = name; } public <E> E getAdapter(Class<E> adapter) { if (adapter == IRule.class) return adapter.cast(this); if (adapter == IGrammar.class) return adapter.cast(getParent()); if (adapter == IOptions.class) return adapter.cast(getOptions()); if (adapter == ASTNode.class) return adapter.cast(getAST()); return null; } public IBlock getBody() { return body; } public IRuleAction findAction(String name) { if (hasActions()) { for (int i = 0; i < actions.length; i++) { IRuleAction ruleAction = actions[i]; if (ruleAction.getElementName().equals(name)) { return ruleAction; } } } return null; } public IOption findOption(String name) { if (hasOptions()) return options.findOption(name); return null; } public IScope findScope(String name) { if (hasScopes()) { for (int i = 0; i < scopes.length; i++) { IRuleScope ruleScope = scopes[i]; if (ruleScope.getElementName().equals(name)) { return ruleScope; } } } return null; } public IScopeReference findScopeReference(String name) { if (hasScopesReferences()) { for (int i = 0; i < usesScopes.length; i++) { IScopeReference scope = usesScopes[i]; if (scope.getElementName().equals(name)) { return scope; } } } return null; } public IRuleAction[] getActions() { return actions; } public RuleAccessModifier getAccessModifier() { return accessModifier; } private DASTRule getAST() { if (ruleNode == null) { ruleNode = new DASTRule(getElementName(), name.sourceStart(), name .sourceEnd(), sourceStart(), sourceEnd()); Block body = ruleNode.getBody(); switch (accessModifier) { case PUBLIC: ruleNode.setModifier(Modifiers.AccPublic); break; case FRAGMENT: case PRIVATE: ruleNode.setModifier(Modifiers.AccPrivate); break; case PROTECTED: ruleNode.setModifier(Modifiers.AccProtected); break; } if (hasParameters()) { for (int i = 0; i < parameters.getParametersCount(); i++) { IParameter paremeter = parameters.getParemeter(i); Argument argument = (Argument) paremeter .getAdapter(ASTNode.class); ruleNode.addArgument(argument); } } if (hasOptions()) { ruleNode.addDecorator(options.getAdapter(ASTNode.class)); } if (hasActions()) { for (int i = 0; i < actions.length; i++) { ruleNode.addDecorator(actions[i].getAdapter(ASTNode.class)); } } if (hasScopes()) { for (int i = 0; i < scopes.length; i++) { ruleNode.addDecorator(scopes[i].getAdapter(ASTNode.class)); } } if (hasScopesReferences()) { for (int i = 0; i < usesScopes.length; i++) { ruleNode.addDecorator(usesScopes[i] .getAdapter(ASTNode.class)); } } IModelElement[] calls = ModelElementQuery.collectCalls(this); for (int i = 0; i < calls.length; i++) { ICallExpression callExpression = (ICallExpression) calls[i]; body.addStatement(callExpression.getAdapter(ASTNode.class)); } calls = null; } return ruleNode; } public String getDeclaration() { if (declaration == null) { try { declaration = body.toString(); } catch (Exception ex) { } } return declaration; } public String getPlainDocumentation() { return documentation; } public String getDocumentation() { return AntlrTextHelper.parseDoc( documentation ); } public ElementKind getElementKind() { return ElementKind.RULE; } public String getElementName() { return name.getText(); } public ISourceElement getName() { return name; } public IOptions getOptions() { return options; } public IParameter getParameter(int index) { return parameters == null ? null : parameters.getParemeter(index); } public IParameters getParameters() { return parameters; } public int getParametersCount() { return parameters == null ? 0 : parameters.getParametersCount(); } public IReturn getReturn(int index) { return returns == null ? null : returns.getReturn(index); } @Override public boolean equals(Object obj) { if (obj == this) return true; if (obj instanceof IRule) { IRule rule = (IRule) obj; return getParent().equals(rule.getParent()) && getElementName().equals(rule.getElementName()); } return false; } public IReturns getReturns() { return returns; } public int getReturnsCount() { return returns == null ? 0 : returns.getReturnsCount(); } public RuleType getRuleType() { IGrammar grammar = (IGrammar) getParent(); GrammarType grammarType = grammar.getGrammarType(); if (grammarType == GrammarType.LEXER) return RuleType.LEXER; if (grammarType == GrammarType.TREE_PARSER) return RuleType.TREE_PARSER; if (Character.isUpperCase(getElementName().charAt(0))) return RuleType.LEXER; return RuleType.PARSER; } public IRuleScope[] getScopes() { return scopes; } public IScopeReference[] getScopesReferences() { return usesScopes; } public boolean hasActions() { return actions != null && actions.length > 0; } public boolean hasParameters() { return parameters != null; } public boolean hasOptions() { return options != null; } public boolean hasReturns() { return returns != null; } public boolean hasScopes() { return scopes != null && scopes.length > 0; } public boolean hasScopesReferences() { return usesScopes != null && usesScopes.length > 0; } public boolean isIgnored() { return ignored; } public boolean isEmpty() { return ModelElementQuery.count(body, ModelElementCriteriaFactory .kind(ElementKind.CALL)) == 0 && ModelElementQuery.count(body, ModelElementCriteriaFactory .kind(ElementKind.STATEMENT_ACTION)) == 0; // return false; } public boolean isLexerRule() { return Character.isUpperCase(getElementName().charAt(0)); } public String toStringList() { return stringList; } public String toStringTree() { return stringTree; } public void setBody(IBlock body) { this.body = body; ((ARuleBody) body).setParent(this); // ((AStatement) body).setParent(this); // ((AStatement) body).setEnclosingRule(this); // parent(body); if (isLexerRule()) { // check if it's ignored if (this.body.size() > 0) { IAlternative alternative = (IAlternative) this.body.get(0); if (alternative.size() - 1 >= 0) { IStatement statement = alternative .get(alternative.size() - 1); if (statement.getElementKind() == ElementKind.STATEMENT_ACTION) { IStatementAction statementAction = statement .getAdapter(IStatementAction.class); String text = statementAction.getAction().getText(); ignored = IGNORED_RULES.matcher(text).matches(); } } } } } public void setDocumentation(String documentation) { this.documentation = documentation; } public void setOptions(IOptions options) { this.options = options; ((AOptions) this.options).setParent(this); } public void setParameters(IParameters parameters) { this.parameters = parameters; } public void setReturns(IReturns returns) { this.returns = returns; } public void setActions(Collection<IRuleAction> actions) { if (actions == null || actions.size() == 0) { this.actions = EMPTY_ACTIONS; } else { this.actions = new IRuleAction[actions.size()]; this.actions = actions.toArray(this.actions); for (int i = 0; i < this.actions.length; i++) { ((AAbstractModelElement) this.actions[i]).setParent(this); } } } public void setUsesScopes(List<IScopeReference> scopes) { if (scopes == null || scopes.size() == 0) { this.usesScopes = NO_SCOPES_REFERENCES; } else { // scope reference this.usesScopes = scopes .toArray(new IScopeReference[scopes.size()]); } for (int i = 0; i < this.usesScopes.length; i++) { ((AAbstractModelElement) this.usesScopes[i]).setParent(this); } } public void setRuleScopes(List<IRuleScope> scopes) { if (scopes == null || scopes.size() == 0) { this.scopes = NO_RULE_SCOPES; } else { this.scopes = scopes.toArray(new IRuleScope[scopes.size()]); } for (int i = 0; i < this.scopes.length; i++) { ((AAbstractModelElement) this.scopes[i]).setParent(this); } } public void traverse(IModelElementVisitor visitor) { if (visitor.visitRule(this)) { if (hasParameters()) parameters.traverse(visitor); if (hasReturns()) returns.traverse(visitor); if (ruleThrows != null) { ruleThrows.traverse(visitor); } if (hasOptions()) options.traverse(visitor); if (hasScopes()) { for (int i = 0; i < scopes.length; i++) { scopes[i].traverse(visitor); } } if (usesScopes != null && usesScopes.length > 0) { for (int i = 0; i < usesScopes.length; i++) { usesScopes[i].traverse(visitor); } } if (hasActions()) { for (int i = 0; i < actions.length; i++) { actions[i].traverse(visitor); } } body.traverse(visitor); visitor.endvisitRule(this); if (hasCatchs()) { for (int i = 0; i < catchs.length; i++) { catchs[i].traverse(visitor); } } if (ruleFinally != null) { ruleFinally.traverse(visitor); } } } @Override public String toString() { StringBuilder builder = new StringBuilder(); if (documentation != null) { builder.append(documentation); builder.append("\n"); } if (accessModifier != RuleAccessModifier.PUBLIC) { builder.append(accessModifier.description()); builder.append(" "); } builder.append(name.getText()); if (astSuffix != ASTSuffix.NONE) { builder.append(astSuffix.description()); } // parameters if (parameters != null) { builder.append(parameters); } // returns if (returns != null) { builder.append(returns); } // throws if (ruleThrows != null) { builder.append(ruleThrows); } // options if (this.options != null) { builder.append("\n"); builder.append(options); } // scopes if (scopes != null && scopes.length > 0) { builder.append("\n"); builder.append("scope {\n"); for (int i = 0; i < scopes.length; i++) { builder.append(scopes[i]); if (i + 1 < scopes.length) { builder.append(", "); } } builder.append("\n}\n"); } if (actions != null && actions.length > 0) { builder.append("\n"); for (int i = 0; i < actions.length; i++) { builder.append(actions[i]); } } if (this.usesScopes != null && this.usesScopes.length > 0) { builder.append("\nscope "); for (int i = 0; i < usesScopes.length; i++) { builder.append(usesScopes[i]); if (i + 1 < usesScopes.length) { builder.append(", "); } } builder.append(";\n"); } builder.append(":\n"); if (body != null) { builder.append(body); } builder.append("\n;\n\n"); if (hasCatchs()) { for (int i = 0; i < catchs.length; i++) { builder.append(catchs[i]); } } if (ruleFinally != null) { builder.append(ruleFinally); } return builder.toString(); } public void setStringList(String stringList) { this.stringList = stringList; } public void setStringTree(String stringTree) { this.stringTree = stringTree; } public void setCatchs(List<IRuleCatch> catchs) { if (catchs == null || catchs.size() == 0) { this.catchs = NO_CATCHS; } else { this.catchs = catchs.toArray(new IRuleCatch[catchs.size()]); for (IRuleCatch ruleCatch : catchs) { ((AAbstractModelElement) ruleCatch).setParent(this); } } } public IRuleCatch[] getCatchs() { return catchs; } public boolean hasCatchs() { return catchs.length > 0; } public void setFinally(ARuleFinally ruleFinally) { this.ruleFinally = ruleFinally; ruleFinally.setParent(this); } public IRuleFinally getFinally() { return ruleFinally; } public IRuleThrows getThrows() { return ruleThrows; } public void setThrows(ARuleThrows ruleThrows) { this.ruleThrows = ruleThrows; ruleThrows.setParent(this); } public String toEbnf() { return new ToEbnfVisitor(this).toEbnf(); } }