/* * 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.logging.Logger; import jetbrains.mps.smodel.legacy.ConceptMetaInfoConverter; import jetbrains.mps.util.InternUtil; import jetbrains.mps.util.WeakSet; import jetbrains.mps.util.annotation.ToRemove; import org.apache.log4j.Level; import org.apache.log4j.LogManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.annotations.Immutable; 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; import org.jetbrains.mps.openapi.model.SNodeId; import org.jetbrains.mps.openapi.model.SNodeReference; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.Stack; public abstract class SReference implements org.jetbrains.mps.openapi.model.SReference { public static final SReference[] EMPTY_ARRAY = new SReference[0]; private static final Set<SReference> ourErrorReportedRefs = new WeakSet<SReference>(); private final static ThreadLocal<Boolean> ourLoggingOff = new ThreadLocal<Boolean>() { @Override protected Boolean initialValue() { return false; } }; protected final SNode mySourceNode; // made protected only for assert in DynamicReference private SReferenceLink myRoleId; private volatile String myResolveInfo; /** * role must be "genuine", interned */ @Deprecated protected SReference(String role, SNode sourceNode) { mySourceNode = sourceNode; assert sourceNode != null; myRoleId = ((ConceptMetaInfoConverter) sourceNode.getConcept()).convertAssociation(role); } protected SReference(SReferenceLink role, SNode sourceNode) { myRoleId = role; mySourceNode = sourceNode; } public static SReference create(SReferenceLink id, SNode sourceNode, SNode targetNode) { if (sourceNode.getModel() != null && targetNode.getModel() != null) { // 'mature' reference return new StaticReference(id, sourceNode, targetNode.getModel().getReference(), targetNode.getNodeId(), targetNode.getName()); } return new StaticReference(id, sourceNode, targetNode); } @Deprecated @ToRemove(version = 3.2) public static SReference create(String role, SNode sourceNode, SNode targetNode) { if (sourceNode.getModel() != null && targetNode.getModel() != null) { // 'mature' reference return new StaticReference(role, sourceNode, targetNode.getModel().getReference(), targetNode.getNodeId(), targetNode.getName()); } return new StaticReference(role, sourceNode, targetNode); } public static SReference create(SReferenceLink role, SNode sourceNode, SModelReference targetModelReference, SNodeId targetNodeId) { return new StaticReference(role, sourceNode, targetModelReference, targetNodeId, null); } public static SReference create(SReferenceLink role, SNode sourceNode, SModelReference targetModelReference, SNodeId targetNodeId, String resolveInfo) { return new StaticReference(role, sourceNode, targetModelReference, targetNodeId, resolveInfo); } public static SReference create(SReferenceLink role, SNode sourceNode, SNodeReference pointer, String resolveInfo) { return create(role, sourceNode, pointer.getModelReference(), pointer.getNodeId(), resolveInfo); } @Deprecated @ToRemove(version = 3.2) public static SReference create(String role, SNode sourceNode, SModelReference targetModelReference, SNodeId targetNodeId) { return new StaticReference(role, sourceNode, targetModelReference, targetNodeId, null); } @Deprecated @ToRemove(version = 3.3) public static SReference create(String role, SNode sourceNode, SModelReference targetModelReference, SNodeId targetNodeId, String resolveInfo) { return new StaticReference(role, sourceNode, targetModelReference, targetNodeId, resolveInfo); } @Deprecated @ToRemove(version = 3.3) public static SReference create(String role, SNode sourceNode, SNodeReference pointer, String resolveInfo) { return create(role, sourceNode, pointer.getModelReference(), pointer.getNodeId(), resolveInfo); } @Deprecated @ToRemove(version = 3.3) public static SReference create(String role, SNode sourceNode, SNode targetNode, String resolveInfo) { SReference ref = create(role, sourceNode, targetNode); ref.setResolveInfo(resolveInfo); return ref; } /** * @return Whether logging was really disabled by this call, i.e. it wasn't already disabled before */ public static boolean disableLogging() { boolean wasOff = ourLoggingOff.get(); ourLoggingOff.set(true); return !wasOff; } public static void enableLogging() { ourLoggingOff.set(false); } public static SNode getTargetNodeSilently(org.jetbrains.mps.openapi.model.SReference ref) { boolean needToEnableLogging = false; try { needToEnableLogging = disableLogging(); return ref.getTargetNode(); } finally { if (needToEnableLogging) { enableLogging(); } } } @Override @Deprecated @ToRemove(version = 3.2) public String getRole() { return myRoleId.getRoleName(); } @Deprecated @ToRemove(version = 3.2) public void setRole(String newRole) { myRoleId = ((ConceptMetaInfoConverter) mySourceNode.getConcept()).convertAssociation(newRole); } @Override public SReferenceLink getLink() { return myRoleId; } @Override public SNode getSourceNode() { return mySourceNode; } //------------------------- @Override public final SNode getTargetNode() { return getTargetNode_internal(); } @Override public SNodeReference getTargetNodeReference() { return new SNodePointer(getTargetSModelReference(), getTargetNodeId()); } //-------- factory methods ----------- @Override @Nullable public abstract SModelReference getTargetSModelReference(); @Deprecated /** * Use method in SReferenceBase class, as when you change ref, you know what ref it is * @Deprecated in 3.0 */ public abstract void setTargetSModelReference(@NotNull SModelReference targetModelReference); @Override @Nullable public SNodeId getTargetNodeId() { SNode targetNode = getTargetNode(); return targetNode == null ? null : targetNode.getNodeId(); } public void makeDirect() { } public boolean makeIndirect() { return false; } public String getResolveInfo() { return myResolveInfo; } //-------- error logging ----------- public void setResolveInfo(String info) { myResolveInfo = InternUtil.intern(info); } protected abstract SNode getTargetNode_internal(); /** * prints error to log * @param onlyWarn if true then warning must be printed out. Must be true almost always. */ protected final void error(String message, boolean onlyWarn, ProblemDescription... problems) { if (!ourLoggingOff.get()) { //skip errors in java stubs because they can have reference to classes that doesn't present in the class path SModel model = getSourceNode().getModel(); if (model != null && SModelStereotype.isStubModel(model)) { return; } synchronized (ourErrorReportedRefs) { if (!ourErrorReportedRefs.contains(this)) { ourErrorReportedRefs.add(this); String msg = String.format("Could not resolve reference '%s' from %s.", getRole(), getSourceNode()); msg += "\n" + getSourceNode().getReference(); if (message != null) { msg += "\n" + " -- " + message; } // fixme AP: multiline log messages is a bad style Logger log = Logger.wrap(LogManager.getLogger(this.getClass())); if (onlyWarn) log.warning(msg); else log.error(msg); if (problems != null) { for (ProblemDescription pd : problems) { if (onlyWarn) log.warning(pd.getMessage(), pd.getNode()); else log.error(pd.getMessage(), pd.getNode()); } } } } } } @Immutable public static final class ProblemDescription { private final SNodeReference myNode; private final String myMessage; public ProblemDescription(@NotNull SNodeReference node, @NotNull String message) { myNode = node; myMessage = message; } @NotNull public SNodeReference getNode() { return myNode; } public String getMessage() { return myMessage; } } }