try {
XMLPersistenceHelper.checkTag(element, GemPersistenceConstants.TABLETOP_TAG);
} catch (BadXMLDocumentException bxde) {
loadStatus.add(new Status(Status.Severity.ERROR, GemCutter.getResourceString("SOM_TableTopLoadFailure"), bxde));
}
// Make sure the tabletop is clear before we continue further.
blankTableTop();
// Temporarily set the target's name to something invalid, to avoid name collisions with any gems being loaded.
gemGraph.getTargetCollector().setName("!tempTargetLoadName!");
// Also disable automatic gem graph reflector updating.
gemGraph.setArgumentUpdatingDisabled(true);
// The set of loaded collector gems.
Set<Gem> collectorGemSet = new HashSet<Gem>();
// We instantiate emitters last, as they cannot be instantiated without their associated collectors.
Set<Element> emitterElements = new HashSet<Element>();
// A new context in which the gems are loaded.
GemContext gemContext = new GemContext();
Argument.LoadInfo loadInfo = new Argument.LoadInfo();
// Set of IDs of gem elements which couldn't be instantiated
Set<String> unknownGemIds = new HashSet<String>();
try {
// Instantiate displayed gem children, except for emitters (which need collectors to be instantiated first)
boolean targetWasLoaded = false;
Node childNode;
for (childNode = element.getFirstChild(); isDisplayedGemElement(childNode); childNode = childNode.getNextSibling()) {
Element displayedGemElement = (Element)childNode;
// Get the gem node.
Node gemNode = displayedGemElement.getFirstChild();
try {
XMLPersistenceHelper.checkIsElement(gemNode);
} catch (BadXMLDocumentException e) {
loadStatus.add(new Status(Status.Severity.ERROR, GemCutter.getResourceString("SOM_ErrorLoadingFromNode") + gemNode.getLocalName(), e));
}
// instantiate gem node
Class<?> gemClass = getGemClass((Element)gemNode);
if (gemClass == null) {
// unrecognized type
String gemId = Gem.getGemId(displayedGemElement);
if (gemId != null) {
unknownGemIds.add(gemId);
}
loadStatus.add(new Status(Status.Severity.WARNING, GemCutter.getResourceString("SOM_UnrecognizedGemType") + displayedGemElement.getLocalName(), null));
} else if (gemClass == ReflectorGem.class) {
// emitter gem - save for later
emitterElements.add(displayedGemElement);
} else {
// a gem which we should instantiate now. Do this if possible.
DisplayedGem displayedGem = getDisplayedGem(displayedGemElement, gemContext, loadInfo, perspective, loadStatus);
if (displayedGem == null) {
// Couldn't instantiate.
String gemId = Gem.getGemId(displayedGemElement);
if (gemId != null) {
unknownGemIds.add(gemId);
}
continue;
}
Gem gem = displayedGem.getGem();
if (!targetWasLoaded && gem instanceof CollectorGem) {
// HACK(?): reassign things so the gem graph target looks like the loaded target.
CollectorGem newTarget = (CollectorGem)gem;
CollectorGem gemGraphTarget = gemGraph.getTargetCollector();
gemGraphTarget.setName(newTarget.getUnqualifiedName());
targetDisplayedCollector.setLocation(displayedGem.getLocation());
gemContext.addGem(gemGraphTarget, gemContext.getIdentifier(newTarget, false));
loadInfo.remapGem(newTarget, gemGraphTarget);
targetWasLoaded = true;
} else {
// Add the gem to the tabletop
addGem(displayedGem, displayedGem.getLocation());
}
// Add to the collector map if it's a collector
if (gem instanceof CollectorGem) {
collectorGemSet.add(gem);
}
}
}
if (!targetWasLoaded) {
loadStatus.add(new Status(Status.Severity.ERROR, GemCutter.getResourceString("SOM_TargetDidntLoad"), null));
gemGraph.getTargetCollector().setName("target"); // Just set to something valid..
}
// Instantiate displayed emitters.
for (final Element emitterElement : emitterElements) {
DisplayedGem displayedGem = getDisplayedGem(emitterElement, gemContext, loadInfo, perspective, loadStatus);
if (displayedGem == null) {
// Couldn't instantiate.
String gemId = Gem.getGemId(emitterElement);
if (gemId != null) {
unknownGemIds.add(gemId);
}
continue;
}
// Add the gem to the tabletop
addGem(displayedGem, displayedGem.getLocation());
}
// update collector and reflector argument info.
Set<CollectorGem> collectorSet = gemGraph.getCollectors();
for (final CollectorGem collectorGem : collectorSet) {
try {
collectorGem.loadArguments(loadInfo, gemContext);
} catch (BadXMLDocumentException bdxe) {
loadStatus.add(new Status(Status.Severity.WARNING, GemCutter.getResourceString("SOM_ErrorLoadingCollector"), bdxe));
}
}
for (final Gem gem : gemGraph.getGems()) {
if (gem instanceof ReflectorGem) {
try {
((ReflectorGem)gem).loadArguments(loadInfo, gemContext);
} catch (BadXMLDocumentException bdxe) {
loadStatus.add(new Status(Status.Severity.WARNING, GemCutter.getResourceString("SOM_ErrorLoadingEmitter"), bdxe));
}
}
}
// Instantiate connections.
while (childNode != null && childNode instanceof Element && Connection.isConnectionElement((Element)childNode)) {
Element connectionElement = (Element)childNode;
// Make the connection. Skip connecting unknown gems.
Connection connection = null;
try {
connection = Connection.elementToConnection(connectionElement, gemContext, unknownGemIds);
} catch (BadXMLDocumentException bxde) {
loadStatus.add(new Status(Status.Severity.ERROR, GemCutter.getResourceString("SOM_CouldntMakeConnection"), bxde));
}
if (connection != null) {
Gem.PartOutput fromPart = connection.getSource();
Gem.PartInput toPart = connection.getDestination();
if (fromPart == null || toPart == null) {
loadStatus.add(new Status(Status.Severity.ERROR, GemCutter.getResourceString("CouldntMakeConnection"), null));
} else {
// Attempt the connection
try {
connect(connection);
} catch (Exception e) {
loadStatus.add(new Status(Status.Severity.ERROR, GemCutter.getResourceString("CouldntMakeConnection"), e));
}
}
}
// get the next element
childNode = childNode.getNextSibling();
}
// Make sure the inputs targets actually make sense.
gemGraph.validateInputTargets();
// Update reflectors.
for (final CollectorGem gem : collectorSet) {
gem.updateReflectedInputs();
}
// Now that we have instantiated as much of the gem graph as we can,
// carry out some validation to ensure that the current gem graph is valid.
boolean gemGraphValid = true;
try {
gemGraph.typeGemGraph(getTypeCheckInfo());
} catch (TypeException te) {
gemGraphValid = false;
loadStatus.add(new Status(Status.Severity.ERROR, GemCutter.getResourceString("SOM_InvalidGemGraph"), null));
}
// If the gem graph is not valid, disconnect value gems, since they are a big culprit.
if (!gemGraphValid) {
// The biggest culprit is value gems. Disconnect them all.
for (final Connection connection : gemGraph.getConnections()) {
if (connection.getSource().getGem() instanceof ValueGem) {
disconnect(connection);
}
}
// Make sure the inputs targets actually make sense.
gemGraph.validateInputTargets();
// Update reflectors.
for (final CollectorGem gem : collectorSet) {
gem.updateReflectedInputs();
}
// try again..
gemGraphValid = true;
try {
gemGraph.typeGemGraph(getTypeCheckInfo());
} catch (TypeException te) {
gemGraphValid = false;
}
}
// If the gem graph is still not valid, disconnect the remaining connections.
// This should almost always work.
if (!gemGraphValid) {
// Disconnect remaining connections.
for (final Connection connection : gemGraph.getConnections()) {
disconnect(connection);
}
// Make sure the inputs targets actually make sense.
gemGraph.validateInputTargets();
// Update reflectors.
for (final CollectorGem gem : collectorSet) {
gem.updateReflectedInputs();
}
try {
// Type check..
gemGraph.typeGemGraph(getTypeCheckInfo());
} catch (TypeException e) {
GemCutter.CLIENT_LOGGER.log(Level.SEVERE, GemCutter.getResourceString("SOM_CantConstructLoadState"));
doNewTableTopUserAction();
}
}
// sanity test..
if (GemCutterPersistenceHelper.isDisplayedGemElement(childNode)) {
BadXMLDocumentException bxde = new BadXMLDocumentException(childNode, "Gem elements must appear before connection elements.");
loadStatus.add(new Status(Status.Severity.ERROR, GemCutter.getResourceString("SOM_InvalidXMLstructure"), bxde));
}
} finally {
// Try to recover from unexpected exceptions..