/******************************************************************************* * 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.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import org.deved.antlride.core.AntlrCore; import org.deved.antlride.core.build.AntlrBuildUnit; import org.deved.antlride.core.build.AntlrSourceParserRepository; import org.deved.antlride.core.model.ElementKind; import org.deved.antlride.core.model.GrammarType; import org.deved.antlride.core.model.IGrammar; import org.deved.antlride.core.model.IGrammarAction; import org.deved.antlride.core.model.IGrammarScope; import org.deved.antlride.core.model.IImport; import org.deved.antlride.core.model.IImports; import org.deved.antlride.core.model.IModelElement; import org.deved.antlride.core.model.IOption; import org.deved.antlride.core.model.IOptionValue; import org.deved.antlride.core.model.IOptions; import org.deved.antlride.core.model.IRule; import org.deved.antlride.core.model.IScope; import org.deved.antlride.core.model.ISourceElement; import org.deved.antlride.core.model.IToken; import org.deved.antlride.core.model.ITokens; import org.deved.antlride.core.model.ast.AntlrModelElementLocator; import org.deved.antlride.core.model.ast.IModelElementVisitor; import org.deved.antlride.core.model.dltk.ast.DASTGrammar; import org.deved.antlride.core.util.AntlrTextHelper; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceVisitor; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.dltk.ast.ASTNode; public class AGrammar implements IGrammar { private static final IGrammarAction[] EMPTY_ACTIONS = new IGrammarAction[0]; private static final IRule[] EMPTY_RULES = new IRule[0]; private static final IGrammar[] NO_DEPS = new IGrammar[0]; private static final IGrammarScope[] EMPTY_SCOPES = new IGrammarScope[0]; private static final Map<String, String> DEFAULT_OPTIONS = new HashMap<String, String>() { /** * */ private static final long serialVersionUID = 1L; { put("language", "Java"); } }; private IGrammarAction[] actions; private String documentation; private ISourceElement name; private IOptions options; private IRule[] rules; private IGrammarScope[] scopes; private ITokens tokens; private GrammarType type; private String source; private int[] offsets; private IPath file; private IPath folder; private IPath absoluteFile; private IPath absoluteFolder; private boolean valid; private DASTGrammar grammarModule; private boolean implicitLexerGrammar; private boolean prototypeGrammar; private IImports imports; private Collection<IGrammar> grammars; private IRule[] allrules; private List<String> comments = new ArrayList<String>(); public AGrammar(IPath file, String source) { this.source = source; this.offsets = offsets(); // paths this.file = file; this.folder = file.removeLastSegments(1); IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); String parent = file.segment(0).trim(); if (parent.length() == 0) { absoluteFile = root.getLocation().removeLastSegments(1) .append(file.lastSegment()); } else { IProject project = root.getProject(parent); absoluteFile = project.getLocation().removeLastSegments(1) .append(file); } absoluteFolder = absoluteFile.removeLastSegments(1); // check for implicit lexer grammar or prototype grammar try { IFile resource = root.getFile(file); processResource(resource); } catch (Exception ex) { AntlrCore.error(ex); } } public AGrammar(String source) { this.source = source; } protected void processResource(IFile resource) throws CoreException { String prototype = resource .getPersistentProperty(AntlrBuildUnit.DEPENDENT_GRAMMAR); this.prototypeGrammar = Boolean.valueOf(prototype).booleanValue(); } public IImports getImports() { return imports; } public void setImports(IImports imports) { this.imports = imports; } @Override public boolean equals(Object obj) { if (obj == this) return true; if (obj instanceof IGrammar) { IGrammar grammar = (IGrammar) obj; return getAbsoluteFile().equals(grammar.getAbsoluteFile()); } return false; } @Override public int hashCode() { return getAbsoluteFile().hashCode(); } public IRule firstRule() { if (hasRules()) return rules[0]; return null; } public boolean isValid() { return valid; } public boolean isCombinedGrammar() { return type == GrammarType.COMBINED; } public boolean isImplicitLexerGrammar() { return implicitLexerGrammar; } public boolean isLexerGrammar() { return type == GrammarType.LEXER; } public boolean isParserGrammar() { return type == GrammarType.PARSER; } public boolean isPrototypeGrammar() { return prototypeGrammar; } public boolean isTreeParserGrammar() { return type == GrammarType.TREE_PARSER; } public boolean isCompositeGrammar() { return hasImports(); } public void setValid(boolean generateCode) { this.valid = generateCode; } public <E> E getAdapter(Class<E> adapter) { if (adapter == IGrammar.class) return adapter.cast(this); if (adapter == ASTNode.class) return adapter.cast(getGrammarModule()); return null; } private DASTGrammar getGrammarModule() { if (grammarModule == null) { grammarModule = new DASTGrammar(this); } return grammarModule; } public ElementKind getElementKind() { return ElementKind.GRAMMAR; } public IModelElement getElementAt(int position) { AntlrModelElementLocator locator = new AntlrModelElementLocator(this); IModelElement element = locator.getElementAt(position); return element; } public String getElementName() { if (name == null) { String name = getFile().removeFileExtension().segment( getFile().segmentCount() - 1); return name; } return name.getText(); } public void traverse(IModelElementVisitor visitor) { if (visitor.visitGrammar(this)) { if (hasOptions()) options.traverse(visitor); if (hasImports()) imports.traverse(visitor); if (hasTokens()) tokens.traverse(visitor); if (hasScopes()) { for (int i = 0; i < scopes.length; i++) { scopes[i].traverse(visitor); } } if (hasActions()) { for (int i = 0; i < actions.length; i++) { actions[i].traverse(visitor); } } IRule[] rules = getRules(); if (rules != null && rules.length > 0) { for (int i = 0; i < rules.length; i++) { rules[i].traverse(visitor); } } visitor.endvisitGrammar(this); } } public IGrammarAction findAction(String name) { if (hasActions()) for (int i = 0; i < actions.length; i++) { IGrammarAction action = actions[i]; if (action.getElementName().equals(name)) { return action; } } return null; } public IOption findOption(String name) { if (hasOptions()) return options.findOption(name); return null; } public IRule findRule(String name) { IRule[] rules = getRules(); for (int i = 0; i < rules.length; i++) { IRule rule = rules[i]; if (rule.getName().getText().equals(name)) { return rule; } } switch (type) { case TREE_PARSER: case PARSER: IGrammar tokenGrammar = getTokenVocab(); if (tokenGrammar != null) { return tokenGrammar.findRule(name); } } return null; } public IGrammarScope findScope(String name) { for (int i = 0; i < scopes.length; i++) { IGrammarScope scope = scopes[i]; if (scope.getElementName().equals(name)) { return scope; } } return null; } public IImport findImport(String name) { if (hasImports()) { IImport[] imports = getImports().getImports(); for (IImport imp : imports) { if (name.equals(imp.getElementName())) { return imp; } } } return null; } public IToken findToken(String name) { if (hasTokens()) return tokens.findToken(name); switch (type) { case TREE_PARSER: case PARSER: IGrammar tokenGrammar = getTokenVocab(); if (tokenGrammar != null) { return tokenGrammar.findToken(name); } } return null; } public IGrammarAction[] getActions() { return actions; } public String getPlainDocumentation() { return documentation; } public String getDocumentation() { return AntlrTextHelper.parseDoc(documentation); } public boolean isIn(int position) { return true; } public IPath getAbsoluteFile() { return absoluteFile; } public IPath getAbsoluteFolder() { return absoluteFolder; } public IPath getFile() { return file; } public IPath getFolder() { return folder; } public ISourceElement getName() { return name; } public String getOption(String optionName) { String value = null; IOption option = findOption(optionName); if (option != null) { IOptionValue optionValue = option.getValue(); if (optionValue != null) { value = optionValue.getText(); } } if (value == null) { value = DEFAULT_OPTIONS.get(optionName); } return value; } public IOptions getOptions() { return options; } public IRule[] getRules() { // if (isCompositeGrammar()) { // if (allrules == null) { // Collection<IRule> ruleList = new ArrayList<IRule>(); // ruleList.addAll(Arrays.asList(rules)); // for (IGrammar g : grammars) { // ruleList.addAll(Arrays.asList(g.getRules())); // } // allrules = ruleList.toArray(new IRule[ruleList.size()]); // ruleList.clear(); // for (int i = 0; i < rules.length; i++) { // rules[i] = null; // } // rules = null; // } // } else { allrules = rules; // } return allrules; } public void addDependent(IGrammar grammar) { if (grammars == null) { grammars = new ArrayList<IGrammar>(); } grammars.add(grammar); } public IGrammarScope[] getScopes() { return scopes; } public String getSource() { return source; } public ITokens getTokens() { return tokens; } public GrammarType getGrammarType() { return type == null ? GrammarType.COMBINED : type; } public IGrammar[] getDependents() { if (grammars == null) { return NO_DEPS; } return grammars.toArray(new IGrammar[grammars.size()]); } public IGrammar getTokenVocab() { IGrammar tokenGrammar = null; String tokenGrammarName = getOption("tokenVocab"); if (tokenGrammarName != null) { tokenGrammar = AntlrSourceParserRepository.lookupGrammar( getFolder(), tokenGrammarName + ".g"); } return tokenGrammar; } public boolean hasActions() { return actions != null && actions.length > 0; } public boolean hasImports() { return imports != null && imports.getImports().length > 0; } public boolean hasOptions() { return options != null; } public boolean hasOption(String name) { return findOption(name) != null; } public boolean hasRules() { return getRules() != null && getRules().length > 0; } public boolean hasRule(String name) { return findRule(name) != null; } public boolean hasScope(String name) { return findScope(name) != null; } public boolean hasScopes() { return scopes != null && scopes.length > 0; } public boolean hasToken(String name) { return findToken(name) != null; } public boolean hasTokens() { return tokens != null; } public IModelElement getParent() { return null; } public int sourceStart() { return 0; } public int sourceEnd() { IRule[] rules = getRules(); if (rules.length > 0) { return rules[rules.length - 1].sourceEnd(); } return 0; } private int[] offsets() { List<Integer> clines = new ArrayList<Integer>(); clines.add(new Integer(0)); int offset = 0; for (int i = 0; i < source.length(); i++) { char ch = source.charAt(i); offset++; if (ch == '\n') { clines.add(new Integer(offset)); } } int[] lines = new int[clines.size()]; for (int i = 0; i < lines.length; i++) { lines[i] = clines.get(i).intValue(); } return lines; } public void setName(ISourceElement name) { this.name = name; } public int getOffset(int line) { if (line > -1 && line < offsets.length) return offsets[line]; return -1; } public void setDocumentation(String documentation) { this.documentation = documentation; } public void setOptions(IOptions options) { this.options = options; } public void setTokens(ITokens tokens) { this.tokens = tokens; } public void setGrammarType(GrammarType type) { this.type = type; } public void setActions(Collection<IGrammarAction> actions) { if (actions == null || actions.size() == 0) { this.actions = EMPTY_ACTIONS; } else { this.actions = new IGrammarAction[actions.size()]; this.actions = actions.toArray(this.actions); for (IGrammarAction action : this.actions) { ((AGrammarAction) action).setParent(this); } } } public void setRules(Collection<IRule> rules) { if (rules == null || rules.size() == 0) { this.rules = EMPTY_RULES; } else { this.rules = rules.toArray(new IRule[rules.size()]); } } public void setScopes(Collection<IScope> scopes) { if (scopes == null || scopes.size() == 0) { this.scopes = EMPTY_SCOPES; } else { this.scopes = new IGrammarScope[scopes.size()]; this.scopes = scopes.toArray(this.scopes); for (int i = 0; i < this.scopes.length; i++) { ((AGrammarScope) this.scopes[i]).setParent(this); } } } @Override public String toString() { StringBuilder builder = new StringBuilder(); if (documentation != null && documentation.length() > 0) { builder.append(documentation); builder.append("\n"); } if (type != null && type.description().length() > 0) { builder.append(type.description()); builder.append(" "); } if (name != null) { builder.append("grammar "); builder.append(name.getText()); builder.append(";\n"); } if (options != null) { builder.append(options); } if (tokens != null) { builder.append(tokens); } if (scopes != null && scopes.length > 0) { builder.append("\n"); for (int i = 0; i < scopes.length; i++) { builder.append(scopes[i]); } } if (actions != null) { for (int i = 0; i < actions.length; i++) { builder.append(actions[i]); } } if (rules != null) { for (int i = 0; i < rules.length; i++) { builder.append(rules[i]); } } return builder.toString(); } public void addComment(String comment) { comments.add(comment); } public String[] getComments() { return comments.toArray(new String[comments.size()]); } public String toEbnf() { StringBuilder buff = new StringBuilder(); if (hasRules()) { IRule[] rules = getRules(); for (IRule rule : rules) { buff.append(rule.toEbnf()).append("\n"); } } return buff.toString(); } }