/* * 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.components.CoreComponent; import jetbrains.mps.extapi.persistence.ModelFactoryService; import jetbrains.mps.project.MPSExtentions; import jetbrains.mps.project.ModuleId; import jetbrains.mps.project.structure.modules.ModuleReference; import jetbrains.mps.smodel.SModelId.ForeignSModelId; import jetbrains.mps.smodel.SModelId.IntegerSModelId; import jetbrains.mps.smodel.SModelId.RegularSModelId; import jetbrains.mps.smodel.SModelId.RelativePathSModelId; import jetbrains.mps.smodel.SNodePointer; import jetbrains.mps.util.annotation.ToRemove; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.mps.openapi.model.SModelId; import org.jetbrains.mps.openapi.model.SModelReference; import org.jetbrains.mps.openapi.model.SNodeId; import org.jetbrains.mps.openapi.model.SNodeReference; import org.jetbrains.mps.openapi.module.SModuleId; import org.jetbrains.mps.openapi.module.SModuleReference; import org.jetbrains.mps.openapi.persistence.FindUsagesParticipant; import org.jetbrains.mps.openapi.persistence.ModelFactory; import org.jetbrains.mps.openapi.persistence.ModelRoot; import org.jetbrains.mps.openapi.persistence.ModelRootFactory; import org.jetbrains.mps.openapi.persistence.NavigationParticipant; import org.jetbrains.mps.openapi.persistence.SModelIdFactory; import org.jetbrains.mps.openapi.persistence.SNodeIdFactory; import org.jetbrains.mps.openapi.persistence.datasource.DataSourceType; import org.jetbrains.mps.openapi.persistence.datasource.FileExtensionDataSourceType; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; /** * evgeny, 10/23/12 */ public class PersistenceRegistry extends org.jetbrains.mps.openapi.persistence.PersistenceFacade implements CoreComponent { private static final Logger LOG = LogManager.getLogger(PersistenceRegistry.class); public static final String DEFAULT_MODEL_ROOT = "default"; public static final String OBSOLETE_MODEL_ROOT = "obsolete"; public static final String JAVA_CLASSES_ROOT = "java_classes"; private final ModelFactoryService MODEL_FACTORY_SERVICE = ModelFactoryService.getInstance(); @ToRemove(version = 181) private final Map<String, ModelFactory> myLegacyFileExt2ModelFactoryMap = new ConcurrentHashMap<>(); private final Map<String, ModelRootFactory> myRootFactories = new HashMap<String, ModelRootFactory>(); private final Map<String, SModelIdFactory> myModelIdFactory = new HashMap<String, SModelIdFactory>(); private final Map<String, SNodeIdFactory> myNodeIdFactory = new HashMap<String, SNodeIdFactory>(); private final Set<FindUsagesParticipant> myFindUsagesParticipants = new LinkedHashSet<FindUsagesParticipant>(); private final Set<NavigationParticipant> myNavigationParticipants = new LinkedHashSet<NavigationParticipant>(); private boolean isDisabled = false; public static PersistenceRegistry getInstance() { return (PersistenceRegistry) INSTANCE; } @Override public ModelRootFactory getModelRootFactory(String type) { if (type == null || type.length() == 0) { throw new IllegalArgumentException("type"); } return myRootFactories.get(type); } @Override public void setModelRootFactory(String type, ModelRootFactory factory) { if (factory == null) { myRootFactories.remove(type); } else { myRootFactories.put(type, factory); } } @Deprecated @Override public ModelFactory getModelFactory(String extension) { if (myLegacyFileExt2ModelFactoryMap.containsKey(extension)) { return myLegacyFileExt2ModelFactoryMap.get(extension); } return MODEL_FACTORY_SERVICE.getDefaultModelFactory(FileExtensionDataSourceType.of(extension)); } @Deprecated @Override public ModelFactory getDefaultModelFactory() { String defaultExt = MPSExtentions.MODEL; return getModelFactory(defaultExt); } @Deprecated @Override public void setModelFactory(String extension, ModelFactory factory) { if (factory == null) { FileExtensionDataSourceType type = FileExtensionDataSourceType.of(extension); MODEL_FACTORY_SERVICE.getModelFactories(type).forEach(MODEL_FACTORY_SERVICE::unregister); } else { myLegacyFileExt2ModelFactoryMap.put(extension, factory); if (!Objects.equals(factory.getFileExtension(), extension)) { LOG.error("The model factory '" + factory + "' is trying to register using the legacy mechanism via PersistenceFacade.\n" + "The declared file extension '" + factory.getFileExtension() + "' is not equal to the passed '" + extension + "'\n" + "Please fix this.", new Throwable()); return; } MODEL_FACTORY_SERVICE.register(factory); } } @Deprecated @Override public Set<String> getModelFactoryExtensions() { Set<String> result = new HashSet<>(myLegacyFileExt2ModelFactoryMap.keySet()); for (ModelFactory modelFactory : MODEL_FACTORY_SERVICE.getFactories()) { List<DataSourceType> preferredDataSourceTypes = new ArrayList<>(modelFactory.getPreferredDataSourceTypes()); result.addAll(preferredDataSourceTypes.stream() .filter(dataSourceType -> dataSourceType instanceof FileExtensionDataSourceType) .map(dataSourceType -> ((FileExtensionDataSourceType) dataSourceType).getFileExtension()) .collect(Collectors.toList())); } return Collections.unmodifiableSet(result); } @NotNull @Override public SModuleId createModuleId(@NotNull String text) { return ModuleId.fromString(text); } @NotNull @Override public String asString(@NotNull SModuleId moduleId) { return moduleId.toString(); } @Override public String asString(@NotNull SModuleReference reference) { return reference.toString(); } @Override public SModuleReference createModuleReference(@NotNull String text) { return ModuleReference.parseReference(text); } @Override public SModuleReference createModuleReference(@NotNull SModuleId moduleId, String moduleName) { return new ModuleReference(moduleName, moduleId); } @Override public SModelId createModelId(String text) { int colon = text.indexOf(':'); if (colon == -1) { throw new IllegalArgumentException(String.format("No model id factory designator (':') in %s", text)); } final String factoryDesignator = text.substring(0, colon); SModelIdFactory factory = myModelIdFactory.get(factoryDesignator); if (factory == null) { throw new IllegalArgumentException(String.format("Unknown model id factory '%s' in %s", factoryDesignator, text)); } return factory.create(text.substring(colon + 1)); } @Override public String asString(@NotNull SModelId modelId) { // FIXME In fact, shall delegate to proper SModelIdFactory. toString here is just the first step in transition - once all code out there // stops using SModelId.toString() for persistence and switch to PersistenceFacade.asString, this implementation shall change and delegate to factory. return modelId.toString(); } @Override public SModelReference createModelReference(String text) { if (text == null) { throw new IllegalArgumentException(); } return jetbrains.mps.smodel.SModelReference.parseReference(text); } @Override public String asString(@NotNull SModelReference modelRef) { // FIXME once there's no direct uses of SModelReference#toString in persistence code, this might change to produce another format return modelRef.toString(); } @Override public SModelReference createModelReference(SModuleReference module, @NotNull SModelId modelId, @NotNull String modelName) { return new jetbrains.mps.smodel.SModelReference(module, modelId, modelName); } @Override public void setModelIdFactory(String type, SModelIdFactory factory) { if (factory == null) { myModelIdFactory.remove(type); } else { myModelIdFactory.put(type, factory); } } @Override public SNodeId createNodeId(String text) { if (text.length() == 0) return null; char c = text.charAt(0); if (c == '~' || c <= '9' && c >= '0') { // default id is supported without type+colon prefix return jetbrains.mps.smodel.SNodeId.fromString(text); } int colon = text.indexOf(':'); if (colon == -1) { throw new IllegalArgumentException(); } SNodeIdFactory factory = myNodeIdFactory.get(text.substring(0, colon)); if (factory == null) { return null; } return factory.create(text.substring(colon + 1)); } @NotNull @Override public String asString(@NotNull SNodeReference nodeRef) { return SNodePointer.serialize(nodeRef); } @Override public SNodeReference createNodeReference(String text) { return SNodePointer.deserialize(text); } @Override public void setNodeIdFactory(String type, SNodeIdFactory factory) { if (factory == null) { myNodeIdFactory.remove(type); } else { myNodeIdFactory.put(type, factory); } } @Override public Iterable<String> getTypeIds() { return Collections.unmodifiableCollection(myRootFactories.keySet()); } @Override public void addFindUsagesParticipant(FindUsagesParticipant participant) { myFindUsagesParticipants.add(participant); } @Override public void removeFindUsagesParticipant(FindUsagesParticipant participant) { myFindUsagesParticipants.remove(participant); } @Override public Set<FindUsagesParticipant> getFindUsagesParticipants() { return isDisabled ? Collections.<FindUsagesParticipant>emptySet() : Collections.unmodifiableSet(myFindUsagesParticipants); } public boolean isFastSearch() { return !isDisabled; } @Override public void addNavigationParticipant(NavigationParticipant participant) { myNavigationParticipants.add(participant); } @Override public void removeNavigationParticipant(NavigationParticipant participant) { myNavigationParticipants.remove(participant); } @Override public Set<NavigationParticipant> getNavigationParticipants() { return myNavigationParticipants; } @Override public void init() { if (INSTANCE != null) { throw new IllegalStateException("double initialization"); } INSTANCE = this; setModelRootFactory(DEFAULT_MODEL_ROOT, new DefaultModelRootFactory()); setNodeIdFactory(jetbrains.mps.smodel.SNodeId.TYPE, jetbrains.mps.smodel.SNodeId::fromString); setModelIdFactory(RegularSModelId.TYPE, jetbrains.mps.smodel.SModelId::regular); setModelIdFactory(ForeignSModelId.TYPE, jetbrains.mps.smodel.SModelId::foreign); setModelIdFactory(RelativePathSModelId.TYPE, RelativePathSModelId::new); setModelIdFactory(IntegerSModelId.TYPE, IntegerSModelId::parse); } @Override public void dispose() { INSTANCE = null; } public void disableFastFindUsages() { isDisabled = true; } public void enableFastFindUsages() { isDisabled = false; } private static class DefaultModelRootFactory implements ModelRootFactory { @NotNull @Override public ModelRoot create() { return new DefaultModelRoot(); } } }