/** * 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.regex.dfa; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import com.aptana.ide.regex.IRegexRunner; import com.aptana.ide.regex.IRegexRunnerNode; import com.aptana.ide.regex.sets.CharacterSet; import com.aptana.ide.regex.sets.NumberSet; /** * @author Kevin Lindsey */ public class DFAGraph implements IRegexRunner { /* * Fields */ private IRegexRunnerNode[] _nodes; private int _acceptState; /** * temp */ public CharacterSet _transitionSet; /* * Properties */ /** * Get the node associated with the given index * * @param index * The index of the node to return * @return The node at the specified index */ public IRegexRunnerNode getItem(int index) { return this._nodes[index]; } /** * Returns the number of nodes in this graph * * @return The number of nodes in this graph */ public int getSize() { return this._nodes.length; } /* * Constructors */ /** * Create a new instance DFAGraph */ public DFAGraph() { this._transitionSet = null; this._nodes = new DFANode[0]; } /* * Methods */ /** * Add a node to this graph * * @param node * The node to add to this graph * @return The index of the node in this graph */ public int add(DFANode node) { int length = this._nodes.length; DFANode[] newNodes = new DFANode[length + 1]; // copy array System.arraycopy(this._nodes, 0, newNodes, 0, length); // add node newNodes[length] = node; // assign new array this._nodes = newNodes; return length; } /** * Create a new node based on the given set and add it to this graph * * @param inputs * The set to associate with the new node * @return The index of the new node in this graph */ public int add(NumberSet inputs) { return this.add(new DFANode(inputs)); } // /** // * Try to match this DFA against the specified string // * // * @param source // * The string to run this DFA on // * @return The accept state value. This will be -1 if there was no match // */ // public int match(String source) // { // return this.match(source.toCharArray(), 0); // } // // /** // * Try to match this DFA against the specified string // * // * @param source // * The character array to run this DFA on // * @return The accept state value. This will be -1 if there was no match // */ // public int match(char[] source) // { // return this.match(source, 0, source.length); // } // // /** // * Try to match this DFA against the specified string starting at the given index // * // * @param source // * @param startPosition // * @return The accept state value. This will be -1 if there was no match // */ // public int match(char[] source, int startPosition) // { // return this.match(source, startPosition, source.length); // } /* * IRegexRunner implementation */ /** * @see com.aptana.ide.regex.IRegexRunner#getAcceptState() */ public int getAcceptState() { return this._acceptState; } /** * @see com.aptana.ide.regex.IRegexRunner#match(java.lang.String, int, int) */ public int match(String source, int startPosition, int endPosition) { return this.match(source.toCharArray(), startPosition, endPosition); } /** * @see com.aptana.ide.regex.IRegexRunner#match(char[], int, int) */ public int match(char[] source, int startPosition, int endPosition) { int currentState = 0; this._acceptState = this._nodes[0].getAcceptState(); int lastAccept = (this._acceptState == -1) ? -2 : startPosition; for (int i = startPosition; i < endPosition; i++) { int index = this._transitionSet.inputIndex(source[i]); if (index == -1) { // this character does not exist as an input break; } else { IRegexRunnerNode move = this._nodes[currentState]; if (move != null) { int nextState = move.getItem(index); if (nextState != -1) { // advance to next sate currentState = nextState; int acceptState = this._nodes[currentState].getAcceptState(); // remember this position, if this is an accept state if (acceptState != -1) { lastAccept = i; this._acceptState = acceptState; } } else { // no transition in this state for this input break; } } else { // throw new Exception("internal inconsistency"); } } } return lastAccept + 1; } /** * read * * @param input * @throws IOException */ public void read(DataInput input) throws IOException { // read transition set this._transitionSet = new CharacterSet(); this._transitionSet.setMembers(input.readUTF()); // read node count int nodeCount = input.readInt(); // read nodes this._nodes = new IRegexRunnerNode[nodeCount]; for (int i = 0; i < nodeCount; i++) { IRegexRunnerNode node = new DFANode(); node.read(input); this._nodes[i] = node; } } /** * write * * @param output * @throws IOException */ public void write(DataOutput output) throws IOException { // write transition set String members = new String(this._transitionSet.getMembers()); output.writeUTF(members); // write node count int nodeCount = this._nodes.length; output.writeInt(nodeCount); // write nodes for (int i = 0; i < nodeCount; i++) { this._nodes[i].write(output); } } // /** // * toAssembly // * // * @param className // * @return String // */ // public String toAssembly(String className) // { // SourceWriter writer = new SourceWriter(); // // // declare class // writer.printlnWithIndent(".class public " + className); // writer.printlnWithIndent(".super java/lang/Object"); // writer.printlnWithIndent(".implements com/aptana/ide/regex/IRegexRunner"); // writer.println(); // // // define fields // writer.printlnWithIndent(".field private _acceptState I"); // writer.println(); // // // define constructor // writer.printlnWithIndent(".method public <init>()V"); // writer.increaseIndent(); // writer.printlnWithIndent("aload_0"); // writer.printlnWithIndent("invokenonvirtual java/lang/Object/<init>()V"); // writer.printlnWithIndent("return"); // writer.decreaseIndent(); // writer.printlnWithIndent(".end method"); // writer.println(); // // // define getAcceptState // writer.printlnWithIndent(".method public getAcceptState()I"); // writer.increaseIndent(); // writer.printlnWithIndent("aload_0"); // writer.printlnWithIndent("getfield " + className + "/_acceptState I"); // writer.printlnWithIndent("return"); // writer.decreaseIndent(); // writer.printlnWithIndent(".end method"); // writer.println(); // // // define setGroup // writer.printlnWithIndent(".method public setGroup(Ljava/lang/String;)V"); // writer.increaseIndent(); // writer.printlnWithIndent("return"); // writer.printlnWithIndent(".limit stack 1"); // writer.printlnWithIndent(".limit locals 2"); // writer.decreaseIndent(); // writer.printlnWithIndent(".end method"); // writer.println(); // // // output match(char[], int, int)I // writer.printlnWithIndent(".method public match([CII)I"); // writer.increaseIndent(); // writer.printlnWithIndent(".limit locals 6"); // // for (int i = 0; i < this._nodes.length; i++) // { // DFANode node = (DFANode) this._nodes[i]; // // writer.printlnWithIndent("Label" + i + ":"); // node.toAssembly(writer, "Label", "ReturnNoMatch"); // } // // writer.printlnWithIndent("ReturnNoMatch:"); // writer.increaseIndent(); // writer.printlnWithIndent("bipush -1"); // writer.printlnWithIndent("return"); // writer.decreaseIndent(); // writer.decreaseIndent(); // writer.printlnWithIndent(".end method"); // writer.println(); // // // define read // writer.printlnWithIndent(".method public read(Ljava/io/DataInput;)V"); // writer.increaseIndent(); // writer.printlnWithIndent(".limit stack 2"); // writer.printlnWithIndent(".limit locals 1"); // writer.printlnWithIndent("return"); // writer.decreaseIndent(); // writer.printlnWithIndent(".end method"); // writer.println(); // // // define write // writer.printlnWithIndent(".method public write(Ljava/io/DataOutput;)V"); // writer.increaseIndent(); // writer.printlnWithIndent(".limit stack 2"); // writer.printlnWithIndent(".limit locals 1"); // writer.printlnWithIndent("return"); // writer.decreaseIndent(); // writer.printlnWithIndent(".end method"); // writer.println(); // // return writer.toString(); // } }