/*
* 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) {
}
}