blob: 21784385c0e832d267b70e47c8cff1dc98694fd0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.tools.ant.module.nodes;
import java.awt.Toolkit;
import javax.swing.Action;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.StyledDocument;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.apache.tools.ant.module.AntModule;
import org.apache.tools.ant.module.api.AntProjectCookie;
import org.apache.tools.ant.module.api.support.TargetLister;
import org.apache.tools.ant.module.xml.AntProjectSupport;
import org.openide.ErrorManager;
import org.openide.actions.OpenAction;
import org.openide.cookies.EditorCookie;
import org.openide.cookies.LineCookie;
import org.openide.cookies.OpenCookie;
import org.openide.filesystems.FileObject;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataObjectNotFoundException;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.PropertySupport;
import org.openide.nodes.Sheet;
import org.openide.text.Line;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
import org.openide.util.WeakListeners;
import org.openide.util.actions.SystemAction;
import org.openide.util.lookup.Lookups;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
final class AntTargetNode extends AbstractNode implements ChangeListener {
/** main project, not necessarily the one defining this target */
private final AntProjectCookie project;
private final TargetLister.Target target;
/**
* Create a new target node.
* @param project the <em>main</em> project with this target (may not be the project this is physically part of)
* @param target a representation of this target
* @param allTargets all other targets in the main project
*/
public AntTargetNode(AntProjectCookie project, TargetLister.Target target) {
super(Children.LEAF, Lookups.fixed(target, new TargetOpenCookie(target)));
this.project = project;
assert !target.isOverridden() : "Cannot include overridden targets";
this.target = target;
target.getScript().addChangeListener(WeakListeners.change(this, target.getScript()));
setName(target.getQualifiedName());
setDisplayName(target.getName());
if (target.isDescribed()) {
setShortDescription(target.getElement().getAttribute("description")); // NOI18N
setIconBaseWithExtension("org/apache/tools/ant/module/resources/EmphasizedTargetIcon.gif");
} else if (target.isDefault()) {
setIconBaseWithExtension("org/apache/tools/ant/module/resources/EmphasizedTargetIcon.gif");
} else {
setIconBaseWithExtension("org/apache/tools/ant/module/resources/TargetIcon.gif");
}
}
private static String internalTargetColor = null;
/** Loosely copied from VcsFileSystem.annotateNameHtml */
private static synchronized String getInternalTargetColor() {
if (internalTargetColor == null) {
if (UIManager.getDefaults().getColor("Tree.selectionBackground").equals(UIManager.getDefaults().getColor("controlShadow"))) { // NOI18N
internalTargetColor = "Tree.selectionBorderColor"; // NOI18N
} else {
internalTargetColor = "controlShadow"; // NOI18N
}
}
return internalTargetColor;
}
@Override
public String getHtmlDisplayName() {
// Use markup to indicate the default target, imported targets, and internal targets.
boolean imported = target.getScript() != project;
if (!imported && !target.isDefault() && !target.isInternal()) {
return null;
}
StringBuffer name = new StringBuffer(target.getName());
if (imported) {
name.insert(0, "<i>"); // NOI18N
name.append("</i>"); // NOI18N
}
if (target.isDefault()) {
name.insert(0, "<b>"); // NOI18N
name.append("</b>"); // NOI18N
}
if (target.isInternal()) {
name.insert(0, "'>"); // NOI18N
name.insert(0, getInternalTargetColor());
name.insert(0, "<font color='!"); // NOI18N
name.append("</font>"); // NOI18N
}
return name.toString();
}
public void stateChanged (ChangeEvent ev) {
firePropertyChange (null, null, null);
}
@Override
public boolean canDestroy () {
return false;
}
@Override
public boolean canRename () {
return false;
}
@Override
public boolean canCopy () {
return true;
}
@Override
public boolean canCut () {
return false;
}
@Override public Action[] getActions(boolean context) {
// XXX consider defining this path as an API
return Utilities.actionsForPath("org-apache-tools-ant-module/target-actions").toArray(new Action[0]);
}
@Override
public Action getPreferredAction() {
return SystemAction.get(OpenAction.class);
}
@Override
protected Sheet createSheet() {
Sheet sheet = super.createSheet ();
Sheet.Set props = sheet.get (Sheet.PROPERTIES);
if (props == null) {
props = Sheet.createPropertiesSet();
sheet.put(props);
}
String[] attrs = new String[] {"name", "description", "depends"}; // NOI18N
for (String attr : attrs) {
org.openide.nodes.Node.Property<?> prop = new AntProperty(target.getElement(), attr);
prop.setDisplayName (NbBundle.getMessage (AntTargetNode.class, "PROP_target_" + attr));
prop.setShortDescription (NbBundle.getMessage (AntTargetNode.class, "HINT_target_" + attr));
props.put (prop);
}
/*XXX
props.put (new BuildSequenceProperty());
*/
return sheet;
}
/**
* Node displaying the sequence of all called targets when executing.
*/
private final class BuildSequenceProperty extends PropertySupport.ReadOnly<String> {
/** Creates new BuildSequenceProperty.
*/
public BuildSequenceProperty() {
super ("buildSequence", // NOI18N
String.class,
NbBundle.getMessage (AntTargetNode.class, "PROP_target_sequence"),
NbBundle.getMessage (AntTargetNode.class, "HINT_target_sequence")
);
}
/** Computes the dependencies of all called targets and returns an ordered
* sequence String.
* @param target the target that gets executed
* /
protected String computeTargetDependencies(org.w3c.dom.Element target) {
if (target == null) {
return "";
}
// get ProjectElement
Element proj = (Element) target.getParentNode ();
if (proj == null) {
// just return current target name
return target.getAttribute ("name"); // NOI18N
} else {
// List with all called targets. the last called target is the first
// in the list
List callingList = new LinkedList();
// add this target.
callingList = addTarget (callingList, target, 0, proj);
if (callingList != null) {
return getReverseString (callingList);
} else {
return NbBundle.getMessage (AntProjectNode.class, "MSG_target_sequence_illegaldepends");
}
}
}
/** Adds a target to the List. Calls depends-on targets recursively.
* @param runningList List containing the ordered targets.
* @param target the target that should be added
* @param pos position where this target should be inserted
* @projectElement the Element of the Ant project.
*
* @return list with all targets or null if a target was not found.
* /
protected List addTarget(List runningList, Element target, int pos, Element projectElement) {
String targetName = target.getAttribute ("name"); // NOI18N
if (targetName == null) return runningList;
// search target, skip it if found
Iterator it = runningList.iterator();
while (it.hasNext()) {
if (targetName.equals (it.next())) {
return runningList;
}
}
//add target at the given position...
runningList.add(pos, targetName);
// check dependenciesList
String dependsString = target.getAttribute ("depends"); // NOI18N
if (dependsString == null) return runningList;
// add each target of the dependencies List
StringTokenizer st = new StringTokenizer(dependsString, ", "); // NOI18N
while (st.hasMoreTokens() && runningList != null) {
Element dependsTarget = getTargetElement(st.nextToken(), projectElement);
if (dependsTarget != null) {
runningList = addTarget(runningList, dependsTarget, (pos + 1), projectElement);
} else {
// target is missing, we return null to indicate that something is wrong
return null;
}
}
return runningList;
}
/** Returns the Element of a target given by its name. * /
protected Element getTargetElement(String targetName, Element projectElement) {
NodeList nl = projectElement.getChildNodes();
for (int i = 0; i < nl.getLength (); i++) {
if (nl.item (i) instanceof Element) {
Element el = (Element) nl.item (i);
if (el.getTagName().equals("target") && el.getAttribute("name").equals(targetName)) { // NOI18N
return el;
}
}
}
return null;
}
/** Returns a String of all Elements in the List in reverse order. * /
protected String getReverseString (List l) {
StringBuffer sb = new StringBuffer ();
for (int x= (l.size() - 1); x > -1; x--) {
sb.append (l.get(x));
if (x > 0) sb.append (", "); // NOI18N
}
return sb.toString ();
}
/** Returns the value of this property. */
@Override
public String getValue () {
/*XXX
return computeTargetDependencies(getTarget());
*/
return "XXX BuildSequenceProperty currently unimplemented";
}
}
private static final class TargetOpenCookie implements OpenCookie {
private final TargetLister.Target target;
public TargetOpenCookie(TargetLister.Target target) {
this.target = target;
}
public void open() {
if (target.getScript().getParseException() != null) {
Toolkit.getDefaultToolkit().beep();
return;
}
FileObject script = target.getScript().getFileObject();
assert script != null : "No build script for " + target;
EditorCookie editor;
LineCookie lines;
try {
DataObject d = DataObject.find(script);
editor = d.getLookup().lookup(EditorCookie.class);
lines = d.getLookup().lookup(LineCookie.class);
assert editor != null;
assert lines != null;
} catch (DataObjectNotFoundException e) {
throw new AssertionError(e);
}
try {
StyledDocument doc = editor.openDocument();
InputSource in = AntProjectSupport.createInputSource(script, doc);
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
final int[] line = new int[1];
final String name = target.getName();
class Handler extends DefaultHandler {
private Locator locator;
@Override
public void setDocumentLocator(Locator l) {
locator = l;
}
@Override
public void startElement(String uri, String localname, String qname, Attributes attr) throws SAXException {
if (line[0] == 0) {
if (qname.equals("target") && name.equals(attr.getValue("name"))) { // NOI18N
line[0] = locator.getLineNumber();
}
}
}
}
parser.parse(in, new Handler());
if (line[0] < 1) {
Toolkit.getDefaultToolkit().beep();
return;
}
lines.getLineSet().getCurrent(line[0] - 1).show(Line.ShowOpenType.OPEN, Line.ShowVisibilityType.FOCUS);
} catch (Exception e) {
AntModule.err.notify(ErrorManager.INFORMATIONAL, e);
return;
}
}
}
}