/* * xtc - The eXTensible Compiler * Copyright (C) 2004-2007 Robert Grimm * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ package xtc.parser; import java.util.ArrayList; import java.util.List; import xtc.tree.Attribute; import xtc.tree.Comment; import xtc.tree.Node; import xtc.tree.Visitor; /** * Visitor to copy grammar nodes. This visitor makes deep copies of * grammars, modules, productions, and elements. Note that when * copying elements, this visitor must be invoked through the {@link * #copy(Element)} method. Further note that, if the element to be * copied contains a generic node value or a generic recursion value, * the element must also contain all bindings referenced by that value * element. Otherwise, an <code>IllegalArgumentException</code> is * signalled. * * @author Robert Grimm * @version $Revision: 1.51 $ */ public class Copier extends Visitor { /** The list of source bindings. */ protected List<Binding> source; /** The list of target bindings. */ protected List<Binding> target; /** Create a new copier. */ public Copier() { source = new ArrayList<Binding>(); target = new ArrayList<Binding>(); } /** * Match the specified binding with its copy. * * @param b The binding. * @return The corresponding copy. * @throws IllegalArgumentException Signals that the specified * binding has no copy. */ protected Binding match(Binding b) { final int size = source.size(); int idx = -1; for (int i=0; i<size; i++) { if (b == source.get(i)) { idx = i; break; } } if (-1 == idx) { throw new IllegalArgumentException("Copying element without binding for " + b.name); } return target.get(idx); } /** * Patch the specified bindings. This method replaces the source * bindings in the specified list of bindings with the corresponding * copies, thus ensuring that the list contains previously copied * bindings. * * @param bindings The bindings to patch. */ protected void patch(List<Binding> bindings) { final int size = bindings.size(); for (int i=0; i<size; i++) { bindings.set(i, match(bindings.get(i))); } } /** * Copy the specified element. * * @param e The element. * @return A deep copy. * @throws IllegalArgumentException * Signals that the specified element is incomplete. */ @SuppressWarnings("unchecked") public <T extends Element> T copy(T e) { // Clear the lists of bindings. source.clear(); target.clear(); return (T)dispatch(e); } /** Copy the specified grammar. */ public Grammar visit(Grammar g) { Grammar copy = new Grammar(new ArrayList<Module>(g.modules.size())); copy.setLocation(g); for (Module m : g.modules) { copy.modules.add((Module)dispatch(m)); } return copy; } /** Copy the specified module. */ public Module visit(Module m) { Module copy = new Module(); copy.setLocation(m); copy.documentation = (Comment)dispatch(m.documentation); copy.name = m.name; copy.parameters = (ModuleList)dispatch(m.parameters); if (null != m.dependencies) { copy.dependencies = new ArrayList<ModuleDependency>(m.dependencies.size()); for (ModuleDependency dep : m.dependencies) { copy.dependencies.add((ModuleDependency)dispatch(dep)); } } copy.modification = m.modification; copy.header = (Action)dispatch(m.header); copy.body = (Action)dispatch(m.body); copy.footer = (Action)dispatch(m.footer); if (null != m.attributes) { copy.attributes = new ArrayList<Attribute>(m.attributes); } copy.productions = new ArrayList<Production>(m.productions.size()); for (Production p : m.productions) { copy.productions.add((Production)dispatch(p)); } return copy; } /** Copy the specified comment. */ public Comment visit(Comment c) { Node node = (Node)dispatch(c.getNode()); Comment copy = new Comment(c.kind, new ArrayList<String>(c.text), node); copy.setLocation(c); return copy; } /** Copy the specified module import declaration. */ public ModuleImport visit(ModuleImport i) { ModuleImport copy = new ModuleImport(i.module, (ModuleList)dispatch(i.arguments), i.target); copy.setLocation(i); return copy; } /** Copy the specified module instantiation declaration. */ public ModuleInstantiation visit(ModuleInstantiation i) { ModuleInstantiation copy = new ModuleInstantiation(i.module, (ModuleList)dispatch(i.arguments), i.target); copy.setLocation(i); return copy; } /** Copy the specified module modification. */ public ModuleModification visit(ModuleModification m) { ModuleModification copy = new ModuleModification(m.module, (ModuleList)dispatch(m.arguments), m.target); copy.setLocation(m); return copy; } /** Copy the specified module list. */ public ModuleList visit(ModuleList l) { ModuleList copy = new ModuleList(new ArrayList<ModuleName>(l.names)); copy.setLocation(l); return copy; } /** Copy the specified full production. */ public FullProduction visit(FullProduction p) { FullProduction copy = new FullProduction(null, p.type, p.name, p.qName, copy(p.choice)); copy.setLocation(p); if (null != p.attributes) { copy.attributes = new ArrayList<Attribute>(p.attributes); } copy.dType = p.dType; return copy; } /** Copy the specified alternative addition. */ public AlternativeAddition visit(AlternativeAddition p) { AlternativeAddition copy = new AlternativeAddition(p.dType, p.name, copy(p.choice), p.sequence, p.isBefore); copy.setLocation(p); copy.type = p.type; copy.qName = p.qName; return copy; } /** Copy the specified alternative removal. */ public AlternativeRemoval visit(AlternativeRemoval p) { AlternativeRemoval copy = new AlternativeRemoval(p.dType, p.name, new ArrayList<SequenceName>(p.sequences)); copy.setLocation(p); copy.type = p.type; copy.qName = p.qName; return copy; } /** Copy the specified production override. */ public ProductionOverride visit(ProductionOverride p) { ProductionOverride copy = new ProductionOverride(p.dType, p.name, copy(p.choice), p.isComplete); copy.setLocation(p); if (null != p.attributes) { copy.attributes = new ArrayList<Attribute>(p.attributes); } copy.type = p.type; copy.qName = p.qName; return copy; } /** Copy the specified ordered choice. */ public OrderedChoice visit(OrderedChoice c) { final int length = c.alternatives.size(); OrderedChoice copy = new OrderedChoice(new ArrayList<Sequence>(length)); copy.setLocation(c); for (Sequence alt : c.alternatives) { copy.alternatives.add((Sequence)dispatch(alt)); } return copy; } /** Copy the specified repetition. */ public Repetition visit(Repetition r) { Repetition copy = new Repetition(r.once, (Element)dispatch(r.element)); copy.setLocation(r); return copy; } /** Copy the specified option. */ public Option visit(Option o) { Option copy = new Option((Element)dispatch(o.element)); copy.setLocation(o); return copy; } /** Copy the specified sequence. */ public Sequence visit(Sequence s) { final int size = s.size(); Sequence copy = new Sequence(s.name, new ArrayList<Element>(size)); copy.setLocation(s); for (int i=0; i<size; i++) { copy.add((Element)dispatch(s.get(i))); } return copy; } /** Copy the specified followed-by predicate. */ public FollowedBy visit(FollowedBy p) { FollowedBy copy = new FollowedBy((Element)dispatch(p.element)); copy.setLocation(p); return copy; } /** Copy the specified not-followed-by predicate. */ public NotFollowedBy visit(NotFollowedBy p) { NotFollowedBy copy = new NotFollowedBy((Element)dispatch(p.element)); copy.setLocation(p); return copy; } /** Copy the specified semantic predicate. */ public SemanticPredicate visit(SemanticPredicate p) { SemanticPredicate copy = new SemanticPredicate((Action)dispatch(p.element)); copy.setLocation(p); return copy; } /** Copy the specified voided element. */ public VoidedElement visit(VoidedElement v) { VoidedElement copy = new VoidedElement((Element)dispatch(v.element)); copy.setLocation(v); return copy; } /** Copy the specified binding. */ public Binding visit(Binding b) { Binding copy = new Binding(b.name, (Element)dispatch(b.element)); copy.setLocation(b); source.add(b); target.add(copy); return copy; } /** Copy the specified string match. */ public StringMatch visit(StringMatch m) { StringMatch copy = new StringMatch(m.text, (Element)dispatch(m.element)); copy.setLocation(copy); return copy; } /** Copy the specified character class. */ public CharClass visit(CharClass c) { CharClass copy = new CharClass(c.exclusive, new ArrayList<CharRange>(c.ranges.size())); copy.setLocation(c); copy.ranges.addAll(c.ranges); return copy; } /** Copy the specified character case. */ public CharCase visit(CharCase c) { CharCase copy = new CharCase((CharClass)dispatch(c.klass), (Element)dispatch(c.element)); copy.setLocation(c); return copy; } /** Copy the specified character switch. */ public CharSwitch visit(CharSwitch s) { final int length = s.cases.size(); CharSwitch copy = new CharSwitch(new ArrayList<CharCase>(length)); copy.setLocation(s); for (CharCase kase : s.cases) { copy.cases.add((CharCase)dispatch(kase)); } copy.base = (Element)dispatch(s.base); return copy; } /** Copy the specified action. */ public Action visit(Action a) { Action copy = new Action(new ArrayList<String>(a.code), new ArrayList<Integer>(a.indent)); copy.setLocation(a); return copy; } /** Copy the specified parser action. */ public ParserAction visit(ParserAction pa) { ParserAction copy = new ParserAction((Action)dispatch(pa.element)); copy.setLocation(pa); return copy; } /** Copy the specified parse tree node. */ public ParseTreeNode visit(ParseTreeNode n) { ParseTreeNode copy = new ParseTreeNode(new ArrayList<Binding>(n.predecessors), null, new ArrayList<Binding>(n.successors)); copy.setLocation(n); patch(copy.predecessors); if (null != n.node) copy.node = match(n.node); patch(copy.successors); // Done. return copy; } /** Copy the specified binding value. */ public BindingValue visit(BindingValue v) { BindingValue copy = new BindingValue(match(v.binding)); copy.setLocation(v); // Done. return copy; } /** Copy the specified proper list value. */ public ProperListValue visit(ProperListValue v) { ProperListValue copy = new ProperListValue(v.type, new ArrayList<Binding>(v.elements), null); copy.setLocation(v); patch(copy.elements); if (null != v.tail) copy.tail = match(v.tail); // Done. return copy; } /** Copy the specified action base value. */ public ActionBaseValue visit(ActionBaseValue v) { ActionBaseValue copy = new ActionBaseValue(match(v.list), match(v.seed)); copy.setLocation(v); // Done. return copy; } /** Copy the specified generic node value. */ public GenericNodeValue visit(GenericNodeValue v) { GenericNodeValue copy = new GenericNodeValue(v.name, new ArrayList<Binding>(v.children), new ArrayList<Binding>(v.formatting)); copy.setLocation(v); patch(copy.children); patch(copy.formatting); // Done. return copy; } /** Copy the specified generic action value. */ public GenericActionValue visit(GenericActionValue v) { GenericActionValue copy = new GenericActionValue(v.name, v.first, new ArrayList<Binding>(v.children), new ArrayList<Binding>(v.formatting)); copy.setLocation(v); patch(copy.children); patch(copy.formatting); // Done. return copy; } /** Copy the specified generic recursion value. */ public GenericRecursionValue visit(GenericRecursionValue v) { GenericRecursionValue copy = new GenericRecursionValue(v.name, v.first, new ArrayList<Binding>(v.children), new ArrayList<Binding>(v.formatting), match(v.list)); copy.setLocation(v); patch(copy.children); patch(copy.formatting); // Done. return copy; } /** * Visit the specified element. This method provides the default * implementation for nonterminals, terminals (besides character * classes and switches), node markers, null literals, and value * elements (besides properlists, generic node, generic action, and * generic recursion values), which are immutable and not containers * for other elements. */ public Element visit(Element e) { return e; } }