/** * This file Copyright (c) 2005-2008 Aptana, Inc. This program is * dual-licensed under both the Aptana Public License and the GNU General * Public license. You may elect to use one or the other of these licenses. * * This program is distributed in the hope that it will be useful, but * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or * NONINFRINGEMENT. Redistribution, except as permitted by whichever of * the GPL or APL you select, is prohibited. * * 1. For the GPL license (GPL), you can redistribute and/or modify this * program under the terms of the GNU General Public License, * Version 3, as published by the Free Software Foundation. You should * have received a copy of the GNU General Public License, Version 3 along * with this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Aptana provides a special exception to allow redistribution of this file * with certain other free and open source software ("FOSS") code and certain additional terms * pursuant to Section 7 of the GPL. You may view the exception and these * terms on the web at http://www.aptana.com/legal/gpl/. * * 2. For the Aptana Public License (APL), this program and the * accompanying materials are made available under the terms of the APL * v1.0 which accompanies this distribution, and is available at * http://www.aptana.com/legal/apl/. * * You may view the GPL, Aptana's exception and additional terms, and the * APL in the file titled license.html at the root of the corresponding * plugin containing this source file. * * Any modifications to this file must keep this entire header intact. */ package com.aptana.ide.parsing.bnf; import com.aptana.ide.io.SourceWriter; import com.aptana.ide.parsing.bnf.nodes.GrammarNode; import com.aptana.ide.parsing.bnf.nodes.IGrammarNode; import com.aptana.ide.parsing.bnf.nodes.GrammarNodeTypes; import com.aptana.ide.parsing.bnf.nodes.NonTerminalNode; import com.aptana.ide.parsing.bnf.nodes.ProductionNode; import com.aptana.ide.parsing.bnf.nodes.TerminalNode; /** * @author Kevin Lindsey */ public class Item { private ProductionNode _production; private IGrammarNode _definition; private int _position; /** * Item * * @param definition */ public Item(ProductionNode production) { this._production = production; IGrammarNode child = (IGrammarNode) this._production.getChild(0); if (child.getTypeIndex() == GrammarNodeTypes.SEQUENCE) { this._definition = child; } else { this._definition = production; } } /** * Clone and reset position * * @param item */ public Item(Item item) { this._production = item._production; this._definition = item._definition; this._position = item._position; } /** * @see java.lang.Object#equals(java.lang.Object) */ public boolean equals(Object obj) { boolean result = false; if (this == obj) { result = true; } else if (obj instanceof Item) { Item that = (Item) obj; result = (this._production == that._production && this._position == that._position); } return result; } /** * isAtNonTerminal * * @return boolean */ public boolean isAtNonTerminal() { IGrammarNode currentNode = this.getCurrentNode(); return (currentNode != null && currentNode instanceof NonTerminalNode); } /** * isInitialItem * * @return boolean */ public boolean isInitialItem() { return (this._position == 0); } /** * isCompletedItem * * @return boolean */ public boolean isCompletedItem() { return (this._position == this._definition.getChildCount()); } /** * advance */ public void advance() { if (this.isCompletedItem() == false) { this._position++; } } /** * getCurrentNode * * @return INode or null */ public IGrammarNode getCurrentNode() { IGrammarNode result = null; if (this.isCompletedItem() == false) { result = (IGrammarNode) this._definition.getChild(this._position); } return result; } /** * getFollow * * @param terminals */ public void getFollow(TerminalList terminals) { IGrammarNode currentNode = this.getCurrentNode(); this.advance(); if (this.isCompletedItem()) { currentNode.getOwningProduction().getFollow(terminals); } else { IGrammarNode sibling = this.getCurrentNode(); switch (sibling.getTypeIndex()) { case GrammarNodeTypes.TERMINAL: // 2a. terminals.add((TerminalNode) sibling); break; case GrammarNodeTypes.NONTERMINAL: NonTerminalNode nonTerminal = (NonTerminalNode) sibling; TerminalList first = new TerminalList(); // 2b. nonTerminal.getFirst(first); first.removeEpsilon(); terminals.add(first); // 2c. (B is nullable) if (nonTerminal.isNullable()) { sibling.getOwningProduction().getFollow(terminals); } break; default: break; } } } /** * @see java.lang.Object#hashCode() */ public int hashCode() { return this._production.hashCode() ^ this._position; } /** * getIndex * * @return */ public int getIndex() { GrammarNode grammar = this._production.getOwningGrammar(); int result = -1; for (int i = 0; i < grammar.getChildCount(); i++) { if (grammar.getChild(i) == this._production) { result = i; break; } } return result; } /** * getName * * @return */ public String getName() { return this._production.getName(); } /** * getPosition * * @return int */ public int getPosition() { return this._position; } /** * setPosition * * @param position */ public void setPosition(int position) { if (this._definition != null) { if (0 <= position && position <= this._definition.getChildCount()) { this._position = position; } } } /** * @see java.lang.Object#toString() */ public String toString() { SourceWriter writer = new SourceWriter(); writer.print(this._production.getName()).print(" : "); //$NON-NLS-1$ for (int i = 0; i < this._definition.getChildCount(); i++) { if (i == this._position) { writer.print(". "); //$NON-NLS-1$ } this._definition.getChild(i).getSource(writer); if (i < this._definition.getChildCount() - 1) { writer.print(" "); //$NON-NLS-1$ } } if (this._position == this._definition.getChildCount()) { writer.print("."); //$NON-NLS-1$ } return writer.toString(); } }