/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * GemCutterPersistenceManager.java * Creation date: Nov 7, 2003 * By: Frank Worsley */ package org.openquark.gems.client; import java.util.Set; import org.openquark.cal.compiler.AdjunctSource; import org.openquark.cal.compiler.CALSourceGenerator; import org.openquark.cal.compiler.CompilerMessageLogger; import org.openquark.cal.compiler.MessageLogger; import org.openquark.cal.compiler.ModuleName; import org.openquark.cal.compiler.QualifiedName; import org.openquark.cal.compiler.Scope; import org.openquark.cal.compiler.SourceModel; import org.openquark.cal.compiler.TypeExpr; import org.openquark.cal.metadata.FunctionMetadata; import org.openquark.cal.services.CALFeatureName; import org.openquark.cal.services.GemDesign; import org.openquark.cal.services.GemEntity; import org.openquark.cal.services.Status; import org.openquark.util.xml.BadXMLDocumentException; import org.openquark.util.xml.NamespaceInfo; import org.openquark.util.xml.XMLPersistenceHelper; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; /** * This class takes care of loading and saving GemCutter gem designs and definitions. * @author Frank Worsley */ public class GemCutterPersistenceManager { /** The GemCutter this manager is for. */ private final GemCutter gemCutter; /** * Constructor for a new GemCutterPersistenceManager. * @param gemCutter the GemCutter this manager is for */ GemCutterPersistenceManager(GemCutter gemCutter) { if (gemCutter == null) { throw new NullPointerException(); } this.gemCutter = gemCutter; } /** * Attach the saved form of this object as a child XML node. * @param parentNode the node that will be the parent of the generated XML. * The generated XML will be appended as a subtree of this node. * Note: parentNode must be a node type that can accept children (eg. an Element or a DocumentFragment) */ private void saveXML(Node parentNode) { Document document = (parentNode instanceof Document) ? (Document)parentNode : parentNode.getOwnerDocument(); // Create the GemCutter element, with the appropriate default namespace Element resultElement = document.createElementNS(GemPersistenceConstants.GEMS_NS, GemPersistenceConstants.GEMCUTTER_TAG); parentNode.appendChild(resultElement); // Declare gem namespace and schema. NamespaceInfo gemNSInfo = Gem.getNamespaceInfo(); XMLPersistenceHelper.attachNamespaceAndSchema(document, gemNSInfo, GemPersistenceConstants.GEMS_SCHEMA_LOCATION, GemPersistenceConstants.GEMS_NS); // Add info for the tabletop gemCutter.getTableTop().saveXML(resultElement); // Now add GemCutter-specific info // For now, there isn't any. } /** * Restore the contents of this object from an element. * @param element the element at the head of the subtree which represents the object to reconstruct. * @param loadStatus the ongoing status of the load. */ private void loadXML(Element element, Status loadStatus){ try { XMLPersistenceHelper.checkTag(element, GemPersistenceConstants.GEMCUTTER_TAG); Node tableTopNode = element.getFirstChild(); XMLPersistenceHelper.checkIsElement(tableTopNode); // Just call the tabletop to restore itself. gemCutter.getTableTop().loadXML((Element) tableTopNode, gemCutter.getPerspective(), loadStatus); // TODOEL: write a schema and validate against it } catch (BadXMLDocumentException bxde) { loadStatus.add(new Status(Status.Severity.ERROR, GemCutter.getResourceString("SOM_GCLoadFailure"), bxde)); } } /** * Save a specific Gem. * @param gemToSave the Gem to save * @param scope the visibility of the gem to save * @return the status of the save */ Status saveGem(CollectorGem gemToSave, Scope scope) { if (gemToSave == null) { throw new NullPointerException(); } // TODO // Right now this either replaces the gem text if the gem is defined and the appropriate markers // can be found, or fails. If save succeeds, the design view is also written out to an XML file. // Determine the name of the gem being saved QualifiedName gemName = QualifiedName.make(gemCutter.getWorkingModuleName(), gemToSave.getUnqualifiedName()); // Get the gem definition. String gemDefinition = getGemDefinition(gemToSave, scope); // Get the gem metadata as functional agent metadata. CALFeatureName gemEntityFeatureName = CALFeatureName.getFunctionFeatureName(gemName); FunctionMetadata collectorMetadata = gemToSave.getDesignMetadata(); FunctionMetadata newGemMetadata = new FunctionMetadata(gemEntityFeatureName, collectorMetadata.getLocale()); collectorMetadata.copyTo(newGemMetadata); // Get the gem design. Document designDocument = XMLPersistenceHelper.getEmptyDocument(); saveXML(designDocument); Status saveStatus = gemCutter.getWorkspace().saveEntity(gemName, gemDefinition, newGemMetadata, designDocument); if (saveStatus.getSeverity() != Status.Severity.ERROR) { // TEMP: this is a re-entrancy hack gemCutter.recompileWorkspace(true); } return saveStatus; } /** * Obtain the CAL definition of a gem. * @param collectorGem the gem for which the definition should be obtained.. * @param scope the scope of the gem. */ private String getGemDefinition(CollectorGem collectorGem, Scope scope) { // Get the qualified name. String unqualifiedGemName = collectorGem.getUnqualifiedName(); // Now get the text of the CAL definition for the gem. // The definition.. SourceModel.FunctionDefn scDef = CALSourceGenerator.getFunctionSourceModel(unqualifiedGemName, collectorGem, scope); // TODO Display any error messages from logger? CompilerMessageLogger logger = new MessageLogger (); // The declaration.. TypeExpr type = gemCutter.getTypeChecker().checkFunction( new AdjunctSource.FromSourceModel(scDef), gemCutter.getWorkingModuleName(), logger); String typeDeclaration = (type == null) ? null : unqualifiedGemName + " :: " + type.toString() + ";"; StringBuilder sb = new StringBuilder(); if (typeDeclaration != null) { sb.append(typeDeclaration + "\n"); } sb.append(scDef + "\n"); return sb.toString(); } /** * Load a gem's design and put it on the table top. * @param gemEntity the entity whose design to load. * @param loadStatus a Status object for storing the status of the load operation */ void loadGemDesign(GemEntity gemEntity, Status loadStatus) { GemDesign gemDesign = gemEntity.getDesign(loadStatus); if (gemDesign != null) { loadGemDesign(gemDesign, loadStatus); } } /** * Load a gem design and put it on the tabletop. * @param gemDesign the design to load. * @param loadStatus a Status object for storing the status of the load operation. */ void loadGemDesign(GemDesign gemDesign, Status loadStatus) { // Switch to the module the design is from. QualifiedName designName = gemDesign.getDesignName(); ModuleName moduleName = designName.getModuleName(); if (gemCutter.getWorkspace().getMetaModule(moduleName) != null) { gemCutter.changeModuleAndNewTableTop(moduleName, false); } loadXML(gemDesign.getDesignDocument().getDocumentElement(), loadStatus); // Find the collector that was loaded on the table top. String unqualifiedDesignName = designName.getUnqualifiedName(); Set<CollectorGem> collectors = gemCutter.getTableTop().getGemGraph().getCollectorsForName(unqualifiedDesignName); if (collectors.iterator().hasNext()) { CollectorGem collectorGem = collectors.iterator().next(); // Find the gem in the working module with the same name GemEntity entity = gemCutter.getWorkspace().getGemEntity(designName); if (entity != null) { // Load the metadata for the gem and copy it to the collector FunctionMetadata gemMetadata = (FunctionMetadata)entity.getMetadata(GemCutter.getLocaleFromPreferences()); collectorGem.setDesignMetadata(gemMetadata); } else { // Clear the metadata for the collector, since there is no gem entity for the design collectorGem.clearDesignMetadata(); } } else { loadStatus.add(new Status(Status.Severity.ERROR, GemCutter.getResourceString("SOM_CouldntFindCollector"), null)); } } }