/* * Copyright 2003-2012 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jetbrains.mps.openapi.persistence; import jetbrains.mps.util.annotation.ToRemove; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.model.SModel; import org.jetbrains.mps.openapi.model.SModelName; import org.jetbrains.mps.openapi.persistence.datasource.DataSourceType; import org.jetbrains.mps.openapi.persistence.datasource.FileExtensionDataSourceType; import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Map; /** * Represents a data source loading/saving/upgrading strategy. * * The location resides rather on the {@link DataSource} * side than here while the storage parameters belong here. * * NB: The <code>ModelFactory</code> knows about <code>DataSource</code> but * not vice versa. * * Creates/upgrades/saves/loads models (instances of SModel) from data sources. * * NB: the default implementation of the methods added in 3.5 will be dropped at the 2017.3 * minor release. Please do not linger to pass on the new API. * * @author apyshkin */ public interface ModelFactory { // --- default options --- /** * @deprecated not used currently */ @ToRemove(version = 2017.2) @Deprecated String OPTION_MODULEREF = "moduleReference"; /** * @deprecated not used currently */ @ToRemove(version = 2017.2) @Deprecated String OPTION_PACKAGE = "package"; /** * @deprecated not in use anymore */ @ToRemove(version = 2017.2) @Deprecated String OPTION_RELPATH = "relativePath"; /** * Denotes a model name, used as a key in the <code>Map<String, String></code> parameter * in the {@link #create(DataSource, Map)} methods. * * @deprecated this option is unnecessary. Use {@link #create(DataSource, SModelName, ModelLoadingOption...)} instead. */ @ToRemove(version = 2017.3) @Deprecated String OPTION_MODELNAME = "modelName"; /** * Boolean value, indicates we don't care to build complete model on load, rather read content as-is, * and tread loaded model as mere container for nodes, <code>SModelData</code>-like. * We use this mechanism from merge driver and various tools that are going to access nodes from * the model but are not going to expose this model anywhere else. * * @deprecated String option is not informatory. Use a {@link ContentOption} instead */ @ToRemove(version = 2017.3) @Deprecated String OPTION_CONTENT_ONLY = "contentOnly"; // --- API --- /** * 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. * * @return The loaded model * @throws UnsupportedDataSourceException if the data source is not supported * @deprecated <code>Map<String,String></code> is hardly perceivable. Please use * rather {@link #load(DataSource, ModelLoadingOption...)} instead */ @ToRemove(version = 2017.3) @Deprecated @NotNull SModel load(@NotNull DataSource dataSource, @NotNull Map<String, String> options) throws IOException; /** * Creates a new empty model. * Implementor must throw <code>UnsupportedDataSourceException</code> if * #canCreate returns false. * * @param dataSource if null then the default data source is created for the supplied model name * @param options must content {@link #OPTION_MODELNAME} mapping. * * @throws UnsupportedDataSourceException if the data source is not supported, in other words * {@link #canCreate(DataSource, Map)} returns false * @throws IOException if the model cannot be created for some other reasons * * @deprecated use more flexible {@link #create(DataSource, SModelName, ModelLoadingOption...)} instead */ @ToRemove(version = 2017.3) @Deprecated @NotNull SModel create(@NotNull DataSource dataSource, @NotNull Map<String, String> options) throws IOException; /** * Indicates, whether the supplied data source can be used to hold models created by this factory. * * --------- * Currently this method implementations are quite shallow and de facto it does not work at all * as the API designer must have suggested. Now the real semantics of this method is whether the data * source is supported by the <code>ModelFactory</code>. * I suggest to use the honest {@link #supports} method and catch the {@link ModelCreationException} post factum rather then * do the check beforehand. * AP * --------- * * @deprecated rather try calling {@link #create(DataSource, SModelName, ModelLoadingOption...)} method and * catching the {@link ModelCreationException} during creation or * use {@link #supports(DataSource)} method to acknowledge whether this data source is supported at all. */ @ToRemove(version = 2017.3) @Deprecated boolean canCreate(@NotNull DataSource dataSource, @NotNull Map<String, String> options); /** * Determines whether the provided data source is maintained by this model factory instance. * Call it before calling load/create/upgrade methods in order to avoid the {@link UnsupportedDataSourceException}. * * @return true iff the given data source can be managed by this factory */ default boolean supports(@NotNull DataSource dataSource) { return true; } /** * Creates a new model with the supplied <code>modelName</code> on the given <code>DataSource</code>. * Might consider additional options provided in the <code>options</code> varargs. * [General rule: options.contain(SomeOption) => SomeOption is on] * * @param dataSource -- location to create the model in * @param modelName -- a new model name (note that it might be constructed easily from full-qualified <code>String</code>) * @param options -- custom options * @see ModelLoadingOption * @return newly created model * * @throws UnsupportedDataSourceException iff {@link #supports(DataSource)} returns false * @throws ModelCreationException iff there was an irrecoverable problem during creation (for instance model file already exists) */ @NotNull default SModel create(@NotNull DataSource dataSource, @NotNull SModelName modelName, @NotNull ModelLoadingOption... options) throws UnsupportedDataSourceException, ModelCreationException { try { Map<String, String> options0 = Collections.singletonMap(OPTION_MODELNAME, modelName.getValue()); return create(dataSource, options0); } catch (IOException e) { throw new ModelCreationException("Default #create implementation failed. Please implement.", Collections.emptyList(), e); } } /** * Loads an existing model from the given <code>DataSource</code>. * Might consider additional options provided in the <code>options</code> varargs. * [General rule: options.contain(SomeOption) => SomeOption is on] * * @param dataSource -- location to load model from * @param options -- custom options * @see ModelLoadingOption * @return loaded model * * @throws UnsupportedDataSourceException iff {@link #supports(DataSource)} returns false * @throws ModelLoadException iff there was an irrecoverable load problem (for instance format was broken or unrecognized) */ @NotNull default SModel load(@NotNull DataSource dataSource, @NotNull ModelLoadingOption... options) throws UnsupportedDataSourceException, ModelLoadException { try { return load(dataSource, Collections.emptyMap()); } catch (IOException e) { throw new ModelLoadException("Default #load implementation failed. Please implement.", Collections.emptyList(), e); } } /** * Checks if the source content is outdated and needs to be upgraded. * * @throws UnsupportedDataSourceException if the data source is not supported */ boolean needsUpgrade(@NotNull DataSource dataSource) throws IOException; /** * Loads the model content, and saves it back in the up-to-date format. * * @throws UnsupportedDataSourceException if the data source is not supported */ void upgrade(@NotNull DataSource dataSource) throws IOException; /** * Saves the model in the factory-specific format (including conversion when needed). * * @throws UnsupportedDataSourceException if the data source is not supported */ void save(@NotNull SModel model, @NotNull DataSource dataSource) throws UnsupportedDataSourceException, ModelSaveException, IOException; /** * returns true if plain text is not enough to represent stored data. * * // @deprecated The contract is not clear ("not enough" means what exactly?). * We would rather turn this into some marker interface than have it here */ /*@ToRemove(version = 2017.3)*/ /*@Deprecated*/ boolean isBinary(); /** * @return the file extension this factory is registered on * null if for instance model factory is associated rather with a group of files than one file. * * @deprecated Outrageous breaking of the separation location from the loading strategy principle, which is * planted in the create, load, etc. methods. * The location notion is hidden in {@link DataSource} and there is absolutely no need to expose it. * In order to work out the currently running scenarios we set up special DataSourceFactory <-> ModelFactory * relations which are exposed through the <code>DataSourceFactoryRuleService</code>, <code>ModelFactoryService</code> and * {@link #getPreferredDataSourceTypes}. */ @ToRemove(version = 2017.3) @Deprecated @Nullable String getFileExtension(); /** * User-readable title of the storage format. * * @deprecated use {@link #getType()} and {@link ModelFactoryType#getFormatTitle()} instead */ @NotNull @Deprecated @ToRemove(version = 2017.3) /*default*/ String getFormatTitle(); /*{ return getType().getFormatTitle(); }*/ /** * Returns an id which is used to get model factory by id in the * {@code ModelFactoryService}. * * @return model factory unique identification entity * * @implNote THE DEFAULT IMPLEMENTATION IS A FALLBACK STUB, PLEASE IMPLEMENT */ @NotNull default ModelFactoryType getType() { return NotImplementedModelFactoryType.INSTANCE; // return value is chosen on purpose } /** * Declares a list of preferred data source formats, * sorted from the most preferred to the less preferred data source type. * * @return a list of data source kinds which might be considered when MPS * meets a data source location and needs to choose a model factory * which is likely to support the content of the data source. * For instance if we have data source types associated with file names (e.g. prefix or suffix) * we are able to establish a file name pattern association with a specific model factory. * For example each model file which ends with '.mps_binary' suffix would be associated with the * corresponding data source type which in turn would be associated with 'MyBinaryModelFactory'. * * @implNote THE DEFAULT IMPLEMENTATION IS A FALLBACK STUB, PLEASE IMPLEMENT */ @NotNull default List<DataSourceType> getPreferredDataSourceTypes() { String fileExtension = getFileExtension(); return Collections.singletonList(FileExtensionDataSourceType.of(fileExtension)); } }