/*==========================================================================*\
| $Id: SubmissionTarget.java,v 1.3 2010/12/06 21:06:48 aallowat Exp $
|*-------------------------------------------------------------------------*|
| Copyright (C) 2006-2009 Virginia Tech
|
| This file is part of Web-CAT Electronic Submitter.
|
| Web-CAT is free software; you can redistribute it and/or modify
| it under the terms of the GNU General Public License as published by
| the Free Software Foundation; either version 2 of the License, or
| (at your option) any later version.
|
| Web-CAT is distributed in the hope that it will be useful,
| but WITHOUT ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
| GNU General Public License for more details.
|
| You should have received a copy of the GNU General Public License along
| with Web-CAT; if not, see <http://www.gnu.org/licenses/>.
\*==========================================================================*/
package org.webcat.submitter.targets;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.w3c.dom.Node;
import org.webcat.submitter.AmbiguityResolutionPolicy;
import org.webcat.submitter.ILongRunningTask;
import org.webcat.submitter.SubmissionTargetException;
import org.webcat.submitter.internal.Xml;
import org.webcat.submitter.internal.utility.PathMatcher;
//--------------------------------------------------------------------------
/**
* An abstract base class from which all the objects in the submission target
* tree are derived.
*
* @author Tony Allevato (Virginia Tech Computer Science)
* @author latest changes by: $Author: aallowat $
* @version $Revision: 1.3 $ $Date: 2010/12/06 21:06:48 $
*/
public abstract class SubmissionTarget
{
//~ Constructors ..........................................................
// ----------------------------------------------------------
/**
* Initializes fields in the SubmissionTarget.
*
* @param parent the SubmissionTarget that represents this node's parent in
* the tree
*/
protected SubmissionTarget(SubmissionTarget parent)
{
packager = DEFAULT_PACKAGER;
this.parent = parent;
name = null;
hidden = false;
ambiguityResolution = AmbiguityResolutionPolicy.EXCLUDE;
transportParams = new LinkedHashMap<String, String>();
packagerParams = new LinkedHashMap<String, String>();
otherAttributes = new LinkedHashMap<String, String>();
}
//~ Methods ...............................................................
// ----------------------------------------------------------
/**
* Gets the parent node to this node in the tree.
*
* @return the SubmissionTarget that is the parent of this target
*/
public SubmissionTarget parent()
{
return parent;
}
// ----------------------------------------------------------
/**
* Gets the root of the tree that this object is contained in.
*
* @return the root of the tree, or null if it could not be found (or if
* the top-level target was not a {@link RootTarget})
*/
public RootTarget getRoot()
{
SubmissionTarget par = this;
while (par.parent() != null)
{
par = par.parent();
}
return (par instanceof RootTarget) ? (RootTarget) par : null;
}
// ----------------------------------------------------------
/**
* Overridden by derived classes to specify whether the node may contain
* children.
*
* @return true if the node may contain children; otherwise, false
*/
public abstract boolean isContainer();
// ----------------------------------------------------------
/**
* Overridden by derived classes to specify whether the node should be
* displayed at the same level as its parent, or if it should be nested at
* a lower level. Typically a target that is a container will not be nested
* if it does not have a name, but subclasses can provide their own
* behavior.
*
* @return true if the node should be nested at a lower level in the tree;
* false if it should be displayed at the same level as its parent
*/
public abstract boolean isNested();
// ----------------------------------------------------------
/**
* Overridden by derived classes to specify whether an action can be taken
* on this node. In a wizard, for example, this would enable the
* Next/Finish button so the user can continue with the submission.
*
* @return true if the node is actionable; otherwise, false
*/
public abstract boolean isActionable();
// ----------------------------------------------------------
/**
* Overridden by derived classes to specify whether the node has been
* loaded into local memory. This is always true for most nodes except
* imported groups, which return true only if the external XML file has
* already been processed.
*
* @return true if the node is local or if it has been delay-loaded; false
* if the node is an imported group that has not let been expanded
*/
public abstract boolean isLoaded();
// ----------------------------------------------------------
/**
* Gets the name of the target.
*
* @return the name of the target
*/
public String getName()
{
return name;
}
// ----------------------------------------------------------
/**
* Sets the name of the target.
*
* @param value the name of the target
*/
public void setName(String value)
{
name = value;
}
// ----------------------------------------------------------
/**
* Gets a value indicating whether the target should be hidden in the user
* interface.
*
* @return true if the target should be hidden; otherwise, false
*/
public boolean isHidden()
{
return hidden;
}
// ----------------------------------------------------------
/**
* Sets a value indicating whether the target should be hidden in the user
* interface.
*
* @param value true if the target should be hidden; otherwise, false
*/
public void setHidden(boolean value)
{
hidden = value;
}
// ----------------------------------------------------------
/**
* Gets a value indicating whether file filter ambiguities are resolved by
* including the file or excluding it.
*
* @return one of the {@link AmbiguityResolutionPolicy} values indicating
* what to do when an ambiguity occurs
* @throws SubmissionTargetException if an error occurs while delay-loading
* the node
*/
public AmbiguityResolutionPolicy getAmbiguityResolution()
throws SubmissionTargetException
{
return ambiguityResolution;
}
// ----------------------------------------------------------
/**
* Sets a value indicating whether file filter ambiguities are resolved by
* including the file or excluding it.
*
* @param value one of the {@link AmbiguityResolutionPolicy} values
* indicating what to do when an ambiguity occurs
*/
public void setAmbiguityResolution(AmbiguityResolutionPolicy value)
{
ambiguityResolution = value;
}
// ----------------------------------------------------------
/**
* Gets the included file patterns for this node.
*
* @return an array of Strings that represent the file patterns
* @throws SubmissionTargetException if an error occurs while delay-loading
* the node
*/
public String[] getIncludedFiles() throws SubmissionTargetException
{
return (includes != null) ? includes : new String[0];
}
// ----------------------------------------------------------
/**
* Sets the included file patterns for this node.
*
* @param array an array of Strings that represent the file patterns
*/
public void setIncludedFiles(String[] array)
{
includes = (array != null) ? array.clone() : null;
}
// ----------------------------------------------------------
/**
* Gets the excluded file patterns for this node.
*
* @return an array of Strings that represent the file patterns
* @throws SubmissionTargetException if an error occurs while delay-loading
* the node
*/
public String[] getExcludedFiles() throws SubmissionTargetException
{
return (excludes != null) ? excludes : new String[0];
}
// ----------------------------------------------------------
/**
* Sets the excluded file patterns for this node.
*
* @param array an array of Strings that represent the file patterns
*/
public void setExcludedFiles(String[] array)
{
excludes = (array != null) ? array.clone() : null;
}
// ----------------------------------------------------------
/**
* Gets the required file patterns for this node.
*
* @return an array of Strings that represent the file patterns
* @throws SubmissionTargetException if an error occurs while delay-loading
* the node
*/
public String[] getRequiredFiles() throws SubmissionTargetException
{
return (required != null) ? required : new String[0];
}
// ----------------------------------------------------------
/**
* Sets the required file patterns for this node.
*
* @param array an array of Strings that represent the file patterns
*/
public void setRequiredFiles(String[] array)
{
required = (array != null) ? array.clone() : null;
}
// ----------------------------------------------------------
/**
* Recursively walks up the tree from the current node and collects a list
* of all the required file patterns for a submission at this level.
*
* @return an array of Strings representing all the required files
* @throws SubmissionTargetException if an error occurs while delay-loading
* the node
*/
public String[] getAllRequiredFiles() throws SubmissionTargetException
{
List<String> list = new ArrayList<String>();
getAllRequiredFiles(list);
String[] array = new String[list.size()];
list.toArray(array);
return array;
}
// ----------------------------------------------------------
/**
* This helper function manages the actual recursion for
* {@link #getAllRequiredFiles()}.
*
* @param list a list that will build up the results
*/
private void getAllRequiredFiles(List<String> list)
throws SubmissionTargetException
{
list.addAll(Arrays.asList(getRequiredFiles()));
if (parent != null)
{
parent.getAllRequiredFiles(list);
}
}
// ----------------------------------------------------------
/**
* Gets the value of an attribute for submission targets at this level in
* the tree. This function walks up the tree to find an inherited
* attribute, if necessary.
*
* @param attribute the name of the attribute
* @return a String containing the value of the attribute, or null if it
* is not present
* @throws SubmissionTargetException if an error occurs while delay-loading
* the node
*/
public String getAttribute(String attribute)
throws SubmissionTargetException
{
String localAttribute = getLocalAttribute(attribute);
if (localAttribute != null)
{
return localAttribute;
}
else if (parent != null)
{
return parent.getAttribute(attribute);
}
else
{
return null;
}
}
// ----------------------------------------------------------
/**
* Gets the value of an attribute for submission targets at this level in
* the tree. This function does not walk up the tree to find an inherited
* attribute--it returns the attribute specified for this node only.
*
* @param attribute the name of the attribute
* @return a String containing the value of the attribute, or null if it
* is not present
* @throws SubmissionTargetException if an error occurs while delay-loading
* the node
*/
public String getLocalAttribute(String attribute)
throws SubmissionTargetException
{
return otherAttributes.get(attribute);
}
// ----------------------------------------------------------
/**
* Sets an attribute for this node.
*
* @param attribute the name of the attribute
* @param value the value for the attribute
*/
public void setAttribute(String attribute, String value)
{
if (value != null)
{
otherAttributes.put(attribute, value);
}
else
{
otherAttributes.remove(attribute);
}
}
// ----------------------------------------------------------
/**
* Gets the transport URI for submission targets at this level in the tree.
* This function walks up the tree to find an inherited transport, if
* necessary.
*
* @return a String containing the transport URI
* @throws SubmissionTargetException if an error occurs while delay-loading
* the node
*/
public String getTransport() throws SubmissionTargetException
{
String localTransport = getLocalTransport();
if (localTransport != null)
{
return localTransport;
}
else if (parent != null)
{
return parent.getTransport();
}
else
{
return null;
}
}
// ----------------------------------------------------------
/**
* Gets the transport URI for submission targets at this level in the tree.
* This function does not walk up the tree to find an inherited
* transport--it returns the transport specified for this node only.
*
* @return a String containing the transport URI
* @throws SubmissionTargetException if an error occurs while delay-loading
* the node
*/
public String getLocalTransport() throws SubmissionTargetException
{
return transport;
}
// ----------------------------------------------------------
/**
* Sets the transport URI for this node.
*
* @param uri a String containing the transport URI
*/
public void setTransport(String uri)
{
transport = uri;
}
// ----------------------------------------------------------
/**
* Gets a map containing the transport parameter name/value pairs for this
* node. This function walks up the tree to find an inherited transport, if
* necessary.
*
* @return a Map containing the parameter names and values
* @throws SubmissionTargetException if an error occurs while delay-loading
* the node
*/
public Map<String, String> getTransportParameters()
throws SubmissionTargetException
{
String localTransport = getLocalTransport();
if (localTransport != null)
{
return getLocalTransportParameters();
}
else if (parent != null)
{
return parent.getTransportParameters();
}
else
{
return new LinkedHashMap<String, String>();
}
}
// ----------------------------------------------------------
/**
* Gets a map containing the transport parameter name/value pairs for this
* node. This function does not walk up the tree to find an inherited
* transport--it returns the transport specified for this node only.
*
* @return a Map containing the parameter names and values
* @throws SubmissionTargetException if an error occurs while delay-loading
* the node
*/
public Map<String, String> getLocalTransportParameters()
throws SubmissionTargetException
{
return transportParams;
}
// ----------------------------------------------------------
/**
* Sets the transport parameter name/value pairs for this node.
*
* @param params a Map containing the parameter names and values
*/
public void setTransportParameters(Map<String, String> params)
{
transportParams = new LinkedHashMap<String, String>(params);
}
// ----------------------------------------------------------
/**
* Gets the identifier of the packager used to submit the project. This
* function walks up the tree to find an inherited packager, if necessary.
*
* @return a String containing the packager identifier
* @throws SubmissionTargetException if an error occurs while delay-loading
* the node
*/
public String getPackager() throws SubmissionTargetException
{
String localPackager = getLocalPackager();
if (localPackager != null)
{
return localPackager;
}
else if (parent != null)
{
return parent.getPackager();
}
else
{
return null;
}
}
// ----------------------------------------------------------
/**
* Gets the identifier of the packager used to submit the project. This
* function does not walk up the tree to find an inherited packager--it
* returns the packager specified for this node only.
*
* @return a String containing the packager identifier
* @throws SubmissionTargetException if an error occurs while delay-loading
* the node
*/
public String getLocalPackager() throws SubmissionTargetException
{
return packager;
}
// ----------------------------------------------------------
/**
* Sets the packager identifier for this node.
*
* @param id a String containing the unique identifier of a packager
*/
public void setPackager(String id)
{
packager = id;
}
// ----------------------------------------------------------
/**
* Gets a map containing the packager parameter name/value pairs for this
* node.
*
* @return a Map containing the packager parameter names and values
* @throws SubmissionTargetException if an error occurs while delay-loading
* the node
*/
public Map<String, String> getPackagerParameters()
throws SubmissionTargetException
{
String localPackager = getLocalPackager();
if (localPackager != null)
{
return getLocalPackagerParameters();
}
else if (parent != null)
{
return parent.getPackagerParameters();
}
else
{
return new LinkedHashMap<String, String>();
}
}
// ----------------------------------------------------------
/**
* Gets a map containing the packager parameter name/value pairs for this
* node. This function does not walk up the tree to find an inherited
* packager--it returns the transport specified for this node only.
*
* @return A Map containing the parameter names and values
* @throws SubmissionTargetException if an error occurs while delay-loading
* the node
*/
public Map<String, String> getLocalPackagerParameters()
throws SubmissionTargetException
{
return packagerParams;
}
// ----------------------------------------------------------
/**
* Sets the packager parameter name/value pairs for this node.
*
* @param params a Map containing the parameter names and values
*/
public void setPackagerParameters(Map<String, String> params)
{
packagerParams = new LinkedHashMap<String, String>(params);
}
// ----------------------------------------------------------
/**
* Gets the children of this node. This function only considers the
* link structure of the target tree, not the nested state of any of the
* nodes.
*
* @return an array of SubmissionTargets that represent the children of the
* node
* @throws SubmissionTargetException if an error occurs while delay-loading
* the node
*/
public SubmissionTarget[] getChildren() throws SubmissionTargetException
{
return (children != null) ? children : new SubmissionTarget[0];
}
// ----------------------------------------------------------
/**
* Gets the "logical" children of this node, respecting the nested state of
* any children (so that children of a non-nested child are "pushed up"
* into the parent). This method is appropriate for determining the
* children of a node as they should be displayed in a user interface.
*
* @return an array of SubmissionTargets that represent the logical
* children of the node
* @throws SubmissionTargetException if an exception occurs
*/
public SubmissionTarget[] getLogicalChildren()
throws SubmissionTargetException
{
List<SubmissionTarget> childList = new ArrayList<SubmissionTarget>();
computeLogicalChildren(this, childList);
SubmissionTarget[] array = new SubmissionTarget[childList.size()];
childList.toArray(array);
return array;
}
// ----------------------------------------------------------
/**
* Recursively computes the children for the specified target, taking into
* account the nested state of each target.
*
* @param target the target whose children should be computed
* @param list a list that will hold the children upon returning
* @throws SubmissionTargetException
*/
private static void computeLogicalChildren(SubmissionTarget target,
List<SubmissionTarget> list) throws SubmissionTargetException
{
SubmissionTarget[] children = target.getChildren();
for (SubmissionTarget child : children)
{
if (!child.isHidden())
{
if (child.isContainer() && !child.isNested())
{
computeLogicalChildren(child, list);
}
else
{
list.add(child);
}
}
}
}
// ----------------------------------------------------------
/**
* Sets the children of this node.
*
* @param array an array of SubmissionTargets that represent the new
* children of the node
*/
public void setChildren(SubmissionTarget[] array)
{
children = (array != null) ? array.clone() : null;
}
// ----------------------------------------------------------
/**
* A helper function to ease initializing the included files from a list.
*
* @param list the list to set the included files from
*/
protected void setIncludedFiles(List<String> list)
{
includes = new String[list.size()];
list.toArray(includes);
}
// ----------------------------------------------------------
/**
* A helper function to ease initializing the excluded files from a list.
*
* @param list the list to set the excluded files from
*/
protected void setExcludedFiles(List<String> list)
{
excludes = new String[list.size()];
list.toArray(excludes);
}
// ----------------------------------------------------------
/**
* A helper function to ease initializing the required files from a list.
*
* @param list the list to set the required files from
*/
protected void setRequiredFiles(List<String> list)
{
required = new String[list.size()];
list.toArray(required);
}
// ----------------------------------------------------------
/**
* A helper function to ease initializing the children from a list.
*
* @param list the list to set the children from
*/
protected void setChildren(List<SubmissionTarget> list)
{
children = new SubmissionTarget[list.size()];
list.toArray(children);
}
// ----------------------------------------------------------
/**
* Recursively walks up the submission target tree to determine if a file
* with the specified path should be excluded from the submission.
*
* @param packageRelativePath the package-relative path to check for
* exclusion
* @return true if the file should be excluded; otherwise, false
* @throws SubmissionTargetException if an error occurs while delay-loading
* the node
*/
public boolean isFileExcluded(String packageRelativePath)
throws SubmissionTargetException
{
boolean localExclude = false;
boolean localInclude = false;
// Check to see if the file is excluded locally.
String[] exc = getExcludedFiles();
for (String patternString : exc)
{
if (new PathMatcher(patternString).matches(packageRelativePath))
{
localExclude = true;
break;
}
}
// Check to see if the file is explicitly included locally.
String[] inc = getIncludedFiles();
for (String patternString : inc)
{
if (new PathMatcher(patternString).matches(packageRelativePath))
{
localInclude = true;
break;
}
}
if (localInclude && localExclude)
{
if (ambiguityResolution == AmbiguityResolutionPolicy.EXCLUDE)
{
return true;
}
else
{
return false;
}
}
else if (localExclude)
{
return true;
}
else if (localInclude)
{
return false;
}
// If no explicit mention of the file was found,
// try going up the assignment tree.
if (parent != null)
{
return parent.isFileExcluded(packageRelativePath);
}
else
{
// If we're at the top level and the file has still not matched a
// pattern, we include it unless the top level node has any
// explicit inclusions, in which case we exclude it.
if (inc.length == 0)
{
return false;
}
else
{
return true;
}
}
}
// ----------------------------------------------------------
/**
* Used by delay-loading nodes, this function copies the entire tree rooted
* at the specified node into the current tree at the position represented
* by the node on which this method is invoked.
*
* @param target the tree from which to copy
* @throws SubmissionTargetException if there are any errors during
* delay-loading
*/
protected void copyFrom(SubmissionTarget target)
throws SubmissionTargetException
{
// Used for delay-loading. Takes the specified definition object
// and copies it into the current object.
setAmbiguityResolution(target.getAmbiguityResolution());
setChildren(target.getChildren());
setIncludedFiles(target.getIncludedFiles());
setExcludedFiles(target.getExcludedFiles());
setRequiredFiles(target.getRequiredFiles());
setTransport(target.getTransport());
setTransportParameters(target.getTransportParameters());
setPackager(target.getPackager());
setPackagerParameters(target.getPackagerParameters());
}
// ----------------------------------------------------------
/**
* Gets a set of attribute names that should not be included in the
* attribute set used by {@link #getAttribute(String)} and related
* methods.
*
* @return the set of attribute names that should not be included in the
* target's attribute set
*/
protected Set<String> getIgnoredAttributes()
{
Set<String> ignored = new HashSet<String>();
ignored.add("name");
ignored.add("hidden");
return ignored;
}
// ----------------------------------------------------------
/**
* Subdivides the next unit of work in the specified long-running task into
* a subtask containing as many units of work as there are children in an
* XML node.
*
* @param parentNode the node whose children will determine the length of
* the subtask
* @param task the long-running task to subdivide
*/
protected void startSubtaskForChildNodes(Node parentNode,
ILongRunningTask task)
{
task.beginSubtask(parentNode.getChildNodes().getLength());
}
// ----------------------------------------------------------
/**
* Parses the specified XML node and builds a subtree from the data.
*
* @param node the XML document node to parse from
* @param task the long-running task to run under
* @throws SubmissionTargetException if any errors occurred during parsing
*/
public abstract void parse(Node node, ILongRunningTask task)
throws SubmissionTargetException;
// ----------------------------------------------------------
/**
* Parses the common attributes for the specified XML node and adds them
* to the target.
*
* @param node the XML document node to parse from
* @param task the long-running task to run under
* @throws SubmissionTargetException if any errors occurred during parsing
*/
protected void parseCommonAttributes(Node node, ILongRunningTask task)
throws SubmissionTargetException
{
Node nameNode = node.getAttributes().getNamedItem(
Xml.Attributes.NAME);
Node hiddenNode = node.getAttributes().getNamedItem(
Xml.Attributes.HIDDEN);
String hiddenString = null;
if (nameNode != null)
{
setName(nameNode.getNodeValue());
}
if (hiddenNode != null)
{
hiddenString = hiddenNode.getNodeValue();
}
setHidden(Boolean.parseBoolean(hiddenString));
Set<String> ignored = getIgnoredAttributes();
for (int i = 0; i < node.getAttributes().getLength(); i++)
{
Node attribute = node.getAttributes().item(i);
String attributeName = attribute.getNodeName();
if (!ignored.contains(attributeName))
{
String value = attribute.getNodeValue();
otherAttributes.put(attributeName, value);
}
}
}
// ----------------------------------------------------------
/**
* Parses a transport element and initializes the appropriate fields.
*
* @param parentNode the XML node representing the transport element
* @param task the long-running task to run under
* @throws SubmissionTargetException if there are any parsing errors
*/
protected void parseTransport(Node parentNode, ILongRunningTask task)
throws SubmissionTargetException
{
startSubtaskForChildNodes(parentNode, task);
Node uriNode = parentNode.getAttributes().getNamedItem(
Xml.Attributes.URI);
if (uriNode != null)
{
transport = uriNode.getNodeValue();
}
// Parse the parameter tags.
Node node = parentNode.getFirstChild();
while (node != null)
{
String nodeName = node.getLocalName();
if (Xml.Elements.PARAM.equals(nodeName))
{
parseTransportParameter(node, task);
}
else if (Xml.Elements.FILE_PARAM.equals(nodeName))
{
parseTransportFileParameter(node, task);
}
else
{
task.doWork(1);
}
node = node.getNextSibling();
}
task.finishSubtask();
}
// ----------------------------------------------------------
/**
* Parses a packager element and initializes the appropriate fields.
*
* @param parentNode the XML node representing the packager element
* @param task the long-running task to run under
* @throws SubmissionTargetException if there are any parsing errors
*/
protected void parsePackager(Node parentNode, ILongRunningTask task)
throws SubmissionTargetException
{
startSubtaskForChildNodes(parentNode, task);
Node idNode = parentNode.getAttributes().getNamedItem(
Xml.Attributes.ID);
if (idNode != null)
{
packager = idNode.getNodeValue();
}
// Parse the parameter tags.
Node node = parentNode.getFirstChild();
while (node != null)
{
String nodeName = node.getLocalName();
if (Xml.Elements.PARAM.equals(nodeName))
{
parsePackagerParameter(node, task);
}
else
{
task.doWork(1);
}
node = node.getNextSibling();
}
task.finishSubtask();
}
// ----------------------------------------------------------
/**
* Parses a packager param element and initializes the appropriate fields.
*
* @param node the XML node representing the param element
* @param task the long-running task to run under
* @throws SubmissionTargetException if there are any parsing errors
*/
protected void parsePackagerParameter(Node node, ILongRunningTask task)
throws SubmissionTargetException
{
Node nameNode = node.getAttributes().getNamedItem(
Xml.Attributes.NAME);
Node valueNode = node.getAttributes().getNamedItem(
Xml.Attributes.VALUE);
if (nameNode != null && valueNode != null)
{
packagerParams.put(nameNode.getNodeValue(),
valueNode.getNodeValue());
}
task.doWork(1);
}
// ----------------------------------------------------------
/**
* Parses a transport param element and initializes the appropriate fields.
*
* @param node the XML node representing the param element
* @param task the long-running task to run under
* @throws SubmissionTargetException if there are any parsing errors
*/
protected void parseTransportParameter(Node node, ILongRunningTask task)
throws SubmissionTargetException
{
Node nameNode = node.getAttributes().getNamedItem(
Xml.Attributes.NAME);
Node valueNode = node.getAttributes().getNamedItem(
Xml.Attributes.VALUE);
if (nameNode != null && valueNode != null)
{
transportParams.put(nameNode.getNodeValue(),
valueNode.getNodeValue());
}
task.doWork(1);
}
// ----------------------------------------------------------
/**
* Parses a transport file-param element and initializes the appropriate
* fields.
*
* @param node the XML node representing the file-param element
* @param task the long-running task to run under
* @throws SubmissionTargetException if there are any parsing errors
*/
protected void parseTransportFileParameter(Node node, ILongRunningTask task)
throws SubmissionTargetException
{
Node nameNode = node.getAttributes().getNamedItem(
Xml.Attributes.NAME);
Node valueNode = node.getAttributes().getNamedItem(
Xml.Attributes.VALUE);
if (nameNode != null && valueNode != null)
{
transportParams.put("$file." + nameNode.getNodeValue(),
valueNode.getNodeValue());
}
}
// ----------------------------------------------------------
/**
* Parses the file pattern from an include, exclude, or required element
* and initializes the appropriate fields.
*
* @param node the XML node representing the file pattern element
* @param task the long-running task to run under
* @return the file pattern
* @throws SubmissionTargetException if there are any parsing errors
*/
protected String parseFilePattern(Node node, ILongRunningTask task)
throws SubmissionTargetException
{
Node patternNode = node.getAttributes().getNamedItem(
Xml.Attributes.PATTERN);
task.doWork(1);
if (patternNode != null)
{
return patternNode.getNodeValue();
}
else
{
return null;
}
}
// ----------------------------------------------------------
/**
* Parses the filter ambiguity resolution policy and initializes the
* appropriate fields.
*
* @param node the XML node representing the filter-ambiguity element
* @param task the long-running task to run under
* @throws SubmissionTargetException if there are any parsing errors
*/
protected void parseFilterAmbiguity(Node node, ILongRunningTask task)
throws SubmissionTargetException
{
Node choiceNode = node.getAttributes().getNamedItem(
Xml.Attributes.CHOICE);
if (choiceNode != null)
{
String value = choiceNode.getNodeValue();
if ("include".equals(value))
{
setAmbiguityResolution(AmbiguityResolutionPolicy.INCLUDE);
}
else
{
setAmbiguityResolution(AmbiguityResolutionPolicy.EXCLUDE);
}
}
task.doWork(1);
}
// ----------------------------------------------------------
/**
* Overridden by subclasses to write this target in XML format.
*
* @param writer the PrintWriter to write the output to
* @param indentLevel the number of levels to indent the output
*/
public abstract void writeToXML(PrintWriter writer, int indentLevel);
// ----------------------------------------------------------
/**
* Writes three spaces for each indentation level to the specified writer.
*
* @param indentLevel an integer specifying the indentation level of the
* element
* @param writer the writer that will store the XML code
*/
protected void padToIndent(int indentLevel, PrintWriter writer)
{
for (int i = 0; i < indentLevel; i++)
{
writer.print(" ");
}
}
// ----------------------------------------------------------
/**
* Writes the given string to the writer, first converting any special XML
* characters (angle brackets, quotation mark, ampersand) to XML entities.
*
* @param string the string to convert and write
* @param writer the writer that will store the XML code
*/
protected void writeXMLString(String string, PrintWriter writer)
{
for (int i = 0; i < string.length(); i++)
{
char ch = string.charAt(i);
switch (ch)
{
case '<': writer.print("<"); break;
case '>': writer.print(">"); break;
case '&': writer.print("&"); break;
case '"': writer.print("""); break;
default: writer.print(ch); break;
}
}
}
// ----------------------------------------------------------
/**
* Writes the file patterns, transport, and packager elements for the
* current node to the specified writer.
*
* @param indentLevel an integer specifying the indentation level of the
* element
* @param writer the writer that will store the XML code
*/
protected void writeSharedProperties(int indentLevel, PrintWriter writer)
{
// Write file patterns.
writeFilePatterns(indentLevel, writer);
// Write packager.
writePackager(indentLevel, writer);
// Write transport.
writeTransport(indentLevel, writer);
}
// ----------------------------------------------------------
/**
* Writes the file patterns for the current node to the specified writer.
*
* @param indentLevel an integer specifying the indentation level of the
* element
* @param writer the writer that will store the XML code
*/
protected void writeFilePatterns(int indentLevel, PrintWriter writer)
{
if (ambiguityResolution == AmbiguityResolutionPolicy.INCLUDE)
{
padToIndent(indentLevel, writer);
writer.print("<filter-ambiguity choice=\"include\"/>");
}
if (includes.length > 0)
{
for (String pattern : includes)
{
writePatternTag(indentLevel, "include", pattern, writer);
}
}
if (excludes.length > 0)
{
for (String pattern : excludes)
{
writePatternTag(indentLevel, "exclude", pattern, writer);
}
}
if (required.length > 0)
{
for (String pattern : required)
{
writePatternTag(indentLevel, "required", pattern, writer);
}
}
}
// ----------------------------------------------------------
/**
* Writes a single file pattern element to the specified writer.
*
* @param indentLevel an integer specifying the indentation level of the
* element
* @param type the element type name
* @param value the file pattern
* @param writer the writer that will store the XML code
*/
protected void writePatternTag(int indentLevel, String type, String value,
PrintWriter writer)
{
padToIndent(indentLevel, writer);
writer.print("<");
writer.print(type);
writer.print(" pattern=\"");
writeXMLString(value, writer);
writer.println("\"/>");
}
// ----------------------------------------------------------
/**
* Writes the transport element for the current node to the specified
* writer.
*
* @param indentLevel an integer specifying the indentation level of the
* element
* @param writer the writer that will store the XML code
*/
protected void writeTransport(int indentLevel, PrintWriter writer)
{
if (transport != null)
{
padToIndent(indentLevel, writer);
writer.print("<transport uri=\"");
writeXMLString(transport, writer);
writer.print("\"");
if (transportParams.isEmpty())
{
writer.println("/>");
}
else
{
writer.println(">");
for (String name : transportParams.keySet())
{
String value = transportParams.get(name);
padToIndent(indentLevel + 1, writer);
if (name.startsWith("$file."))
{
writer.print("<file-param name=\"");
writeXMLString(name.substring(6), writer);
writer.print("\" value=\"");
writeXMLString(value, writer);
writer.println("\"/>");
}
else
{
writer.print("<param name=\"");
writeXMLString(name, writer);
writer.print("\" value=\"");
writeXMLString(value, writer);
writer.println("\"/>");
}
}
padToIndent(indentLevel, writer);
writer.println("</transport>");
}
}
}
// ----------------------------------------------------------
/**
* Writes the packager element for the current node to the specified
* writer.
*
* @param indentLevel an integer specifying the indentation level of the
* element
* @param writer the writer that will store the XML code
*/
protected void writePackager(int indentLevel, PrintWriter writer)
{
if (packager.equals(DEFAULT_PACKAGER)
&& (packagerParams == null || packagerParams.size() == 0))
{
return;
}
if (packager != null)
{
padToIndent(indentLevel, writer);
writer.print("<packager id=\"");
writeXMLString(packager, writer);
writer.print("\"");
if (packagerParams.isEmpty())
{
writer.println("/>");
}
else
{
writer.println(">");
for (String name : packagerParams.keySet())
{
String value = packagerParams.get(name);
padToIndent(indentLevel + 1, writer);
writer.print("<param name=\"");
writeXMLString(name, writer);
writer.print("\" value=\"");
writeXMLString(value, writer);
writer.println("\"/>");
}
padToIndent(indentLevel, writer);
writer.println("</packager>");
}
}
}
// ----------------------------------------------------------
/**
* Writes the children of the current node to the specified writer.
*
* @param indentLevel an integer specifying the indentation level of the
* element
* @param writer the writer that will store the XML code
*/
protected void writeChildren(int indentLevel, PrintWriter writer)
{
for (SubmissionTarget child : children)
{
child.writeToXML(writer, indentLevel);
}
}
//~ Static/instance variables .............................................
/* The default packager to use when none is specified. */
private static String DEFAULT_PACKAGER =
"org.webcat.submitter.packagers.zip";
/* The parent object to this object in the tree. */
private SubmissionTarget parent;
/* The name of the target. */
private String name;
/* Indicates whether the target should be hidden. */
private boolean hidden;
/* The list of file patterns that represent files that are to be included
in a submission. */
private String[] includes;
/* The list of file patterns that represent files that are to be excluded
from a submission. */
private String[] excludes;
/* The list of file patterns that represent files that must be included in
a submission. */
private String[] required;
/* Indicates whether exclusion should be preferred over inclusion in the
event that two filters at the same level of the tree conflict for a
particular file. */
private AmbiguityResolutionPolicy ambiguityResolution;
/* The string that contains the submission protocol and destination
location. */
private String transport;
/* A table of name-value pairs that represent additional parameters that
are included with the submission. */
private LinkedHashMap<String, String> transportParams;
/* The globally unique identifier of the packager used to submit the
project. The packager should already have been registered with the
submission core plug-in. */
private String packager;
/* A table of name-value pairs that represent additional parameters that
are passed to the packager. */
private LinkedHashMap<String, String> packagerParams;
/* Other attributes associated with a submission target (such as file
size limits). */
private LinkedHashMap<String, String> otherAttributes;
/* The list of child nodes to this node in the submission target tree. */
private SubmissionTarget[] children;
}