/*
* 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.ide.devkit.generator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.openapi.model.SNodeReference;
import org.jetbrains.mps.openapi.module.SRepository;
import javax.swing.Icon;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* UI-related aspects of tracer nodes, not to use TracerNode from GenerationTracerView or GenerationTracerTree.
* @author Artem Tikhomirov
*/
final class TraceNodeUI {
@Nullable
private final Kind myKind;
@Nullable
private final SNodeReference myTargetNode;
private final Icon myIcon;
private List<TraceNodeUI> myChildren;
private final String myText;
/*package*/ TraceNodeUI(@NotNull String text, Icon icon, @Nullable SNodeReference targetNode) {
myKind = null;
myTargetNode = targetNode;
myIcon = icon;
myText = text;
}
/*package*/ TraceNodeUI(@Nullable Kind kind, @Nullable SNodeReference targetNode) {
myKind = kind;
myTargetNode = targetNode;
myIcon = null;
myText = null;
}
public String getText(SRepository repo) {
if (myText != null) {
return myText;
}
if (myTargetNode == null) {
if (myKind != null) {
return '<' + String.valueOf(myKind) + '>';
}
return "<unknown>";
}
SNode n = myTargetNode.resolve(repo);
if (n == null) {
// GenTrace records additions and deletions as <no node id> at corresponding side, here
// we check whether it's the node we simply can't access now, or it's consciously recorded addition/removal
if (myTargetNode.getNodeId() == null) {
if (myKind == Kind.INPUT) {
return "<no input>";
} else if (myKind == Kind.OUTPUT) {
return "<no output>";
}
// fall-through
}
return myTargetNode.toString();
}
// TODO initialize myText once in the cons
String text = String.format("[%s] %s (%s)", n.getConcept().getName(), n.getPresentation(), n.getNodeId());
if (myKind == Kind.APPROXIMATE_OUTPUT || myKind == Kind.APPROXIMATE_INPUT) {
return "[approximate location] " + text;
} else {
return text;
}
}
public Icon getIcon(SRepository contextRepo) {
if (myIcon != null) {
return myIcon;
}
return Icons.getIcon(myKind, myTargetNode.resolve(contextRepo));
}
public boolean hasChildren() {
return myChildren != null && !myChildren.isEmpty();
}
public Iterable<TraceNodeUI> getChildren() {
if (myChildren == null) {
return Collections.emptyList();
}
return myChildren;
}
public void addChild(@NotNull TraceNodeUI child) {
if (myChildren == null) {
myChildren = new ArrayList<TraceNodeUI>(5);
}
myChildren.add(child);
}
// whatever it is, MPSTreeNode requires one
public String getNodeIdentifier() {
if (myTargetNode != null) {
return String.valueOf(myTargetNode.hashCode());
} else if (myKind != null) {
return '<' + String.valueOf(myKind) + '>';
} else {
return String.valueOf(hashCode());
}
}
public boolean hasNextStep() {
return myKind == Kind.OUTPUT || myKind == Kind.APPROXIMATE_OUTPUT;
}
public boolean hasPrevStep() {
return myKind == Kind.INPUT || myKind == Kind.APPROXIMATE_INPUT;
}
@Nullable
public SNodeReference getNavigateTarget() {
return myTargetNode;
}
public enum Kind {
INPUT("input"),
OUTPUT("output"),
TEMPLATE("template"),
MACRO("macro"),
RULE("rule"),
RULE_CONSEQUENCE("rule consequence"),
SWITCH("switch"),
COPY_OPERATION("copy"),
MAPPING_SCRIPT("mapping script"),
APPROXIMATE_OUTPUT("approximate output"),
APPROXIMATE_INPUT("approximate input");
private String myText;
Kind(String text) {
myText = text;
}
public String toString() {
return myText;
}
}
}