/********************************************************************** * Copyright (c) 2005-2009 ant4eclipse project team. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Nils Hartmann, Daniel Kasmeroglu, Gerd Wuetherich **********************************************************************/ package org.ant4eclipse.ant.misc; import org.ant4eclipse.ant.platform.core.SubElementAndAttributesComponent; import org.ant4eclipse.ant.platform.core.delegate.SubElementAndAttributesDelegate; import org.ant4eclipse.ant.platform.core.task.AbstractProjectBasedTask; import org.ant4eclipse.lib.core.service.ServiceRegistryAccess; import org.ant4eclipse.lib.core.util.TextBuffer; import org.ant4eclipse.lib.core.util.Utilities; import org.ant4eclipse.lib.platform.model.resource.EclipseProject; import org.ant4eclipse.lib.platform.tools.ReferencedProjectsResolverService; import org.apache.tools.ant.BuildException; import java.io.File; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.Map; /** * <p> * This task is used to create a visualisation of dependencies. * </p> * * @author Daniel Kasmeroglu (Daniel.Kasmeroglu@Kasisoft.net) */ public class VisualiseDependenciesTask extends AbstractProjectBasedTask implements SubElementAndAttributesComponent { /** * The reference type that is used for the resolving process of projects. A value of <code>null</code> means that all * reference types are being tried. */ private String[] _referencetypes; /** The location of the destination file. */ private File _destfile; /** */ private SubElementAndAttributesDelegate _subElementAndAttributesDelegate; public VisualiseDependenciesTask() { this._subElementAndAttributesDelegate = new SubElementAndAttributesDelegate(this); this._referencetypes = null; } /** * Changes the reference type used to influence the project resolving process. * * @param newreferencetype * A reference type which depends on the current configuration of a4e. */ public void setReferencetypes(String newreferencetypes) { String[] elements = null; if (newreferencetypes != null) { elements = newreferencetypes.split(","); } this._referencetypes = Utilities.cleanup(elements); } /** * Changes the location of the destination file to be stored. * * @param newfile * The new location of the destination file to be stored. */ public void setDestination(File newfile) { this._destfile = newfile; } /** * {@inheritDoc} */ public Object createDynamicElement(String name) throws BuildException { return this._subElementAndAttributesDelegate.createDynamicElement(name); } /** * {@inheritDoc} */ public List<Object> getSubElements() { return this._subElementAndAttributesDelegate.getSubElements(); } /** * {@inheritDoc} */ public Map<String, String> getSubAttributes() { return this._subElementAndAttributesDelegate.getSubAttributes(); } /** * {@inheritDoc} */ public void setDynamicAttribute(String name, String value) throws BuildException { this._subElementAndAttributesDelegate.setDynamicAttribute(name, value); } /** * {@inheritDoc} */ @Override protected void preconditions() throws BuildException { super.preconditions(); requireWorkspaceAndProjectNameSet(); if (this._referencetypes != null) { // check if we can use the provided reference type String[] allowed = getResolver().getReferenceTypes(); for (String reftype : this._referencetypes) { if (!Utilities.contains(reftype, allowed)) { throw new BuildException("The 'referencetypes' value '" + reftype + "' is not supported."); } } } } /** * {@inheritDoc} */ @Override protected void doExecute() { String[] types = getResolver().getReferenceTypes(); for (String type : types) { System.err.println("type: " + type); } if (this._referencetypes != null) { // there's a restriction provided by the user types = this._referencetypes; } EclipseProject project = getEclipseProject(); List<EclipseProject> referenced = new ArrayList<EclipseProject>(); referenced.add(project); // load the directly referenced projects referenced.addAll(getResolver().resolveReferencedProjects(project, types, getSubElements())); TextBuffer text = new TextBuffer(null, null); DependencyEmitter emitter = new DotEmitter(text); emitter.begin(); Map<String, String> namemappings = new Hashtable<String, String>(); for (int i = 0; i < referenced.size(); i++) { String name = referenced.get(i).getSpecifiedName(); namemappings.put(name, name.replace(' ', '_').replace('.', '_')); emitter.node(namemappings.get(name), name); } String current = namemappings.get(project.getSpecifiedName()); for (int i = 1; i < referenced.size(); i++) { emitter.edge(current, namemappings.get(referenced.get(i).getSpecifiedName())); } emitter.end(); Utilities.writeFile(this._destfile, text.toString(), "UTF-8"); } /** * Returns the currently registered resolver service. * * @return The currently registered resolver service. Not <code>null</code>. */ private ReferencedProjectsResolverService getResolver() { /** * @todo [09-Jul-2009:KASI] The inner convenience classes located in service interfaces should be removed. I'm just * using this shortcut here in order to support refactoring in future. */ return ServiceRegistryAccess.instance().getService(ReferencedProjectsResolverService.class); } /* * graph graphname { // The label attribute can be used to change the label of a node a [label="Foo"]; // Here, the * node shape is changed. b [shape=box]; // These edges both have different line properties a -- b -- c [color=blue]; * b -- d [style=dotted]; */ private static interface DependencyEmitter { void node(String name, String label); void edge(String from, String to); void begin(); void end(); } /* ENDINTERFACE */ private static class DotEmitter implements DependencyEmitter { private TextBuffer buffer; public DotEmitter(TextBuffer storage) { this.buffer = storage; } public void begin() { this.buffer.writeLine("digraph example {"); this.buffer.indent(); } public void edge(String from, String to) { this.buffer.writeLineF("%s -> %s;", from, to); } public void end() { this.buffer.dedent(); this.buffer.writeLine("}"); } public void node(String name, String label) { this.buffer.writeLineF("%s [label=\"%s\", shape=box];", name, label); } } /* ENDCLASS */ } /* ENDCLASS */