/* * 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.smodel; import jetbrains.mps.extapi.model.SModelBase; import jetbrains.mps.smodel.loading.ModelLoadingState; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.model.SModelReference; import org.jetbrains.mps.openapi.persistence.DataSource; /** * General [openapi]SModel implementation, with proper synchronization and loading notifications, with factory method {@link #createModel()} * for subclasses to override. * * IMPLEMENTATION NOTES: * Bears 'ModelDescriptor' name to keep it consistent, where model data is smodel.SModel, and openapi.SModel implementation is * merely a proxy to model data. Once I can do similar implementation using API classes only, shall switch to use 'Model' for * these proxies (openapi.SModel) and stick to various XXXModelData for smodel.SModel. * * Lives in j.m.smodel, not j.m.extapi.model as it depends from smodel.SModel now, and I want API to use API classes only. */ public abstract class RegularModelDescriptor extends SModelBase { // FIXME SModelBase/SModelDefscriptorStub with gtSModelInternal demand we keep SModel, not SModelData private volatile jetbrains.mps.smodel.SModel mySModel; /** * left protected for subclasses that need extended control over loading process (i.e. partial/sequential model loading) */ protected final Object myLoadLock = new Object(); protected RegularModelDescriptor(@NotNull SModelReference modelReference, @NotNull DataSource dataSource) { super(modelReference, dataSource); } @Override public final jetbrains.mps.smodel.SModel getSModelInternal() { SModel copy = mySModel; if (copy != null) { return copy; } final ModelLoadingState oldState; synchronized (myLoadLock) { oldState = getLoadingState(); if (mySModel == null) { ModelLoadResult<jetbrains.mps.smodel.SModel> loadResult = createModel(); mySModel = loadResult.getModelData(); mySModel.setModelDescriptor(this); setLoadingState(loadResult.getState()); } } fireModelStateChanged(oldState, getLoadingState()); return mySModel; } @Nullable @Override protected final jetbrains.mps.smodel.SModel getCurrentModelInternal() { return mySModel; } /** * @return new model data and level it was loaded to */ @NotNull protected abstract ModelLoadResult<jetbrains.mps.smodel.SModel> createModel(); @Override protected void doUnload() { synchronized (myLoadLock) { super.doUnload(); mySModel = null; } } /** * Handy utility when the new model data is known/obtained beforehand, not through #createModel() factory */ protected void replace(@NotNull ModelLoadResult<jetbrains.mps.smodel.SModel> newModel) { final ModelLoadingState oldState; synchronized (myLoadLock) { oldState = getLoadingState(); if (mySModel != null) { mySModel.setModelDescriptor(null); mySModel.dispose(); mySModel = null; } mySModel = newModel.getModelData(); if (mySModel != null) { mySModel.setModelDescriptor(this); } setLoadingState(newModel.getState()); } fireModelStateChanged(oldState, getLoadingState()); fireModelReplaced(); } }