/*******************************************************************************
* Copyright (c) 2009, 2010 Fraunhofer IWU 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:
* Fraunhofer IWU - initial API and implementation
*******************************************************************************/
package net.enilink.komma.model.base;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import com.google.inject.ConfigurationException;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Names;
import net.enilink.commons.iterator.IExtendedIterator;
import net.enilink.commons.iterator.IMap;
import net.enilink.commons.iterator.WrappedIterator;
import net.enilink.commons.util.extensions.RegistryFactoryHelper;
import net.enilink.composition.traits.Behaviour;
import net.enilink.komma.common.notify.INotification;
import net.enilink.komma.common.notify.INotificationBroadcaster;
import net.enilink.komma.core.EntityVar;
import net.enilink.komma.core.IBindings;
import net.enilink.komma.core.IEntity;
import net.enilink.komma.core.IEntityDecorator;
import net.enilink.komma.core.IEntityManager;
import net.enilink.komma.core.IEntityManagerFactory;
import net.enilink.komma.core.INamespace;
import net.enilink.komma.core.IReference;
import net.enilink.komma.core.KommaException;
import net.enilink.komma.core.KommaModule;
import net.enilink.komma.core.Statement;
import net.enilink.komma.core.StatementPattern;
import net.enilink.komma.core.URI;
import net.enilink.komma.core.URIs;
import net.enilink.komma.dm.IDataManager;
import net.enilink.komma.em.ThreadLocalEntityManager;
import net.enilink.komma.em.concepts.IOntology;
import net.enilink.komma.em.util.ISparqlConstants;
import net.enilink.komma.internal.model.IModelAware;
import net.enilink.komma.model.IModel;
import net.enilink.komma.model.IModelSet;
import net.enilink.komma.model.IModelStatusConstants;
import net.enilink.komma.model.IObject;
import net.enilink.komma.model.IURIConverter;
import net.enilink.komma.model.ModelPlugin;
import net.enilink.komma.model.ModelUtil;
import net.enilink.komma.model.ObjectSupport;
import net.enilink.komma.model.concepts.Model;
import net.enilink.komma.model.concepts.Namespace;
import net.enilink.komma.model.event.NamespaceNotification;
import net.enilink.vocab.owl.OWL;
import net.enilink.vocab.rdf.RDF;
public abstract class ModelSupport
implements IModel, IModel.Internal, INotificationBroadcaster<INotification>, Model, Behaviour<IModel.Internal> {
class ModelInjector implements IEntityDecorator {
@Override
public void decorate(IEntity entity) {
((IModelAware) entity).initModel(getBehaviourDelegate());
}
}
/**
* Merges 2 maps, without changing any of them. If map2 and map1 have the
* same key for an entry, map1's value will be the one in the merged map.
*/
protected static Map<?, ?> mergeMaps(Map<?, ?> map1, Map<?, ?> map2) {
if (map1 == null || map1.isEmpty()) {
return map2;
} else if (map2 == null || map2.isEmpty()) {
return map1;
} else {
Map<Object, Object> mergedMap = new HashMap<Object, Object>(map2);
mergedMap.putAll(map1);
return mergedMap;
}
}
/**
* Represents the transient state of this resource
*/
class State {
KommaModule module;
KommaModule moduleClosure;
Set<URI> importedModels;
// cache the factory instance to quickly create thread-local entity managers
WeakReference<IEntityManagerFactory> factoryRef;
WeakReference<IEntityManager> managerRef;
void reset() {
module = null;
moduleClosure = null;
importedModels = null;
factoryRef = null;
if (managerRef != null) {
IEntityManager manager = managerRef.get();
if (manager != null) {
manager.close();
}
}
}
IEntityManager manager() {
IEntityManager manager = managerRef != null ? managerRef.get() : null;
if (manager == null) {
synchronized (this) {
manager = managerRef != null ? managerRef.get() : null;
if (manager == null) {
manager = new ThreadLocalEntityManager() {
volatile Map<URI, String> uriToPrefix = new ConcurrentHashMap<>();
volatile Map<String, URI> prefixToUri = new ConcurrentHashMap<>();
@SuppressWarnings("resource")
@Override
protected IEntityManager initialValue() {
IEntityManagerFactory factory;
synchronized (State.this) {
factory = factoryRef != null ? factoryRef.get() : null;
if (factory == null) {
factory = getModelSet().getEntityManagerFactory()
// allow interception of call to getModuleClosure()
.createChildFactory(getBehaviourDelegate().getModuleClosure());
factoryRef = new WeakReference<>(factory);
}
}
return factory.create(managerRef.get());
}
@Override
public void removeNamespace(String prefix) {
if (prefix == null) {
prefix = "";
}
for (Namespace ns : new ArrayList<>(getModelNamespaces())) {
if (prefix.equals(ns.getPrefix())) {
getModelNamespaces().remove(ns);
fireNotifications(
Arrays.asList(new NamespaceNotification(prefix, ns.getURI(), null)));
break;
}
}
clearNamespaceCache();
}
@Override
public void setNamespace(String prefix, URI uri) {
if (prefix == null) {
prefix = "";
}
URI oldUri = null;
// prevent addition of redundant prefix/uri
// combinations
if (uri.equals(super.getNamespace(prefix))) {
oldUri = uri;
}
if (oldUri == null) {
for (Namespace ns : new ArrayList<>(getModelNamespaces())) {
if (prefix.equals(ns.getPrefix())) {
ns.setPrefix(prefix);
oldUri = ns.getURI();
break;
}
}
}
if (oldUri == null) {
Namespace ns = getEntityManager().create(Namespace.class);
ns.setPrefix(prefix);
ns.setURI(uri);
getModelNamespaces().add(ns);
}
fireNotifications(Arrays.asList(new NamespaceNotification(prefix, oldUri, uri)));
clearNamespaceCache();
}
@Override
public IExtendedIterator<INamespace> getNamespaces() {
Map<String, INamespace> prefixMap = new LinkedHashMap<>();
for (INamespace ns : WrappedIterator.create(getAllModelNamespaces().iterator())
.andThen(super.getNamespaces().mapWith(
// mark inherited
// namespaces as derived
new IMap<INamespace, INamespace>() {
@Override
public INamespace map(INamespace ns) {
return new net.enilink.komma.core.Namespace(ns.getPrefix(), ns.getURI(), true);
}
}))) {
if (!prefixMap.containsKey(ns.getPrefix())) {
prefixMap.put(ns.getPrefix(), new net.enilink.komma.core.Namespace(
ns.getPrefix(), ns.getURI(), ns.isDerived()));
}
}
return WrappedIterator.create(prefixMap.values().iterator());
}
List<INamespace> getAllModelNamespaces() {
List<INamespace> nsList = new ArrayList<INamespace>(getModelNamespaces());
boolean emptyPrefix = false;
for (INamespace ns : nsList) {
if (ns.getPrefix().isEmpty()) {
emptyPrefix = true;
break;
}
}
if (!emptyPrefix) {
nsList.add(new net.enilink.komma.core.Namespace("", getURI().appendLocalPart("")));
}
KommaModule module = moduleClosure;
if (module != null) {
for (INamespace ns : module.getNamespaces()) {
nsList.add(new net.enilink.komma.core.Namespace(ns.getPrefix(), ns.getURI(),
true));
}
}
return nsList;
}
@Override
public URI getNamespace(String prefix) {
if (prefix == null || prefix.length() == 0) {
return getURI().appendLocalPart("");
}
if (prefixToUri.isEmpty()) {
cacheNamespaces();
}
URI uri = prefixToUri.get(prefix);
return uri != null ? uri : super.getNamespace(prefix);
}
@Override
public String getPrefix(URI namespace) {
if (namespace.equals(getURI().appendLocalPart(""))) {
return "";
}
if (uriToPrefix.isEmpty()) {
cacheNamespaces();
}
String prefix = uriToPrefix.get(namespace);
return prefix != null ? prefix : super.getPrefix(namespace);
}
protected void clearNamespaceCache() {
uriToPrefix.clear();
prefixToUri.clear();
}
protected void cacheNamespaces() {
for (INamespace ns : getAllModelNamespaces()) {
// in case of many prefixes for the same URI
// use the lexicographically first prefix
String prefix = uriToPrefix.get(ns.getURI());
if (prefix == null || prefix.compareTo(ns.getPrefix()) > 0) {
uriToPrefix.put(ns.getURI(), ns.getPrefix());
}
prefixToUri.put(ns.getPrefix(), ns.getURI());
}
}
@Override
protected void finalize() throws Throwable {
// remove all state if weak reference to this
// entity manager is gc'ed
ModelSupport.this.state.remove();
super.finalize();
}
};
injector.injectMembers(manager);
manager.addDecorator(new ModelInjector());
managerRef = new WeakReference<>(manager);
}
}
}
return manager;
}
}
protected EntityVar<State> state;
@Inject
private Injector injector;
private volatile IModelSet.Internal modelSet;
protected State state() {
synchronized (state) {
State s = state.get();
if (s == null) {
state.set(s = new State());
}
return s;
}
}
/*
* documentation inherited
*/
@Override
public void addImport(URI uri, String prefix) {
try {
IEntityManager manager = getManager();
boolean isActive = manager.getTransaction().isActive();
try {
if (!isActive) {
manager.getTransaction().begin();
}
IOntology ontology = getOntology();
if (!manager.hasMatch(ontology, RDF.PROPERTY_TYPE, OWL.TYPE_ONTOLOGY)) {
manager.add(new Statement(ontology, RDF.PROPERTY_TYPE, OWL.TYPE_ONTOLOGY));
}
if (prefix != null && prefix.trim().length() > 0) {
manager.setNamespace(prefix, uri.appendLocalPart(""));
}
manager.add(new Statement(ontology, OWL.PROPERTY_IMPORTS, uri));
if (!isActive) {
manager.getTransaction().commit();
unloadManager();
}
} catch (Exception e) {
if (!isActive && manager != null) {
manager.getTransaction().rollback();
}
throw e;
}
} catch (Exception e) {
throw new KommaException(e);
}
}
/*
* documentation inherited
*/
@Override
public void delete(Map<?, ?> options) throws IOException {
getURIConverter().delete(getURI(), mergeMaps(options, getDefaultDeleteOptions()));
unload();
getModelSet().getModels().remove(this);
}
/*
* documentation inherited
*/
protected void doUnload() {
getErrors().clear();
getWarnings().clear();
unloadManager();
}
/*
* documentation inherited
*/
@Override
public void fireNotifications(Collection<? extends INotification> notifications) {
getModelSet().fireNotifications(notifications);
}
protected Map<?, ?> getDefaultDeleteOptions() {
return null;
}
protected Map<?, ?> getDefaultLoadOptions() {
return null;
}
protected Map<?, ?> getDefaultSaveOptions() {
return null;
}
@Override
public Set<IDiagnostic> getErrors() {
return getModelErrors();
}
/*
* documentation inherited
*/
@Override
public IEntityManager getManager() {
return state().manager();
}
/*
* documentation inherited
*/
@Override
public IModelSet.Internal getModelSet() {
IModelSet.Internal modelSet = this.modelSet;
if (modelSet == null) {
this.modelSet = modelSet = (IModelSet.Internal) getEntityManager()
.createQuery("SELECT DISTINCT ?ms WHERE { ?ms <http://enilink.net/vocab/komma/models#model> ?m }")
.setParameter("m", getBehaviourDelegate()).getSingleResult(IModelSet.class);
}
return modelSet;
}
@Override
public Set<URI> getImports() {
Set<URI> importedModels = state().importedModels;
if (importedModels == null) {
importedModels = new HashSet<>();
try {
IDataManager dm = getModelSet().getDataManagerFactory().get();
try {
// retrieve imported ontologies while filtering those which
// are likely already contained within this model
IOntology ontology = getOntology();
IExtendedIterator<IReference> imports = dm
.createQuery(
ISparqlConstants.PREFIX
+ " SELECT ?import WHERE { ?ontology owl:imports ?import FILTER NOT EXISTS { ?import a owl:Ontology } }",
ontology.getURI().toString(), false, ontology.getURI())
.evaluate().mapWith(new IMap<Object, IReference>() {
@Override
public IReference map(Object value) {
return (IReference) ((IBindings<?>) value).get("import");
}
});
while (imports.hasNext()) {
URI uri = imports.next().getURI();
if (uri != null) {
importedModels.add(uri);
}
}
} catch (Throwable e) {
throw e;
} finally {
dm.close();
}
} catch (Throwable e) {
throw new KommaException(e);
}
state().importedModels = importedModels;
}
return importedModels;
}
@Override
public boolean demandLoadImport(URI imported) {
return true;
}
@Override
public Set<URI> getImportsClosure() {
// duplicated from getModuleClosure, except includeModule() call
Set<URI> seen = new HashSet<>();
Queue<IModel> queue = new LinkedList<>();
queue.add(getBehaviourDelegate());
while (!queue.isEmpty()) {
IModel model = queue.remove();
for (URI imported : model.getImports()) {
if (seen.add(imported)) {
try {
boolean demandLoad = getBehaviourDelegate().demandLoadImport(imported);
queue.add(getModelSet().getModel(imported, demandLoad));
} catch (Throwable e) {
getErrors().add(new DiagnosticWrappedException(getURI().toString(),
new KommaException("Error while loading import: " + imported, e)));
}
}
}
}
return seen;
}
@Override
public synchronized KommaModule getModuleClosure() {
KommaModule moduleClosure = state().moduleClosure;
if (moduleClosure == null) {
state().moduleClosure = moduleClosure = new KommaModule(ModelSupport.class.getClassLoader());
moduleClosure.addWritableGraph(getURI());
// add support for IObject interface
moduleClosure.addConcept(IObject.class);
moduleClosure.addBehaviour(ObjectSupport.class);
// include basic roles
KommaModule modelSetModule = getModelSet().getModule();
if (modelSetModule != null) {
moduleClosure.includeModule(modelSetModule);
}
// include modules from the imports closure
Set<URI> seen = new HashSet<>();
seen.add(getURI());
Queue<IModel> queue = new LinkedList<>();
queue.add(getBehaviourDelegate());
while (!queue.isEmpty()) {
IModel model = queue.remove();
moduleClosure.includeModule(((IModel.Internal) model).getModule());
for (URI imported : model.getImports()) {
if (seen.add(imported)) {
boolean demandLoad = getBehaviourDelegate().demandLoadImport(imported);
IModel importedModel = null;
try {
importedModel = getModelSet().getModel(imported, demandLoad);
} catch (Throwable e) {
getErrors().add(new DiagnosticWrappedException(getURI().toString(),
new KommaException("Error while loading import: " + imported, e)));
}
// try to re-fetch model if load-on-demand is requested
// and
// the initial loading has been failed
if (demandLoad && importedModel == null) {
importedModel = getModelSet().getModel(imported, false);
}
if (importedModel != null) {
queue.add(importedModel);
}
}
}
}
}
return moduleClosure;
}
@Override
public synchronized KommaModule getModule() {
KommaModule module = state().module;
if (module == null) {
state().module = module = new KommaModule(ModelSupport.class.getClassLoader());
module.addWritableGraph(getURI());
// record namespace declarations
for (Namespace ns : getModelNamespaces()) {
module.addNamespace(ns.getPrefix(), ns.getURI());
}
String modelUri = getURI().toString();
// support for registering KommaModules as extensions
IExtensionPoint extensionPoint = RegistryFactoryHelper.getRegistry()
.getExtensionPoint(ModelPlugin.PLUGIN_ID, "modules");
if (extensionPoint != null) {
for (IConfigurationElement cfgElement : extensionPoint.getConfigurationElements()) {
String namespace = cfgElement.getAttribute("uri");
if (modelUri.equals(namespace)) {
try {
KommaModule extensionModule = (KommaModule) cfgElement.createExecutableExtension("class");
module.includeModule(extensionModule);
} catch (CoreException e) {
throw new KommaException("Unable to instantiate extension module", e);
}
}
}
}
// support for registering KommaModules via Guice
try {
for (KommaModule extensionModule : injector.getInstance(Key.get(new TypeLiteral<Set<KommaModule>>() {
}, Names.named(modelUri)))) {
module.includeModule(extensionModule);
}
} catch (ConfigurationException ce) {
// no bound modules found - ignore
}
}
return module;
}
@Override
public URI resolveURI(String localPart) {
return getManager().getNamespace("").appendLocalPart(localPart);
}
/*
* documentation inherited
*/
@Override
public IOntology getOntology() {
return getManager().find(getManager().getNamespace("").trimFragment(), IOntology.class);
}
IURIConverter getURIConverter() {
return getModelSet().getURIConverter();
}
@Override
public Set<IDiagnostic> getWarnings() {
return getModelWarnings();
}
@Override
public boolean isLoaded() {
if (!isModelLoaded()) {
IDataManager ds = getModelSet().getDataManagerFactory().get();
try {
// check if model is already loaded
if (ds.hasMatch(null, null, null, false, getURI())) {
((Model) getBehaviourDelegate()).setModelLoaded(true);
return true;
}
return false;
} finally {
ds.close();
}
} else {
return true;
}
}
@Override
public boolean isLoading() {
return isModelLoading();
}
@Override
public boolean isModified() {
return isModelModified();
}
/*
* documentation inherited
*/
@Override
public void load(Map<?, ?> options) throws IOException {
if (!isLoaded()) {
loadInternal(getURI(), options);
}
}
/*
* documentation inherited
*/
@Override
public void load(URI uri, Map<?, ?> options) throws IOException {
loadInternal(uri, options);
}
protected void loadInternal(URI uri, Map<?, ?> options) throws IOException {
IURIConverter uriConverter = getURIConverter();
Map<?, ?> response = options == null ? null : (Map<?, ?>) options.get(IURIConverter.OPTION_RESPONSE);
if (response == null) {
response = new HashMap<Object, Object>();
}
InputStream inputStream = null;
try {
inputStream = uriConverter.createInputStream(uri,
new ExtensibleURIConverter.OptionsMap(IURIConverter.OPTION_RESPONSE, response, options));
} catch (IOException exception) {
setModelLoaded(true);
setModelLoading(true);
getErrors().clear();
getWarnings().clear();
setModelLoading(false);
setModified(false);
// avoid choking on unknown protocols, URNs, etc.
if (exception instanceof MalformedURLException) {
ModelPlugin.getDefault().log(
new Status(IStatus.WARNING, ModelPlugin.PLUGIN_ID,
IModelStatusConstants.INTERNAL_WARNING,
"Unable to load model '" + uri + "': " + exception.getMessage(), null));
return;
}
throw exception;
}
try {
getBehaviourDelegate().load(inputStream, options);
} finally {
inputStream.close();
Long timeStamp = (Long) response.get(IURIConverter.RESPONSE_TIME_STAMP_PROPERTY);
if (timeStamp != null) {
// setTimeStamp(timeStamp);
}
}
}
/*
* documentation inherited
*/
@Override
public void removeImport(URI importedOnt) {
try {
IEntityManager manager = getManager();
boolean isActive = manager.getTransaction().isActive();
try {
if (!isActive) {
manager.getTransaction().begin();
}
for (INamespace namespace : manager.getNamespaces().toList()) {
if (importedOnt.equals(namespace.getURI())) {
manager.removeNamespace(namespace.getPrefix());
}
}
manager.remove(new Statement(getURI(), OWL.PROPERTY_IMPORTS, importedOnt));
if (!isActive) {
manager.getTransaction().commit();
unloadManager();
}
} catch (Exception e) {
if (!isActive && manager != null) {
manager.getTransaction().rollback();
}
throw e;
}
} catch (Exception e) {
if (e instanceof KommaException) {
throw (KommaException) e;
} else {
throw new KommaException(e);
}
}
}
@Override
public IObject resolve(IReference reference) {
if (reference == null) {
return null;
}
if (reference instanceof IObject && getBehaviourDelegate().equals(((IObject) reference).getModel())) {
return (IObject) reference;
}
return (IObject) getManager().find(reference);
}
/*
* documentation inherited
*/
@Override
public void save(Map<?, ?> options) throws IOException {
Object saveOnlyIfChanged = options != null && options.containsKey(OPTION_SAVE_ONLY_IF_CHANGED)
? options.get(OPTION_SAVE_ONLY_IF_CHANGED)
: getDefaultSaveOptions() != null ? getDefaultSaveOptions().get(OPTION_SAVE_ONLY_IF_CHANGED) : null;
if (saveOnlyIfChanged != null && options.get(IModel.OPTION_CONTENT_DESCRIPTION) == null) {
// add correct content type
Map<Object, Object> newOptions = new HashMap<>(options != null ? options : Collections.emptyMap());
newOptions.put(IModel.OPTION_CONTENT_DESCRIPTION,
ModelUtil.determineContentDescription(getURI(), getModelSet().getURIConverter(), options));
options = newOptions;
}
if (OPTION_SAVE_ONLY_IF_CHANGED_FILE_BUFFER.equals(saveOnlyIfChanged)) {
saveOnlyIfChangedWithFileBuffer(options);
} else if (OPTION_SAVE_ONLY_IF_CHANGED_MEMORY_BUFFER.equals(saveOnlyIfChanged)) {
saveOnlyIfChangedWithMemoryBuffer(options);
} else {
Map<?, ?> response = options == null ? null : (Map<?, ?>) options.get(IURIConverter.OPTION_RESPONSE);
if (response == null) {
response = new HashMap<Object, Object>();
}
IURIConverter uriConverter = getURIConverter();
OutputStream outputStream = uriConverter.createOutputStream(getURI(),
new ExtensibleURIConverter.OptionsMap(IURIConverter.OPTION_RESPONSE, response, options));
try {
getBehaviourDelegate().save(outputStream, options);
} finally {
outputStream.close();
Long timeStamp = (Long) response.get(IURIConverter.RESPONSE_TIME_STAMP_PROPERTY);
if (timeStamp != null) {
// setTimeStamp(timeStamp);
}
}
}
setModified(false);
}
/*
* documentation inherited
*/
protected void saveOnlyIfChangedWithFileBuffer(Map<?, ?> options) throws IOException {
File temporaryFile = File.createTempFile("ModelSaveHelper", null);
try {
URI temporaryFileURI = URIs.createFileURI(temporaryFile.getPath());
IURIConverter uriConverter = getURIConverter();
OutputStream temporaryFileOutputStream = uriConverter.createOutputStream(temporaryFileURI, null);
try {
save(temporaryFileOutputStream, options);
} finally {
temporaryFileOutputStream.close();
}
boolean equal = true;
InputStream oldContents = null;
try {
oldContents = uriConverter.createInputStream(getURI(), getDefaultDeleteOptions());
} catch (IOException exception) {
equal = false;
}
byte[] newContentBuffer = new byte[4000];
if (oldContents != null) {
try {
InputStream newContents = uriConverter.createInputStream(temporaryFileURI, null);
try {
byte[] oldContentBuffer = new byte[4000];
LOOP: for (int oldLength = oldContents.read(oldContentBuffer), newLength = newContents
.read(newContentBuffer); (equal = oldLength == newLength)
&& oldLength > 0; oldLength = oldContents.read(
oldContentBuffer), newLength = newContents.read(newContentBuffer)) {
for (int i = 0; i < oldLength; ++i) {
if (oldContentBuffer[i] != newContentBuffer[i]) {
equal = false;
break LOOP;
}
}
}
} finally {
newContents.close();
}
} finally {
oldContents.close();
}
}
if (!equal) {
Map<?, ?> response = options == null ? null : (Map<?, ?>) options.get(IURIConverter.OPTION_RESPONSE);
if (response == null) {
response = new HashMap<Object, Object>();
}
OutputStream newContents = uriConverter.createOutputStream(getURI(),
new ExtensibleURIConverter.OptionsMap(IURIConverter.OPTION_RESPONSE, response, options));
try {
InputStream temporaryFileContents = uriConverter.createInputStream(temporaryFileURI, null);
try {
for (int length = temporaryFileContents.read(
newContentBuffer); length > 0; length = temporaryFileContents.read(newContentBuffer)) {
newContents.write(newContentBuffer, 0, length);
}
} finally {
temporaryFileContents.close();
}
} finally {
newContents.close();
Long timeStamp = (Long) response.get(IURIConverter.RESPONSE_TIME_STAMP_PROPERTY);
if (timeStamp != null) {
// setTimeStamp(timeStamp);
}
}
}
} finally {
temporaryFile.delete();
}
}
/*
* documentation inherited
*/
protected void saveOnlyIfChangedWithMemoryBuffer(Map<?, ?> options) throws IOException {
IURIConverter uriConverter = getURIConverter();
class MyByteArrayOutputStream extends ByteArrayOutputStream {
public byte[] buffer() {
return buf;
}
public int length() {
return count;
}
}
MyByteArrayOutputStream memoryBuffer = new MyByteArrayOutputStream();
try {
save(memoryBuffer, options);
} finally {
memoryBuffer.close();
}
byte[] newContentBuffer = memoryBuffer.buffer();
int length = memoryBuffer.length();
boolean equal = true;
InputStream oldContents = null;
try {
oldContents = uriConverter.createInputStream(getURI(), getDefaultLoadOptions());
} catch (IOException exception) {
equal = false;
}
if (oldContents != null) {
try {
byte[] oldContentBuffer = new byte[length];
if (oldContents.read(oldContentBuffer) == length && oldContents.read() == -1) {
for (int i = 0; i < length; ++i) {
if (oldContentBuffer[i] != newContentBuffer[i]) {
equal = false;
break;
}
}
} else {
equal = false;
}
} finally {
oldContents.close();
}
}
if (!equal) {
Map<?, ?> response = options == null ? null : (Map<?, ?>) options.get(IURIConverter.OPTION_RESPONSE);
if (response == null) {
response = new HashMap<Object, Object>();
}
OutputStream newContents = uriConverter.createOutputStream(getURI(),
new ExtensibleURIConverter.OptionsMap(IURIConverter.OPTION_RESPONSE, response, options));
try {
newContents.write(newContentBuffer, 0, length);
} finally {
newContents.close();
Long timeStamp = (Long) response.get(IURIConverter.RESPONSE_TIME_STAMP_PROPERTY);
if (timeStamp != null) {
// setTimeStamp(timeStamp);
}
}
}
}
@Override
public void setLoaded(boolean isLoaded) {
setModelLoaded(isLoaded);
}
@Override
public void setModified(boolean isModified) {
setModelModified(isModified);
}
/*
* documentation inherited
*/
@Override
public void setURI(URI uri) {
if (uri != null && !uri.equals(getURI())) {
URI oldURI = getURI();
// rename model in data
getManager().rename(oldURI, uri);
// move model data
IDataManager dm = getModelSet().getDataManagerFactory().get();
try {
dm.getTransaction().begin();
getModelSet().getDataChangeSupport().setEnabled(dm, false);
dm.add(dm.match(null, null, null, false, oldURI), uri);
dm.remove(Collections.singleton(new StatementPattern(null, null, null)), oldURI);
dm.getTransaction().commit();
} finally {
if (dm.getTransaction() != null && dm.getTransaction().isActive()) {
dm.getTransaction().rollback();
}
dm.close();
}
// rename model in meta data
getEntityManager().rename(this, uri);
setModified(true);
// refresh manager
unloadManager();
}
}
/*
* documentation inherited
*/
@Override
public void unload() {
if (isLoaded()) {
setModelLoaded(false);
try {
doUnload();
} finally {
// setTimeStamp(IURIConverter.NULL_TIME_STAMP);
}
}
state.remove();
}
@Override
public synchronized void unloadManager() {
state().reset();
}
}