/* * Copyright 2003-2015 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.smodel.references.ImmatureReferences; import jetbrains.mps.util.InternUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.language.SReferenceLink; import org.jetbrains.mps.openapi.model.SModel; import org.jetbrains.mps.openapi.model.SModelReference; import org.jetbrains.mps.openapi.model.SNode; /** * Igor Alshannikov * Sep 28, 2007 */ public abstract class SReferenceBase extends SReference { protected volatile SNode myImmatureTargetNode; // young private volatile SModelReference myTargetModelReference; // mature protected SReferenceBase(SReferenceLink role, SNode sourceNode, @Nullable SModelReference targetModelReference, @Nullable SNode immatureTargetNode) { super(role, sourceNode); // if ref is 'mature' then 'targetModelRefernce' is either NOT NULL, or it is broken external reference, or it is dynamic reference myTargetModelReference = targetModelReference; // 'young' reference if (immatureTargetNode != null) { ImmatureReferences.getInstance().add(this); } myImmatureTargetNode = immatureTargetNode; } @Deprecated protected SReferenceBase(String role, SNode sourceNode, @Nullable SModelReference targetModelReference, @Nullable SNode immatureTargetNode) { super(InternUtil.intern(role), sourceNode); // if ref is 'mature' then 'targetModelRefernce' is either NOT NULL, or it is broken external reference, or it is dynamic reference myTargetModelReference = targetModelReference; // 'young' reference if (immatureTargetNode != null) { ImmatureReferences.getInstance().add(this); } myImmatureTargetNode = immatureTargetNode; } @Override public SModelReference getTargetSModelReference() { SNode immatureNode = myImmatureTargetNode; if (immatureNode == null || makeIndirect()) { return myTargetModelReference; } SModel model = immatureNode.getModel(); return model == null ? null : model.getReference(); } @Override public synchronized void setTargetSModelReference(@NotNull SModelReference modelReference) { if (!makeIndirect()) { makeMature(); // hack: make mature anyway: only can store ref to target model in 'mature' ref. } myTargetModelReference = modelReference; } public boolean isDirect() { return myImmatureTargetNode != null; } @Override public final boolean makeIndirect() { return makeIndirect(false); } @Override public void makeDirect() { if (myImmatureTargetNode != null) { return; } myImmatureTargetNode = SReference.getTargetNodeSilently(this); if (myImmatureTargetNode != null) { ImmatureReferences.getInstance().add(this); } } public synchronized final boolean makeIndirect(boolean force) { if (myImmatureTargetNode == null) { return true; } ImmatureReferences.getInstance().remove(this); SNode sourceNode = getSourceNode(); SModel sourceModel = sourceNode.getModel(); if (sourceModel == null) { return myImmatureTargetNode == null; } if (sourceNode.getModel() != null && myImmatureTargetNode.getModel() != null) { // convert 'young' reference to 'mature' makeMature(); } if (force && myImmatureTargetNode != null) { if (sourceNode.getModel() != null) { error("Impossible to resolve immature reference", false, new ProblemDescription(myImmatureTargetNode.getReference(), "ImmatureTargetNode(modelID: " + (myImmatureTargetNode.getModel() == null ? "null" : myImmatureTargetNode.getModel().toString()) + ", nodeID: " + myImmatureTargetNode.getNodeId().toString() + "): isRegistered = " + (myImmatureTargetNode.getModel() != null) ) ); myImmatureTargetNode = null; } } return myImmatureTargetNode == null; } protected synchronized void makeMature() { if (myImmatureTargetNode == null) { return; } final SNode immatureNode = myImmatureTargetNode; myImmatureTargetNode = null; adjustMature(immatureNode); setTargetSModelReference(immatureNode.getModel().getReference()); setResolveInfo(getResolveInfo(immatureNode)); } @Nullable protected String getResolveInfo(SNode immatureNode) { // FIXME need a better approach to keep names of predefined attributes; // however, a dependency to generated kernel module is an overkill for the sake of few strings String value = immatureNode.getProperty("resolveInfo"); if (value != null) { return value; } return immatureNode.getName(); } protected void adjustMature(SNode immatureTarget) { } }