/*
* 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.
*/
/*
* WorkspacePersistenceManager.java
* Creation date: Oct 13, 2004.
* By: Edward Lam
*/
package org.openquark.cal.services;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.openquark.cal.compiler.ModuleName;
import org.openquark.util.xml.BadXMLDocumentException;
import org.openquark.util.xml.NamespaceInfo;
import org.openquark.util.xml.XMLPersistenceHelper;
import org.openquark.util.xml.XMLPersistenceHelper.DocumentConstructionException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* This class takes care of saving and loading workspace-related resources.
* @author Edward Lam
*/
class WorkspacePersistenceManager {
/** The workspace with which this manager is associated. */
private final CALWorkspace workspace;
/**
* A simple wrapper around a vault descriptor and a string which identifies a vault type and its location.
* @author Edward Lam
*/
private static class VaultLocation {
private final String vaultDescriptor;
private final String locationString;
/**
* Constructor for a VaultLocation.
*/
public VaultLocation(String vaultDescriptor, String locationString) {
this.vaultDescriptor = vaultDescriptor;
this.locationString = locationString;
}
/**
* @return Returns the vaultDescriptor.
*/
public String getVaultDescriptor() {
return vaultDescriptor;
}
/**
* @return Returns the locationString.
*/
public String getLocationString() {
return locationString;
}
}
/**
* Constructor for a WorkspacePersistenceManager.
* @param workspace the workspace with which this manager is associated.
*/
WorkspacePersistenceManager(CALWorkspace workspace) {
this.workspace = workspace;
}
/**
* Saves the workspace description to a stream.
* @param descriptionOutputStream the stream to which to save the workspace description.
* @param resourceInfo the info about resources in the workspace.
*/
void saveWorkspaceDescription(OutputStream descriptionOutputStream, CALWorkspace.ResourceInfo resourceInfo) {
// Convert the description into an XML document
Document document = XMLPersistenceHelper.getEmptyDocument();
// Attach the root document element.
Element resultElement = document.createElementNS(WorkspacePersistenceConstants.WORKSPACE_NS, WorkspacePersistenceConstants.WORKSPACE_DESCRIPTION_TAG);
document.appendChild(resultElement);
// Just attach the component module descriptions to the top-level document root.
ModuleName[] moduleNames = workspace.getModuleNames();
int nModules = moduleNames.length;
for (int i = 0; i < nModules; i++) {
ModuleName moduleName = moduleNames[i];
saveWorkspaceModuleDescription(resultElement, moduleName, resourceInfo);
}
// Attach namespace and schema info.
XMLPersistenceHelper.attachNamespaceAndSchema(document, getNamespaceInfo(), WorkspacePersistenceConstants.WORKSPACE_DESCRIPTION_SCHEMA_LOCATION, WorkspacePersistenceConstants.WORKSPACE_NS);
// Write the document to the metadata file
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(descriptionOutputStream);
XMLPersistenceHelper.documentToXML(document, bufferedOutputStream, false);
}
/**
* Save the workspace-related info for a module as xml.
* @param parentNode the node to which to save the info.
* @param moduleName the name of the module.
* @param resourceInfo the info about resources in the workspace.
*/
void saveWorkspaceModuleDescription(Node parentNode, ModuleName moduleName, CALWorkspace.ResourceInfo resourceInfo) {
Document document = (parentNode instanceof Document) ? (Document) parentNode : parentNode.getOwnerDocument();
// Attach an element for the module.
Element moduleElement = document.createElementNS(WorkspacePersistenceConstants.WORKSPACE_NS, WorkspacePersistenceConstants.MODULE_TAG);
parentNode.appendChild(moduleElement);
// Set the name of the module as an attribute.
moduleElement.setAttribute(WorkspacePersistenceConstants.MODULE_NAME_ATTR, moduleName.toSourceText());
// Save vault info for the module.
VaultElementInfo info = workspace.getVaultInfo(moduleName);
saveVaultInfo(moduleElement, info);
// Add module revision.
long moduleRevision = info.getRevision();
XMLPersistenceHelper.addLongElementNS(moduleElement, WorkspacePersistenceConstants.WORKSPACE_NS, WorkspacePersistenceConstants.MODULE_REVISION_TAG, moduleRevision);
// Save info for the individual module resources.
saveResourceInfo(moduleElement, moduleName, resourceInfo);
}
/**
* @return the namespace info for the workspace.
*/
static NamespaceInfo getNamespaceInfo() {
return new NamespaceInfo(WorkspacePersistenceConstants.WORKSPACE_NS, WorkspacePersistenceConstants.WORKSPACE_NS_PREFIX);
}
/**
* Loads a workspace description from a stream.
* @param descriptionInputStream the stream from which to obtain the workspace description.
*/
boolean loadWorkspaceDescription(InputStream descriptionInputStream, Status loadStatus) {
try {
InputStream bufferedInputStream = new BufferedInputStream(descriptionInputStream);
Document descriptionDocument = XMLPersistenceHelper.documentFromXML(bufferedInputStream);
Element documentElement = descriptionDocument.getDocumentElement();
XMLPersistenceHelper.checkTag(documentElement, WorkspacePersistenceConstants.WORKSPACE_DESCRIPTION_TAG);
List<Element> moduleDescriptionNodes = XMLPersistenceHelper.getChildElements(documentElement);
for (final Element moduleDescriptionElement : moduleDescriptionNodes) {
loadWorkspaceModuleDescription(moduleDescriptionElement);
}
// TODOEL: write a schema and validate against it
return true;
} catch (BadXMLDocumentException bxde) {
loadStatus.add(new Status(Status.Severity.ERROR, "Workspace description load failure.", bxde));
} catch (DocumentConstructionException ex) {
loadStatus.add(new Status(Status.Severity.ERROR, "The workspace description file XML is invalid.", ex));
} catch (Exception ex) {
loadStatus.add(new Status(Status.Severity.ERROR, "Exception while loading workspace description.", ex));
}
return false;
}
/**
* Load the workspace-related info for a module as xml.
* @param moduleDescriptionElement the element describing the module's workspace-related info.
* @throws BadXMLDocumentException
*/
void loadWorkspaceModuleDescription(Element moduleDescriptionElement) throws BadXMLDocumentException {
XMLPersistenceHelper.checkTag(moduleDescriptionElement, WorkspacePersistenceConstants.MODULE_TAG);
String moduleNameString = moduleDescriptionElement.getAttribute(WorkspacePersistenceConstants.MODULE_NAME_ATTR);
if (moduleNameString.equals("")) {
String errorString = "Could not obtain module from workspace description.";
throw new BadXMLDocumentException(moduleDescriptionElement, errorString);
}
ModuleName moduleName = ModuleName.make(moduleNameString);
List<Element> childElems = XMLPersistenceHelper.getChildElements(moduleDescriptionElement);
int nChildElems = childElems.size();
// Get the vault info element.
Element vaultElement = (nChildElems < 1) ? null : childElems.get(0);
XMLPersistenceHelper.checkIsElement(vaultElement);
// Get the module revision.
Element moduleRevisionElement = (nChildElems < 2) ? null : childElems.get(1);
XMLPersistenceHelper.checkTag(moduleRevisionElement, WorkspacePersistenceConstants.MODULE_REVISION_TAG);
Integer moduleRevisionNum = XMLPersistenceHelper.getElementIntegerValue(moduleRevisionElement);
if (moduleRevisionNum == null) {
throw new BadXMLDocumentException(moduleRevisionElement, "No module revision found.");
}
// Get resource info.
Element revisionInfoElement = (nChildElems < 3) ? null : childElems.get(2);
XMLPersistenceHelper.checkIsElement(revisionInfoElement);
CALWorkspace.ResourceInfo resourceInfo = loadResourceInfo(revisionInfoElement);
// Update the resource info.
workspace.updateResourceInfo(resourceInfo);
// Get the module location.
VaultLocation storedModuleLocation = getVaultLocation(vaultElement);
// Create the vault info.
VaultElementInfo vaultInfo = VaultElementInfo.makeBasic(storedModuleLocation.getVaultDescriptor(),
moduleName.toSourceText(),
storedModuleLocation.getLocationString(),
moduleRevisionNum.intValue());
// Update the info map.
workspace.updateVaultInfo(moduleName, vaultInfo);
}
/**
* Save vault info as xml.
* @param parentElement the element to which the info should be saved.
* @param vaultInfo the info to save.
*/
static void saveVaultInfo(Element parentElement, VaultElementInfo vaultInfo) {
Document document = (parentElement instanceof Document) ? (Document) parentElement : parentElement.getOwnerDocument();
Element vaultInfoElement = document.createElementNS(WorkspacePersistenceConstants.WORKSPACE_NS, WorkspacePersistenceConstants.VAULT_INFO_TAG);
parentElement.appendChild(vaultInfoElement);
// Add vault type.
String vaultDescriptor = vaultInfo.getVaultDescriptor();
XMLPersistenceHelper.addTextElementNS(vaultInfoElement, WorkspacePersistenceConstants.WORKSPACE_NS, WorkspacePersistenceConstants.VAULT_TYPE_TAG, vaultDescriptor);
// Add a text element with the location. (Note: the element will have not text if the location string is null..).
String locationString = vaultInfo.getLocationString();
XMLPersistenceHelper.addTextElementNS(vaultInfoElement, WorkspacePersistenceConstants.WORKSPACE_NS, WorkspacePersistenceConstants.VAULT_LOCATION_TAG, locationString);
}
/**
* Load a vault location from xml.
* @param vaultInfoElement the xml element describing the vault location.
* @return the corresponding location, or null if there isn't any.
* @throws BadXMLDocumentException
*/
private static VaultLocation getVaultLocation(Element vaultInfoElement) throws BadXMLDocumentException {
XMLPersistenceHelper.checkTag(vaultInfoElement, WorkspacePersistenceConstants.VAULT_INFO_TAG);
List<Element> childElems = XMLPersistenceHelper.getChildElements(vaultInfoElement);
int nChildElems = childElems.size();
// Get the vault descriptor string.
Element vaultDescriptorElement = (nChildElems < 1) ? null : childElems.get(0);
XMLPersistenceHelper.checkIsElement(vaultDescriptorElement);
final String vaultDescriptor = XMLPersistenceHelper.getElementStringValue(vaultDescriptorElement);
// Get the vault location string.
Element locationElement = (nChildElems < 2) ? null : childElems.get(1);
XMLPersistenceHelper.checkIsElement(locationElement);
final String locationString = XMLPersistenceHelper.getElementStringValue(locationElement);
return new VaultLocation(vaultDescriptor, locationString);
}
/**
* Save resource info for individual module resources.
* @param parentElement the element to which the info should be saved.
* @param moduleName the name of the module whose info should be saved.
* @param resourceInfo the info to save.
*/
static void saveResourceInfo(Element parentElement, ModuleName moduleName, CALWorkspace.ResourceInfo resourceInfo) {
Document document = (parentElement instanceof Document) ? (Document) parentElement : parentElement.getOwnerDocument();
// Attach an element to hold all the resource info.
Element resourceInfoParentElement = document.createElementNS(WorkspacePersistenceConstants.WORKSPACE_NS, WorkspacePersistenceConstants.RESOURCE_INFO_TAG);
parentElement.appendChild(resourceInfoParentElement);
// Get the revision and sync time info.
ResourceRevision.Info revisionInfo = resourceInfo.getResourceRevisionInfo();
CALWorkspace.SyncTimeInfo syncTimeInfo = resourceInfo.getResourceSyncTimeInfo();
// Get the resource types.
Set<String> resourceTypes = new LinkedHashSet<String>();
resourceTypes.addAll(Arrays.asList(revisionInfo.getResourceTypes()));
resourceTypes.addAll(Arrays.asList(syncTimeInfo.getResourceTypes()));
// Iterate over the resource types.
for (final String resourceType : resourceTypes) {
Set<ResourceRevision> resourceRevisions = revisionInfo.getResourceRevisions(resourceType);
// Set syncTimes = syncTimeInfo.getResourceSyncTimes(resourceType);
// TODOEL: for now, we assume that anything that has a revision also has a sync time, and vice versa.
// Create the individual resource info element
Element resourceTypeElement = document.createElementNS(WorkspacePersistenceConstants.WORKSPACE_NS, resourceType);
resourceInfoParentElement.appendChild(resourceTypeElement);
for (final ResourceRevision resourceRevision : resourceRevisions) {
long resourceRevisionNum = resourceRevision.getRevisionNumber();
ResourceIdentifier identifier = resourceRevision.getIdentifier();
// Convert to a cal feature name..
if (!(identifier.getFeatureName() instanceof CALFeatureName)) {
continue;
}
CALFeatureName calFeatureName = (CALFeatureName)identifier.getFeatureName();
if (calFeatureName.hasModuleName() && calFeatureName.toModuleName().equals(moduleName)) {
// Add an element to hold the info for this feature.
Element featureElement = document.createElementNS(WorkspacePersistenceConstants.WORKSPACE_NS, WorkspacePersistenceConstants.RESOURCE_FEATURE_TAG);
resourceTypeElement.appendChild(featureElement);
// Add info for the feature.
// Resource name.
identifier.getResourceName().saveXML(featureElement);
// Revision.
XMLPersistenceHelper.addLongElementNS(featureElement, WorkspacePersistenceConstants.WORKSPACE_NS, WorkspacePersistenceConstants.RESOURCE_REVISION_TAG, resourceRevisionNum);
// Sync time.
long resourceSyncTime = syncTimeInfo.getResourceSyncTime(identifier);
XMLPersistenceHelper.addLongElementNS(featureElement, WorkspacePersistenceConstants.WORKSPACE_NS, WorkspacePersistenceConstants.SYNC_TIME_TAG, resourceSyncTime);
}
}
}
}
/**
* Load the resource info from the given xml element.
* @param resourceInfoParentElement the element from which to load resource info.
* @return resource info from the given element.
* @throws BadXMLDocumentException if there was an error obtaining resource info from the given element.
*/
private static CALWorkspace.ResourceInfo loadResourceInfo(Element resourceInfoParentElement)
throws BadXMLDocumentException {
// Check that the parent element carries the expected tag.
XMLPersistenceHelper.checkTag(resourceInfoParentElement, WorkspacePersistenceConstants.RESOURCE_INFO_TAG);
// The list of revisions.
List<ResourceRevision> revisionList = new ArrayList<ResourceRevision>();
// The list of sync times.
List<WorkspaceResource.SyncTime> syncTimeList = new ArrayList<WorkspaceResource.SyncTime>();
// Iterate through the children.
// There will be one for each resource type.
List<Element> revisionInfoElems = XMLPersistenceHelper.getChildElements(resourceInfoParentElement);
for (final Element resourceTypeElement : revisionInfoElems) {
// Get the name of the element. This will be the resource type.
String resourceType = resourceTypeElement.getLocalName();
// Get its children. These will be the features of that type.
List<Element> resourceTypeFeatureElems = XMLPersistenceHelper.getChildElements(resourceTypeElement);
for (final Element featureElement : resourceTypeFeatureElems) {
XMLPersistenceHelper.checkTag(featureElement, WorkspacePersistenceConstants.RESOURCE_FEATURE_TAG);
List<Element> featureChildElems = XMLPersistenceHelper.getChildElements(featureElement);
int nFeatureChildElems = featureChildElems.size();
Element featureNameElement = (nFeatureChildElems < 1) ? null : featureChildElems.get(0);
XMLPersistenceHelper.checkIsElement(featureNameElement);
ResourceName resourceName = ResourceName.getResourceNameWithCALFeatureNameFromXML(featureNameElement);
Element featureRevisionNumElement = (nFeatureChildElems < 2) ? null : featureChildElems.get(1);
XMLPersistenceHelper.checkIsElement(featureRevisionNumElement);
final Long featureRevisionNum = XMLPersistenceHelper.getElementLongValue(featureRevisionNumElement);
if (featureRevisionNum == null) {
throw new BadXMLDocumentException(featureRevisionNumElement, "No feature revision found.");
}
ResourceRevision metadataRevision =
new ResourceRevision(new ResourceIdentifier(resourceType, resourceName), featureRevisionNum.longValue());
revisionList.add(metadataRevision);
// sync time
Element featureSyncTimeElement = (nFeatureChildElems < 3) ? null : featureChildElems.get(2);
XMLPersistenceHelper.checkIsElement(featureSyncTimeElement);
final Long featureSyncTimeNum = XMLPersistenceHelper.getElementLongValue(featureSyncTimeElement);
if (featureSyncTimeNum == null) {
throw new BadXMLDocumentException(featureSyncTimeElement, "No feature sync time found.");
}
WorkspaceResource.SyncTime syncTime = new WorkspaceResource.SyncTime(new ResourceIdentifier(resourceType, resourceName), featureSyncTimeNum.longValue());
syncTimeList.add(syncTime);
}
}
// Create the resource info.
ResourceRevision.Info resourceRevisionInfo = new ResourceRevision.Info(revisionList);
CALWorkspace.SyncTimeInfo syncTimeInfo = new CALWorkspace.SyncTimeInfo(syncTimeList);
return new CALWorkspace.ResourceInfo(resourceRevisionInfo, syncTimeInfo);
}
}