package jetbrains.mps.persistence;
/*Generated by MPS */
import org.jetbrains.mps.openapi.persistence.ModelFactory;
import jetbrains.mps.extapi.model.SModelPersistence;
import org.apache.log4j.Logger;
import org.apache.log4j.LogManager;
import org.jetbrains.mps.openapi.persistence.datasource.DataSourceType;
import org.jetbrains.mps.openapi.persistence.datasource.FileExtensionDataSourceType;
import org.jetbrains.mps.openapi.persistence.PersistenceFacade;
import org.jetbrains.mps.openapi.persistence.UnsupportedDataSourceException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.model.SModel;
import org.jetbrains.mps.openapi.persistence.DataSource;
import java.util.Map;
import java.io.IOException;
import org.jetbrains.mps.openapi.model.SModelReference;
import jetbrains.mps.smodel.SModelId;
import org.jetbrains.mps.openapi.module.SModuleReference;
import jetbrains.mps.util.FileUtil;
import jetbrains.mps.util.NameUtil;
import jetbrains.mps.extapi.model.CustomPersistenceSModel;
import org.jetbrains.mps.openapi.persistence.StreamDataSource;
import org.jetbrains.mps.openapi.model.SModelName;
import org.jetbrains.mps.openapi.persistence.ModelLoadingOption;
import org.jetbrains.mps.openapi.persistence.ModelCreationException;
import java.util.List;
import java.util.Collections;
import jetbrains.mps.vfs.IFile;
import jetbrains.mps.extapi.persistence.FileSystemBasedDataSource;
import java.util.ArrayList;
import org.jetbrains.mps.openapi.persistence.ModelFactoryType;
import org.jetbrains.mps.openapi.persistence.ModelLoadException;
import org.jetbrains.mps.openapi.persistence.ModelSaveException;
import jetbrains.mps.extapi.model.SModelBase;
import jetbrains.mps.extapi.model.SModelData;
import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.persistence.xml.XmlConverter;
import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory;
import java.io.InputStream;
import org.xml.sax.InputSource;
import java.io.InputStreamReader;
import org.jdom.Document;
import jetbrains.mps.util.JDOMUtil;
import org.jdom.JDOMException;
import java.util.Iterator;
import jetbrains.mps.extapi.model.PersistenceProblem;
import jetbrains.mps.util.IterableUtil;
import jetbrains.mps.text.impl.RegularTextUnit;
import jetbrains.mps.text.TextUnit;
import java.io.OutputStream;
import java.io.BufferedOutputStream;
/**
* A sample custom persistence implementation.
*/
public class XmlModelPersistence implements ModelFactory, SModelPersistence {
private static final Logger LOG = LogManager.getLogger(XmlModelPersistence.class);
private static final String XML_EXTENSION = "xml";
private static final DataSourceType XML_TYPE = FileExtensionDataSourceType.of(XML_EXTENSION);
private final PersistenceFacade myFacade = PersistenceFacade.getInstance();
public XmlModelPersistence() {
}
/**
* Instantiates a model on a given data source. Options can be used to pass additional parameters
* like stream encoding (usually, the default is utf-8), package name, containing module reference
* or module relative path of the source.
*
* @throws UnsupportedDataSourceException if the data source is not supported
* @return The loaded model
*/
@NotNull
@Override
public SModel load(@NotNull DataSource dataSource, @NotNull Map<String, String> options) throws IOException {
if (!(supports(dataSource))) {
throw new UnsupportedDataSourceException(dataSource);
}
String moduleRef = options.get(ModelFactory.OPTION_MODULEREF);
boolean contentOnly = "true".equals(options.get(ModelFactory.OPTION_CONTENT_ONLY));
SModelReference ref;
if (moduleRef == null) {
if (!(contentOnly)) {
throw new IOException("cannot load xml model from " + dataSource.getLocation());
}
ref = myFacade.createModelReference(null, SModelId.generate(), "temp");
} else {
org.jetbrains.mps.openapi.model.SModelId id = myFacade.createModelId("path:" + getLocation(dataSource).getPath());
SModuleReference mRef = myFacade.createModuleReference(moduleRef);
String filenameNoExt = FileUtil.getNameWithoutExtension(getLocation(dataSource).getName());
ref = myFacade.createModelReference(mRef, id, NameUtil.namespaceFromPath(filenameNoExt));
}
return new CustomPersistenceSModel(ref, (StreamDataSource) dataSource, this);
}
@NotNull
@Override
public SModel create(@NotNull DataSource source, @NotNull SModelName name, @NotNull ModelLoadingOption... options) throws UnsupportedDataSourceException, ModelCreationException {
throw new UnsupportedOperationException();
}
@NotNull
@Override
public List<DataSourceType> getPreferredDataSourceTypes() {
return Collections.singletonList(XML_TYPE);
}
private IFile getLocation(DataSource dataSource) {
FileSystemBasedDataSource source = (FileSystemBasedDataSource) dataSource;
IFile dataSourceFile = new ArrayList<IFile>(source.getAffectedFiles()).get(0);
return dataSourceFile;
}
@NotNull
@Override
public ModelFactoryType getType() {
return XmlModelPersistence.XmlModelPersistenceType.INSTANCE;
}
public enum XmlModelPersistenceType implements ModelFactoryType {
INSTANCE();
@NotNull
@Override
public String getFormatTitle() {
return "XML Language Persistence";
}
}
@NotNull
@Override
public SModel load(@NotNull DataSource source, @NotNull ModelLoadingOption... options) throws UnsupportedDataSourceException, ModelLoadException {
throw new UnsupportedOperationException();
}
@Override
public boolean supports(@NotNull DataSource source) {
return source instanceof FileSystemBasedDataSource && source instanceof StreamDataSource;
}
/**
* Creates a new empty model.
*
* @throws UnsupportedDataSourceException if the data source is not supported
* @throws IOException if the model cannot be created
*/
@NotNull
@Override
public SModel create(@NotNull DataSource dataSource, @NotNull Map<String, String> options) throws IOException {
if (!(supports(dataSource))) {
throw new UnsupportedDataSourceException(dataSource);
}
String moduleRef = options.get(ModelFactory.OPTION_MODULEREF);
String modelName = options.get(ModelFactory.OPTION_MODELNAME);
if (moduleRef == null || modelName == null) {
throw new IOException("cannot create xml model from " + dataSource.getLocation());
}
org.jetbrains.mps.openapi.model.SModelId id = myFacade.createModelId("path:" + getLocation(dataSource).getPath());
SModuleReference mref = myFacade.createModuleReference(moduleRef);
if (mref == null) {
throw new IOException("cannot create xml model for " + moduleRef);
}
SModelReference ref = myFacade.createModelReference(mref, id, modelName);
return new CustomPersistenceSModel(ref, (StreamDataSource) dataSource, this);
}
/**
* Indicates, whether the supplied data source can be used to hold models created by this factory.
*/
@Override
public boolean canCreate(@NotNull DataSource dataSource, @NotNull Map<String, String> options) {
if (!(supports(dataSource))) {
return false;
}
return true;
}
/**
* Saves the model in the factory-specific format (including conversion when needed).
*/
@Override
public void save(@NotNull SModel model, @NotNull DataSource dataSource) throws ModelSaveException, IOException {
if (!(supports(dataSource))) {
throw new UnsupportedDataSourceException(dataSource);
}
writeModel(((SModelBase) model).getSModel(), (StreamDataSource) dataSource);
}
/**
* Checks if the source content is outdated and needs to be upgraded.
*/
@Override
public boolean needsUpgrade(@NotNull DataSource dataSource) throws IOException {
return false;
}
/**
* Loads the model content, and saves it back in the up-to-date format.
*/
@Override
public void upgrade(@NotNull DataSource dataSource) throws IOException {
}
/**
* returns true if plain text is not enough to represent stored data.
*/
@Override
public boolean isBinary() {
return false;
}
/**
* returns the file extension this factory is registered on
*/
@Override
public String getFileExtension() {
return XML_EXTENSION;
}
/**
* User-readable title of the storage format.
*/
@Override
@NotNull
public String getFormatTitle() {
return "XML File";
}
/**
* Creates an empty model
*/
@Override
public SModelData createEmpty(SModelReference reference, StreamDataSource source) {
jetbrains.mps.smodel.SModel sModel = new jetbrains.mps.smodel.SModel(reference);
String name = reference.getModelName();
if (reference.getModelId() instanceof SModelId.RelativePathSModelId) {
name = FileUtil.getNameWithoutExtension(((SModelId.RelativePathSModelId) reference.getModelId()).getFileName());
}
SNode xmlFile = XmlConverter.newDocument(name);
sModel.addLanguage(MetaAdapterFactory.getLanguage(0x479c7a8c02f943b5L, 0x9139d910cb22f298L, "jetbrains.mps.core.xml"));
sModel.addRootNode(xmlFile);
return sModel;
}
/**
* Reads the model
*/
@Override
public SModelData readModel(SModelReference reference, StreamDataSource source) throws IOException {
InputStream in = null;
try {
String name = reference.getModelName();
if (reference.getModelId() instanceof SModelId.RelativePathSModelId) {
name = FileUtil.getNameWithoutExtension(((SModelId.RelativePathSModelId) reference.getModelId()).getFileName());
}
in = source.openInputStream();
InputSource inputSource = new InputSource(new InputStreamReader(in, FileUtil.DEFAULT_CHARSET));
Document document = JDOMUtil.loadDocument(inputSource);
SNode xmlFile = XmlConverter.convertDocument(name, document);
jetbrains.mps.smodel.SModel sModel = new jetbrains.mps.smodel.SModel(reference);
sModel.addLanguage(MetaAdapterFactory.getLanguage(0x479c7a8c02f943b5L, 0x9139d910cb22f298L, "jetbrains.mps.core.xml"));
sModel.addRootNode(xmlFile);
return sModel;
} catch (JDOMException e) {
throw new IOException("cannot read " + source.getLocation(), e);
} finally {
FileUtil.closeFileSafe(in);
}
}
/**
* Saves the model
*/
@Override
public void writeModel(SModelData model, StreamDataSource source) throws IOException, ModelSaveException {
Iterator<SNode> iterator = model.getRootNodes().iterator();
SNode root = (iterator.hasNext() ? iterator.next() : null);
if (root == null) {
throw new ModelSaveException("cannot save empty model", Collections.<SModel.Problem>singletonList(new PersistenceProblem(SModel.Problem.Kind.Save, "cannot save empty model", null, true)));
}
// TODO check concepts
if (IterableUtil.copyToList(model.getRootNodes()).size() > 1) {
throw new ModelSaveException("cannot save more than one root into .xml file", Collections.<SModel.Problem>singletonList(new PersistenceProblem(SModel.Problem.Kind.Save, "cannot save more than one root into .xml file", null, true, -1, -1, root)));
}
RegularTextUnit tu = new RegularTextUnit(root, "dummy.xml");
tu.generate();
if (tu.getState() != TextUnit.Status.Generated) {
throw new ModelSaveException("cannot save xml root", Collections.<SModel.Problem>singleton(new PersistenceProblem(SModel.Problem.Kind.Save, "Failed to generate text, status is " + tu.getState(), null, true)));
}
byte[] content = tu.getBytes();
OutputStream stream = new BufferedOutputStream(source.openOutputStream());
try {
stream.write(content);
stream.flush();
} finally {
FileUtil.closeFileSafe(stream);
}
}
}