/* * 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.extapi.persistence; 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.Internal; import org.jetbrains.mps.annotations.Mutable; import org.jetbrains.mps.annotations.Singleton; import org.jetbrains.mps.openapi.persistence.ModelFactory; import org.jetbrains.mps.openapi.persistence.ModelFactoryType; import org.jetbrains.mps.openapi.persistence.datasource.DataSourceType; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; /** * Unlike the truly immutable core service {@link ModelFactoryCoreService} * this class has special setter methods to allow workbench model factory extensions. * These are going to disappear as API in the 3.6 version, since the public access is unnecessary. * * NB: every method work with the last-added priority -- it acts like a model factory stack * * NB: LEGACY registrations are not available here (though the opposite is a guarantee). * Thus when one uses this API he/she must be strongly convinced that the provider also used * this API not the LEGACY one * * @see org.jetbrains.mps.openapi.persistence.PersistenceFacade * * @author apyshkin * @since 29/12/16 */ @Singleton @Mutable public final class ModelFactoryService implements ModelFactoryRegistry { private static final Logger LOG = LogManager.getLogger(ModelFactoryService.class); private static final ModelFactoryCoreService CORE_SERVICE = ModelFactoryCoreService.getInstance(); private static ModelFactoryService ourInstance; private final List<ModelFactory> myCustomModelFactories = new CopyOnWriteArrayList<>(); private ModelFactoryService() { } @NotNull public static synchronized ModelFactoryService getInstance() { if (ourInstance == null) { ourInstance = new ModelFactoryService(); } return ourInstance; } /** * @internal please do not invoke these two methods -- the only caller is supposed to be ModelFactoryRegister$ModelFactoryProvider * whose purpose is pushing up the model factories registered via IDEA extension points mechanism * * it is lowering its visibility after 3.6 * AP */ @Internal @Mutable public void register(@NotNull ModelFactory factory) { if (myCustomModelFactories.contains(factory)) { LOG.error(String.format("Model factory '%s' is already registered", factory), new Throwable()); return; } myCustomModelFactories.add(factory); } @Internal @Mutable public void unregister(@NotNull ModelFactory factory) { if (!myCustomModelFactories.contains(factory)) { LOG.error(String.format("Model factory '%s' is not found", factory), new Throwable()); return; } myCustomModelFactories.remove(factory); } @NotNull private CompositeMFRegistry createComposite() { return new CompositeMFRegistry(new ModelFactoryRegistryInt(myCustomModelFactories), CORE_SERVICE); } /** * @return factories in the reverse order of registration -- from the newest to the oldest. */ @NotNull public List<ModelFactory> getFactories() { return createComposite().getFactories(); } @Nullable @Override public ModelFactory getFactoryByType(@NotNull ModelFactoryType factoryId) { return createComposite().getFactoryByType(factoryId); } @Nullable @Override public ModelFactory getDefaultModelFactory(@NotNull DataSourceType dataSourceType) { return createComposite().getDefaultModelFactory(dataSourceType); } @NotNull @Override public List<ModelFactory> getModelFactories(@NotNull DataSourceType dataSourceType) { return createComposite().getModelFactories(dataSourceType); } @NotNull @Override public List<ModelFactoryType> getFactoryTypes() { return createComposite().getFactoryTypes(); } /** * Unites two different model factory registries * Looks at the first registry then at the second one. */ private static final class CompositeMFRegistry implements ModelFactoryRegistry { private final ModelFactoryRegistry myFirst; private final ModelFactoryRegistry mySecond; public CompositeMFRegistry(ModelFactoryRegistry first, ModelFactoryRegistry second) { myFirst = first; mySecond = second; } @NotNull @Override public List<ModelFactory> getFactories() { List<ModelFactory> result = new ArrayList<>(myFirst.getFactories()); result.addAll(mySecond.getFactories()); return Collections.unmodifiableList(result); } @Nullable @Override public ModelFactory getFactoryByType(@NotNull ModelFactoryType factoryId) { ModelFactory result = myFirst.getFactoryByType(factoryId); if (result == null) { result = mySecond.getFactoryByType(factoryId); } return result; } @Nullable @Override public ModelFactory getDefaultModelFactory(@NotNull DataSourceType dataSourceType) { ModelFactory result = myFirst.getDefaultModelFactory(dataSourceType); if (result == null) { result = mySecond.getDefaultModelFactory(dataSourceType); } return result; } @NotNull @Override public List<ModelFactory> getModelFactories(@NotNull DataSourceType dataSourceType) { List<ModelFactory> result = new ArrayList<>(myFirst.getModelFactories(dataSourceType)); result.addAll(mySecond.getModelFactories(dataSourceType)); return Collections.unmodifiableList(result); } @NotNull @Override public List<ModelFactoryType> getFactoryTypes() { List<ModelFactoryType> result = new ArrayList<>(myFirst.getFactoryTypes()); result.addAll(mySecond.getFactoryTypes()); return Collections.unmodifiableList(result); } } }