/* * Copyright 2007-2013 * Licensed under GNU Lesser General Public License * * This file is part of EpochX: genetic programming software for research * * EpochX is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * EpochX is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with EpochX. If not, see <http://www.gnu.org/licenses/>. * * The latest version is available from: http://www.epochx.org */ package org.epochx.grammar; import java.util.*; /** * One of potentially multiple options that a <code>GrammarRule</code> can * resolve to. It contains a sequence of one or more <code>GrammarNode</code>s * that together produce a valid mapping for the left-hand side of a grammar * rule as represented by <code>GrammarRule</code> instances. */ public class GrammarProduction implements Cloneable { // The grammar rules and literals in order that make up this production. private final List<GrammarNode> grammarNodes; // Any key/value attributes which exist upon this production. private final Map<String, Object> attributes; /** * Constructs a production around the specified sequence of * <code>GrammarNode</code>s. * * @param grammarNodes a <code>List</code> of <code>GrammarNode</code>s that * provides the mapping sequence for this production. */ public GrammarProduction(List<GrammarNode> grammarNodes) { this.grammarNodes = grammarNodes; attributes = new HashMap<String, Object>(); } /** * Constructs a production with no <code>GrammarNode</code>s. A valid * production should have one or more GrammarNodes so one or more should be * added to this <code>GrammarProduction</code> before use. * * @see GrammarProduction#addGrammarNode(GrammarNode) */ public GrammarProduction() { this(new ArrayList<GrammarNode>()); } /** * Appends the specified symbol to the list of <code>GrammarNodes</code> in * this production. * * @param node the rule or literal to be appended to this production. * @see GrammarProduction#setGrammarNode(int, GrammarNode) */ public void addGrammarNode(GrammarNode node) { grammarNodes.add(node); } /** * Returns a list of the rules and literals that make up this production, in * the order that they resolve to. * * @return the sequence of <code>GrammarNode</code>s that make up this * production. */ public List<GrammarNode> getGrammarNodes() { return grammarNodes; } /** * Returns the <code>GrammarNode</code> at the specified index in the * sequence of rules and literals in this production. * * @return the <code>GrammarNode</code> at the specified index in this * production. */ public GrammarNode getGrammarNode(int index) { return grammarNodes.get(index); } /** * Set the <code>GrammarNode</code> at the specified index, overwriting the * current occupant. * * @param index the index of the current <code>GrammarNode</code> to be * replaced. * @param node the new <code>GrammarNode</code> to place at the specified * index in this production. * @see GrammarProduction#addGrammarNode(GrammarNode) */ public void setGrammarNode(int index, GrammarNode node) { grammarNodes.set(index, node); } /** * Inserts the <code>GrammarNode</code> at the specified index. Shifts the * node currently at that position along one, with their indices incremented * by one. * * @param index the index of where to insert the <code>GrammarNode</code>. * @param node the <code>GrammarNode</code> to place at the specified * index in this production. */ public void addGrammarNode(int index, GrammarNode node) { grammarNodes.add(index, node); } /** * Returns the quantity of <code>GrammarNode</code>s in this production. * * @return the number of <code>GrammarNode</code>s in this production. */ public int getNoGrammarNodes() { return grammarNodes.size(); } /** * Retrieves the value of the attribute with the given key. If no attribute * with that key exists then <code>null</code> is returned. * * Currently all values that were parsed from a String grammar will be * returned here as <code>String</code> objects. In future versions this may * change to parse the values into more suitable Object types. * * @param key the key that identifies the attribute value to return. * @return the Object value of the attribute with the given key, or * <code>null</code> if no attributes with that key exist on this * <code>GrammarProduction</code>. */ public Object getAttribute(String key) { return attributes.get(key); } /** * Sets the value of the attribute with the given key. If no attribute with * that key exists then a new one will be created, if one does exist then it * will be overwritten with the new value. * * @param key the unique key of the attribute to set. * @param value an value of any Object type to store as an attribute under * the given key. */ public void setAttribute(String key, Object value) { attributes.put(key, value); } /** * Retrieve the <code>Set</code> of attribute keys that have been set for * this production. * * @return a <code>Set</code> of all attribute keys that have been set for * this production. If no attributes have been set then an empty set * will be * returned. */ public Set<String> getAttributeKeys() { return attributes.keySet(); } /** * Calculates and returns the minimum depth required to resolve this production * to all <code>GrammarLiteral</code> nodes. The minimum depth of a * production is the largest of all the minimum depths of its * <code>GrammarNodes</code>. * * @return the minimum depth required to resolve to all * <code>GrammarLiteral</code> nodes. */ public int getMinDepth() { int max = 0; for (GrammarNode s: grammarNodes) { int d = 0; if (s instanceof GrammarRule) { d = ((GrammarRule) s).getMinDepth(); } if (d > max) { max = d; } } return max; } /** * Determines whether this production is recursive. A production is * recursive if any of its nodes that are rules (rather than literals) are * recursive. * * @return true if this production is has a <code>GrammarRule</code> that is * recursive and false otherwise. */ public boolean isRecursive() { boolean recursive = false; for (GrammarNode s: grammarNodes) { if (s instanceof GrammarRule) { recursive = ((GrammarRule) s).isRecursive(); } if (recursive) { break; } } return recursive; } /** * Returns a copy of this production which is a new instance of * <code>GrammarProduction</code> with the same set of grammar nodes and * attributes. * * @return a <code>GrammarProduction</code> which is a copy of this * production. */ @Override public GrammarProduction clone() { GrammarProduction clone = null; try { clone = (GrammarProduction) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } // Shallow copy the grammar nodes. clone.grammarNodes.addAll(grammarNodes); // Shallow copy the attributes. clone.attributes.putAll(attributes); return clone; } /** * Returns a string representation of this production and its grammar nodes. * * @return a string representation of this production. */ @Override public String toString() { StringBuilder buffer = new StringBuilder(); for (GrammarNode s: grammarNodes) { if (s instanceof GrammarLiteral) { buffer.append(((GrammarLiteral) s).toString()); } if (s instanceof GrammarRule) { buffer.append('<'); buffer.append(((GrammarRule) s).getName()); buffer.append('>'); } buffer.append(' '); } // Append any attributes. if (attributes.size() > 0) { buffer.append("<?"); Set<String> keys = attributes.keySet(); int i = 0; for (String k: keys) { if (i != 0) { buffer.append(';'); } buffer.append(k); buffer.append('='); buffer.append(attributes.get(k)); i++; } buffer.append("?>"); } return buffer.toString(); } }