/* * Copyright 2003-2016 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 jetbrains.mps.persistence; import jetbrains.mps.extapi.persistence.FileBasedModelRoot; import jetbrains.mps.extapi.persistence.FileSystemBasedDataSource; import jetbrains.mps.extapi.persistence.datasource.PreinstalledDataSourceTypes; import jetbrains.mps.extapi.persistence.SourceRoot; import jetbrains.mps.extapi.persistence.SourceRootKinds; import jetbrains.mps.extapi.persistence.datasource.DataSourceFactoryFromURL; import org.jetbrains.mps.openapi.persistence.datasource.DataSourceType; import jetbrains.mps.extapi.persistence.datasource.DataSourceFactoryFromName; import jetbrains.mps.extapi.persistence.datasource.DataSourceFactoryRuleService; import jetbrains.mps.extapi.persistence.datasource.PreinstalledURLDataSourceFactories; import jetbrains.mps.extapi.persistence.datasource.URLNotSupportedException; import jetbrains.mps.vfs.IFile; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.annotations.Immutable; import org.jetbrains.mps.openapi.model.SModelName; import org.jetbrains.mps.openapi.persistence.DataSource; import java.net.MalformedURLException; import java.net.URL; import java.util.List; import static jetbrains.mps.persistence.DataSourceFactoryBridge.CompositeResult.build; /** * Creates data sources (folder and file based) for the {@link DefaultModelRoot}. * * @author apyshkin * @since 3.5 */ @Immutable public final class DataSourceFactoryBridge { private static final Logger LOG = LogManager.getLogger(DataSourceFactoryBridge.class); @Immutable private final FileBasedModelRoot myModelRoot; public DataSourceFactoryBridge(@NotNull FileBasedModelRoot modelRoot) { myModelRoot = modelRoot; } @NotNull private static DataSourceFactoryFromName getDataSourceFactory(@NotNull DataSourceType dataSourceType) throws DataSourceFactoryNotFoundException { DataSourceFactoryFromName factory = DataSourceFactoryRuleService.getInstance().getFactory(dataSourceType); if (factory == null) { throw new DataSourceFactoryNotFoundException(dataSourceType); } return factory; } @NotNull public CompositeResult<DataSource> createFileDataSource(@NotNull SModelName modelName, @Nullable SourceRoot sourceRoot) throws DataSourceFactoryNotFoundException, NoSourceRootsInModelRootException, SourceRootDoesNotExistException { return create(modelName, sourceRoot, PreinstalledDataSourceTypes.MPS); } @NotNull public CompositeResult<DataSource> createPerRootDataSource(@NotNull SModelName modelName, @Nullable SourceRoot sourceRoot) throws DataSourceFactoryNotFoundException, NoSourceRootsInModelRootException, SourceRootDoesNotExistException { return create(modelName, sourceRoot, PreinstalledDataSourceTypes.MODEL); } /** * @param modelName new model name * @param sourceRoot if null the default (the first one) is chosen * @param dataSourceType the data source type you wish to create * @return new data source and corresponding model creation parameters (FIXME remove params?) * @throws DataSourceFactoryNotFoundException when there is no data source factory for a given type * @throws SourceRootDoesNotExistException when the given source root is not found in the model root * @throws NoSourceRootsInModelRootException when there are no source roots at all in the model root */ @NotNull public CompositeResult<DataSource> create(@NotNull SModelName modelName, @Nullable SourceRoot sourceRoot, @NotNull DataSourceType dataSourceType) throws DataSourceFactoryNotFoundException, SourceRootDoesNotExistException, NoSourceRootsInModelRootException { DataSourceFactoryFromName factory = getDataSourceFactory(dataSourceType); return create(modelName, sourceRoot, factory); } @NotNull public CompositeResult<DataSource> create(@NotNull SModelName modelName, @Nullable SourceRoot sourceRoot, @NotNull DataSourceFactoryFromName factory) throws SourceRootDoesNotExistException, NoSourceRootsInModelRootException { if (sourceRoot == null) { sourceRoot = DefaultModelRoot.Defaults.sourceRoot(myModelRoot); } checkSourceRootIsAttachedToTheModelRoot(sourceRoot); ModelCreationOptions parameters = new ParametersCalculator(myModelRoot).calculate(modelName); DataSource dataSource = factory.create(modelName, sourceRoot, myModelRoot); return build(dataSource, parameters); } private void checkSourceRootIsAttachedToTheModelRoot(@NotNull SourceRoot sourceRoot) throws NoSourceRootsInModelRootException, SourceRootDoesNotExistException { List<SourceRoot> existingSourceRoots = myModelRoot.getSourceRoots(SourceRootKinds.SOURCES); if (existingSourceRoots.isEmpty()) { throw new NoSourceRootsInModelRootException(myModelRoot); } else if (!existingSourceRoots.contains(sourceRoot)) { throw new SourceRootDoesNotExistException(sourceRoot, myModelRoot); } } /** * Creates data source from a file. * No need to calculate model name here -- it must be provided in the * data source itself. */ @Nullable CompositeResult<DataSource> create(@NotNull IFile file) { assert !file.isDirectory(); DataSource dataSource = null; try { URL url = file.getUrl(); DataSourceFactoryFromURL factory = DataSourceFactoryRuleService.getInstance().getFactory(url); if (factory == null) { throw new RuntimeException(new DataSourceFactoryNotFoundException("Could not find factory using the url " + url)); } if (factory.supports(url)) { dataSource = factory.create(url, myModelRoot); } } catch (URLNotSupportedException | MalformedURLException e) { LOG.error("Could not get URL from IFile : '" + file + "'", e); return null; } ModelCreationOptions parameters = new ParametersCalculator(myModelRoot).calculate(); if (dataSource == null) { LOG.error("Data source could not be constructed from the file: " + file, new Throwable()); return null; } return build(dataSource, parameters); } /** * A composite of the data source and creation parameters for it. * Used as a result in the factory methods of the enclosing {@link DataSourceFactoryBridge}. */ @Immutable public static final class CompositeResult<T extends DataSource> { private final T source; private final ModelCreationOptions parameters; private CompositeResult(T source0, ModelCreationOptions parameters0) { this.source = source0; this.parameters = parameters0; } @NotNull public static <T extends DataSource> CompositeResult<T> build(@NotNull T source, @NotNull ModelCreationOptions parameters) { return new CompositeResult<>(source, parameters); } @NotNull public T getDataSource() { return source; } @NotNull public ModelCreationOptions getOptions() { return parameters; } } }