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); } } }