/*
 * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.tools.internal.xjc.reader.gbind;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

Expression that represents an alphabet of a regular language.

Since this package is about a regular expression over element declarations, this represents an XML element declaration (hence the name.) Element needs to be interned, meaning one Element per one tag name.

Implements ElementSet to represent a self.

Author:Kohsuke Kawaguchi
/** * {@link Expression} that represents an alphabet of a regular language. * * <p> * Since this package is about a regular expression over element declarations, * this represents an XML element declaration (hence the name.) * * Element needs to be interned, meaning one {@link Element} per one tag name. * * <p> * Implements {@link ElementSet} to represent a self. * * @author Kohsuke Kawaguchi */
public abstract class Element extends Expression implements ElementSet {
Once we build a graph from Expression, we represent an edge e1 -> e2 by e1.foreEdges.contains(e2) and e2.backEdges.contains(e1).
/** * Once we build a graph from {@link Expression}, * we represent an edge e1 -> e2 by {@code e1.foreEdges.contains(e2)} * and {@code e2.backEdges.contains(e1)}. */
final Set<Element> foreEdges = new LinkedHashSet<Element>(); final Set<Element> backEdges = new LinkedHashSet<Element>();
Previous element in the DFS post-order traveral of the element graph.

We use prevPostOrder==null as a check if the element is visted in DFS, so this chain terminates by a self-reference, not by having null. Set in assignDfsPostOrder(Element)

/** * Previous element in the DFS post-order traveral * of the element graph. * * <p> * We use {@code prevPostOrder==null} as a check if the element is visted in DFS, * so this chain terminates by a self-reference, not by having null. * * Set in {@link #assignDfsPostOrder(Element)} */
/*package*/ Element prevPostOrder;
ConnectedComponent to which this element belongs. Set in #buildStronglyConnectedComponents(List<ConnectedComponent>)
/** * {@link ConnectedComponent} to which this element belongs. * * Set in {@link #buildStronglyConnectedComponents(List<ConnectedComponent>)} */
private ConnectedComponent cc; protected Element() { } ElementSet lastSet() { return this; } boolean isNullable() { return false; }
True if this Element is SourceNode.
/** * True if this {@link Element} is {@link SourceNode}. */
boolean isSource() { return false; }
True if this Element is SinkNode.
/** * True if this {@link Element} is {@link SinkNode}. */
boolean isSink() { return false; } void buildDAG(ElementSet incoming) { incoming.addNext(this); } public void addNext(Element element) { foreEdges.add(element); element.backEdges.add(this); } public boolean contains(ElementSet rhs) { return this==rhs || rhs==ElementSet.EMPTY_SET; }
Just to satisfy the ElementSet contract.
Deprecated: if you statically call this method, there's something wrong.
/** * Just to satisfy the {@link ElementSet} contract. * * @deprecated * if you statically call this method, there's something wrong. */
public Iterator<Element> iterator() { return Collections.singleton(this).iterator(); }
Traverses the Element graph with DFS and set prevPostOrder. Should be first invoked on the source node of the graph.
/** * Traverses the {@link Element} graph with DFS * and set {@link #prevPostOrder}. * * Should be first invoked on the source node of the graph. */
/*package*/ Element assignDfsPostOrder(Element prev) { if(prevPostOrder!=null) return prev; // already visited prevPostOrder = this; // set a dummy value to prepare for cycles for (Element next : foreEdges) { prev = next.assignDfsPostOrder(prev); } this.prevPostOrder = prev; // set to the real value return this; }
Builds a set of strongly connected components and puts them all into the given set.
/** * Builds a set of strongly connected components and puts them * all into the given set. */
public void buildStronglyConnectedComponents(List<ConnectedComponent> ccs) { // store visited elements - loop detection List<Element> visitedElements = new ArrayList<Element>(); for(Element cur=this; cur!=cur.prevPostOrder; cur=cur.prevPostOrder) { if(visitedElements.contains(cur)) { // if I've already processed cur element, I'm in a loop break; } else { visitedElements.add(cur); } if(cur.belongsToSCC()) continue; // start a new component ConnectedComponent cc = new ConnectedComponent(); ccs.add(cc); cur.formConnectedComponent(cc); } } private boolean belongsToSCC() { return cc!=null || isSource() || isSink(); }
Forms a strongly connected component by doing a reverse DFS.
/** * Forms a strongly connected component by doing a reverse DFS. */
private void formConnectedComponent(ConnectedComponent group) { if(belongsToSCC()) return; this.cc=group; group.add(this); for (Element prev : backEdges) prev.formConnectedComponent(group); } public boolean hasSelfLoop() { // if foreEdges have a loop, backEdges must have one. Or vice versa assert foreEdges.contains(this)==backEdges.contains(this); return foreEdges.contains(this); }
Checks if the given ConnectedComponent forms a cut-set of a graph.
Params:
  • visited – Used to keep track of visited nodes.
Returns: true if it is indeed a cut-set. false if not.
/** * Checks if the given {@link ConnectedComponent} forms a cut-set * of a graph. * * @param visited * Used to keep track of visited nodes. * @return * true if it is indeed a cut-set. false if not. */
/*package*/ final boolean checkCutSet(ConnectedComponent cc, Set<Element> visited) { assert belongsToSCC(); // SCC discomposition must be done first if(isSink()) // the definition of the cut set is that without those nodes // you can't reach from soruce to sink return false; if(!visited.add(this)) return true; if(this.cc==cc) return true; for (Element next : foreEdges) { if(!next.checkCutSet(cc,visited)) // we've found a path to the sink return false; } return true; } }