package org.modeldriven.fuml.io;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.modeldriven.fuml.assembly.ElementAssemblerEventListener;
import org.modeldriven.fuml.assembly.ElementGraphAssembler;
import org.modeldriven.fuml.assembly.ElementStubAssembler;
import org.modeldriven.fuml.xmi.XmiChildFinder;
import org.modeldriven.fuml.xmi.stream.StreamNodeEvent;
import org.modeldriven.fuml.xmi.stream.StreamNodeListener;
import org.modeldriven.fuml.xmi.validation.ErrorCode;
import org.modeldriven.fuml.xmi.validation.ValidationError;
import org.modeldriven.fuml.xmi.validation.ValidationErrorCollector;
import org.modeldriven.fuml.xmi.validation.ValidationEventListener;
public class IncrementalElementReader extends ElementReader
implements StreamNodeListener
{
private static Log log = LogFactory.getLog(IncrementalElementReader.class);
private static String STREAM_ELEMENT_NAME_PACKAGED_ELEMENT = "packagedElement";
public IncrementalElementReader() {
}
public String[] getElementNames()
{
return new String[] {
STREAM_ELEMENT_NAME_PACKAGED_ELEMENT
};
}
public void nodeCreated(StreamNodeEvent event) {
// not interested
}
public void nodeCompleted(StreamNodeEvent event) {
if (log.isDebugEnabled())
log.debug("nodeCompleted '" + event.getSource().getLocalName()
+ "'");
XmiChildFinder childFinder = new XmiChildFinder(
event.getSource().getLocalName());
event.getSource().accept(childFinder);
if (childFinder.getResult() != null) {
if (log.isDebugEnabled())
log.debug("ignoring element '" + event.getSource().getLocalName()
+ "' as non-atomic packaged element");
return; // has a descendant with our local name. We want "atomic" packaged elements
}
// FIXME: make finder namespace aware
if (!event.getSource().getContext().getUmlNamespace().getNamespaceURI().equals(
event.getSource().getNamespaceURI()))
return; // not in UML namespace
if (log.isDebugEnabled())
log.debug("validating: "
+ event.getSource().getXmiType() + "("
+ event.getSource().getXmiId() + ")");
ValidationErrorCollector errorCollector = new ValidationErrorCollector(
event.getSource());
errorCollector.setValidateExternalReferences(this.validateExternalReferences);
if (this.validationEventListeners != null)
for (ValidationEventListener listener : this.validationEventListeners)
errorCollector.addEventListener(listener);
errorCollector.validate();
int count = errorCollector.getErrorCount();
if (count == 0) // valid
{
ElementGraphAssembler assembler =
new ElementGraphAssembler(event.getSource());
assembler.setAssembleExternalReferences(this.assembleExternalReferences);
if (this.elementAssemblerEventListeners != null)
for (ElementAssemblerEventListener listener : this.elementAssemblerEventListeners)
assembler.addEventListener(listener);
assembler.start();
assembler.clear();
freeNode(event);
}
else
{
// For packagedElement, allow internal (and external?) invalid reference errors, as these are to
// be resolved in a later traversal over the data. For errors other than these
// we create a stub element such that we can still reference it.
int internalCount = errorCollector.getErrorCount(ErrorCode.INVALID_REFERENCE);
if (count > internalCount) // invalid
{
if (log.isDebugEnabled())
log.debug("found invalid node: "
+ event.getSource().getXmiType() + " ("
+ event.getSource().getXmiId() + ")");
ElementStubAssembler stubAssembler =
new ElementStubAssembler(event.getSource());
if (this.elementAssemblerEventListeners != null)
for (ElementAssemblerEventListener listener : this.elementAssemblerEventListeners)
stubAssembler.addEventListener(listener);
stubAssembler.start();
for (ValidationError error : errorCollector.getErrors())
stubAssembler.addErrorText(error.getText());
freeNode(event);
}
else // unresolved references
if (log.isDebugEnabled())
log.debug("found node with unresolved references: "
+ event.getSource().getXmiType() + " ("
+ event.getSource().getXmiId() + ") - ignoring during incremental loading");
}
}
/**
* Free (clip) the given XMI sub-graph from the stream,
* so to recoup it's memory and keep processing
* @param event - the event containing the XMI node to remove
*/
private void freeNode(StreamNodeEvent event) {
if (event.getParent() != null)
if (!event.getParent().removeChild(event.getSource()))
throw new IOException("could not remove assembled node: "
+ event.getSource().getXmiType() + " ("
+ event.getSource().getXmiId() + ")");
}
}