blob: b9d8ab4481ab984f6ff6f404919be89a5202a0a1 [file] [log] [blame]
//------------------------------------------------------------------------------
// Copyright (c) 2005, 2006 IBM Corporation and others.
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// which accompanies this distribution, and is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// Contributors:
// IBM Corporation - initial implementation
//------------------------------------------------------------------------------
package org.eclipse.epf.persistence;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.common.CommonPlugin;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.ExtendedMetaData;
import org.eclipse.emf.ecore.xmi.XMLHelper;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.emf.ecore.xmi.impl.XMISaveImpl;
import org.eclipse.epf.common.serviceability.EPFVersions;
import org.eclipse.epf.common.serviceability.VersionUtil;
import org.eclipse.epf.resourcemanager.ResourceManager;
import org.eclipse.epf.uma.MethodElement;
/**
* XMLSave implementation for library XMI persistence
*
* @author Phong Nguyen Le
* @since 1.0
*/
public class MultiFileXMISaveImpl extends XMISaveImpl {
public static final String SAVE_SEPARATELY_CLASS_SET = "SAVE_SEPARATELY_CLASS_SET"; //$NON-NLS-1$
/**
* Save options to force saving all resources in the resource set, whether
* they have been modified or unchanged. Its value must be a string "true"
* or "false"
*/
public static final String SAVE_ALL = "SAVE_ALL"; //$NON-NLS-1$
/**
* Save option to save objects of the save type together in the same file if
* MultiFileUtil.createFileURI() returns the same URI for them.
*/
public static final String SAVE_TOGETHER_CLASS_SET = "SAVE_TOGETHER_CLASS_SET"; //$NON-NLS-1$
public static final String BACK_UP_BEFORE_SAVE = "BACK_UP_BEFORE_SAVE"; //$NON-NLS-1$
public static final String DISCARD_UNRESOLVED_REFERENCES = "DISCARD_UNRESOLVED_REFERENCES"; //$NON-NLS-1$
static final String MODIFIED_RESOURCE_SET = "MODIFIED_RESOURCE_SET"; //$NON-NLS-1$
/**
* Save option to specify a TxRecord to log the transactional data for
* fail-safe persistence
*
* @see org.eclipse.epf.uma.util.IFileBasedLibraryPersister.FailSafeMethodLibraryPersister
* @see TxRecord
*/
static final String TX_RECORD = "TX_RECORD"; //$NON-NLS-1$
public static final String MULTI_FILE = "MULTI_FILE"; //$NON-NLS-1$
/**
* Save option to refresh the workspace when new resource is created and
* saved. Its value must be a string "true" or "false"
*/
public static final String REFRESH_NEW_RESOURCE = "REFRESH_NEW_RESOURCE"; //$NON-NLS-1$
/**
* Save option to check the resource for modifiable before saving it. Its
* value must be a string "true" or "false"
*/
public static final String CHECK_MODIFY = "CHECK_MODIFY"; //$NON-NLS-1$
// private Resource resource;
private Set saveSeparatelyClassSet;
private Map options;
/**
* @param helper
*/
public MultiFileXMISaveImpl(XMLHelper helper) {
super(helper);
}
XMLHelper getXMLHelper() {
return helper;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.emf.ecore.xmi.impl.XMLSaveImpl#init(org.eclipse.emf.ecore.xmi.XMLResource,
* java.util.Map)
*/
protected void init(XMLResource resource, Map opts) {
super.init(resource, opts);
if (escape != null) {
// use MyEscape to ignore whitespaces
//
escape = new MyEscape();
}
saveSeparatelyClassSet = (Set) opts.get(SAVE_SEPARATELY_CLASS_SET);
options = opts;
ResourceSet resourceSet = resource.getResourceSet();
if (resourceSet == null) {
resourceSet = new MultiFileResourceSetImpl();
resourceSet.getResources().add(resource);
}
}
boolean canSaveSeparately(Object obj) {
return MultiFileSaveUtil.hasOwnResource(obj, saveSeparatelyClassSet);
}
private String getUmaHREF(Resource resource, InternalEObject o) {
if (o.eIsProxy()
&& o.eProxyURI().scheme().equals(MultiFileURIConverter.SCHEME)) {
return o.eProxyURI().toString();
}
String href = null;
if (o instanceof MethodElement) {
href = helper.getHREF(o);
}
if (href == null) {
PersistencePlugin.getDefault().getLogger().logError("Could not find resource descriptor for resource " + resource + "\n object: " + o); //$NON-NLS-1$ //$NON-NLS-2$
return MultiFileSaveUtil.getHREF(resource, o);
}
return href;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.emf.ecore.xmi.impl.XMLSaveImpl#saveHref(org.eclipse.emf.ecore.EObject,
* org.eclipse.emf.ecore.EStructuralFeature)
*/
protected void saveHref(EObject remote, EStructuralFeature f) {
if (f instanceof EReference && ((EReference) f).isContainment()
&& !toDOM) {
// if this HREF is a contained element, save its ID attribute so the
// proxy can be cached in the
// GUIDToElementMap for temporary use before it can be resolved.
// This helps improve loading time.
//
String href = helper.getHREF(remote);
if (href != null) {
if (escapeURI != null) {
href = escapeURI.convert(href);
}
String name = helper.getQName(f);
doc.startElement(name);
EClass eClass = remote.eClass();
EClass expectedType = (EClass) f.getEType();
if (saveTypeInfo ? xmlTypeInfo.shouldSaveType(eClass,
expectedType, f) : eClass != expectedType
&& expectedType.isAbstract()) {
saveTypeAttribute(eClass);
}
String id = helper.getID(remote);
if (id != null) {
doc.addAttribute(idAttributeName, id);
}
doc.addAttribute(XMLResource.HREF, href);
if (eObjectToExtensionMap != null) {
processAttributeExtensions(remote);
if (processElementExtensions(remote)) {
doc.endElement();
} else {
doc.endEmptyElement();
}
} else {
doc.endEmptyElement();
}
}
return;
}
super.saveHref(remote, f);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.emf.ecore.xmi.impl.XMLSaveImpl#saveFeatures(org.eclipse.emf.ecore.EObject)
*/
protected boolean saveFeatures(EObject o) {
try {
MultiFileXMIResourceImpl resource = (MultiFileXMIResourceImpl) o
.eResource();
if (resource != null) {
EObject container = o.eContainer();
InternalEObject internalEObject = ((InternalEObject) o);
if (o instanceof MethodElement && canSaveSeparately(o)
&& container != null) {
boolean containsNewFeature = (resource == container
.eResource());
MethodElement e = (MethodElement) o;
URI uri = MultiFileSaveUtil
.createFileURI((MethodElement) o);
if (containsNewFeature) {
if (!internalEObject.eIsProxy()) {
resource = (MultiFileXMIResourceImpl) MultiFileSaveUtil
.save(o, uri, options);
}
} else {
// check if there is a ResourceDescriptor for this
// element
// create a new one if needed
//
ResourceManager resMgr = MultiFileSaveUtil
.getResourceManager(container.eResource());
if (resMgr != null
&& resMgr.getResourceDescriptor(e.getGuid()) == null) {
// make sure that no other ResourceManager has a
// ResourceDescriptor for this element
//
MultiFileURIConverter uriConverter = (MultiFileURIConverter) resource
.getResourceSet().getURIConverter();
if (uriConverter
.findResourceDescriptor(e.getGuid()) == null) {
MultiFileSaveUtil.registerWithResourceManager(
resMgr, e, resource.getFinalURI());
}
}
}
String href = getUmaHREF(resource, internalEObject);
doc.addAttribute(XMLResource.HREF, href);
endSaveFeatures(o, 0, null);
return true;
}
if (o instanceof ResourceManager
&& helper.getResource() != resource) {
String href = getUmaHREF(resource, internalEObject);
doc.addAttribute(XMLResource.HREF, href);
endSaveFeatures(o, 0, null);
return true;
}
}
return super.saveFeatures(o);
} catch (RuntimeException e) {
CommonPlugin.INSTANCE.log(e);
if (MultiFileSaveUtil.DEBUG) {
e.printStackTrace();
System.err.println("ERROR saving feature: " + o); //$NON-NLS-1$
System.err.println(" Feature resource: " + o.eResource()); //$NON-NLS-1$
}
throw e;
}
}
/* (non-Javadoc)
* @see org.eclipse.emf.ecore.xmi.impl.XMISaveImpl#addNamespaceDeclarations()
*/
public void addNamespaceDeclarations() {
super.addNamespaceDeclarations();
if (!toDOM) {
// save tool version info
//
for (Iterator iter = VersionUtil.getAllToolIDs().iterator();iter.hasNext();) {
String toolID = (String)iter.next();
EPFVersions epfVersions = VersionUtil.getVersions(toolID);
String nsUri = epfVersions.getNsURI();
doc.addAttribute(ExtendedMetaData.XMLNS_PREFIX + ":" + toolID, nsUri); //$NON-NLS-1$
String toolVersion = epfVersions.getMinToolVersionForCurrentLibraryVersion().getToolVersion().toString();
doc.addAttribute(toolID + ":version", toolVersion); //$NON-NLS-1$
}
}
}
/*
* protected String getDatatypeValue(Object value, EStructuralFeature f,
* boolean isAttribute) { if (value != null && value instanceof String) {
* String str = (String)value; if (str.indexOf("\r") > 0) { str =
* str.replaceAll("\r", ""); return super.getDatatypeValue(str, f,
* isAttribute); } }; return super.getDatatypeValue(value, f, isAttribute); }
*/
// TODO: needs to revisit this class since its base class has been updated a
// lot in EMF 2.2
public static class MyEscape extends Escape {
protected final char[] GREATER = { '&', 'g', 't', ';' };
protected final char[] BLANK = {};
public MyEscape() {
super();
}
/*
* Converts: & to &amp; < to &lt; " to &quot; \t to &#x9; \n to &#xA; \r
* to &#xD;
*/
public String convert(String input) {
boolean changed = false;
int inputLength = input.length();
grow(inputLength);
input.getChars(0, inputLength, value, 0);
int pos = 0;
char ch = 0;
while (inputLength-- > 0) {
ch = value[pos];
switch (ch) {
case '&':
pos = replace(pos, AMP, inputLength);
changed = true;
break;
case '<':
pos = replace(pos, LESS, inputLength);
changed = true;
break;
case '>':
pos = replace(pos, GREATER, inputLength);
changed = true;
break;
case '"':
pos = replace(pos, QUOTE, inputLength);
changed = true;
break;
default:
/*
* if (!XMLChar.isValid(ch)) { throw new
* RuntimeException("An invalid XML character (Unicode: 0x" +
* Integer.toHexString(ch)+") was found in the element
* content:" +input); }
*/
pos++;
break;
}
}
return changed ? new String(value, 0, pos) : input;
}
/**
* Converts: & to &amp; < to &lt; > to &gt; " to &quot; \n to
* platform-dependent line breaking character
*/
public String convertText(String input) {
boolean changed = false;
int inputLength = input.length();
grow(inputLength);
input.getChars(0, inputLength, value, 0);
int pos = 0;
while (inputLength-- > 0) {
switch (value[pos]) {
case '&':
pos = replace(pos, AMP, inputLength);
changed = true;
break;
case '<':
pos = replace(pos, LESS, inputLength);
changed = true;
break;
case '>':
pos = replace(pos, GREATER, inputLength);
changed = true;
break;
case '"':
pos = replace(pos, QUOTE, inputLength);
changed = true;
break;
case '\r':
pos = replace(pos, BLANK, inputLength);
changed = true;
break;
case '\n':
pos = replace(pos, LINE_FEED, inputLength);
changed = true;
break;
default:
pos++;
break;
}
}
return changed ? new String(value, 0, pos) : input;
}
/*
* Taken from XMLSaveImpl$Escape.convertText, and added conversion of >
* This is used for XLIFF's - change it's name so it doesn't impact EMF
* saving Convert: & to &amp; < to &lt; " to &quot; \n to line separator
*/
public String myConvertText(String input) {
boolean changed = false;
int inputLength = input.length();
grow(inputLength);
input.getChars(0, inputLength, value, 0);
int pos = 0;
while (inputLength-- > 0) {
switch (value[pos]) {
case '&':
pos = replace(pos, AMP, inputLength);
changed = true;
break;
case '<':
pos = replace(pos, LESS, inputLength);
changed = true;
break;
case '>':
pos = replace(pos, GREATER, inputLength);
changed = true;
break;
case '"':
pos = replace(pos, QUOTE, inputLength);
changed = true;
break;
case '\n': {
pos = replace(pos, LINE_FEED, inputLength);
changed = true;
break;
}
default:
pos++;
break;
}
}
return changed ? new String(value, 0, pos) : input;
}
}
public static boolean checkModifyRequired(Map options) {
Object opt = options.get(MultiFileXMISaveImpl.CHECK_MODIFY);
return opt != null ? Boolean.valueOf(opt.toString()).booleanValue()
: false;
}
}