0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: *
0017: */
0018:
0019: package org.apache.tools.ant.helper;
0020:
0021: import java.io.File;
0022: import java.io.FileInputStream;
0023: import java.io.FileNotFoundException;
0024: import java.io.IOException;
0025: import java.io.UnsupportedEncodingException;
0026: import java.util.Locale;
0027: import org.apache.tools.ant.BuildException;
0028: import org.apache.tools.ant.IntrospectionHelper;
0029: import org.apache.tools.ant.Location;
0030: import org.apache.tools.ant.Project;
0031: import org.apache.tools.ant.ProjectHelper;
0032: import org.apache.tools.ant.RuntimeConfigurable;
0033: import org.apache.tools.ant.Target;
0034: import org.apache.tools.ant.Task;
0035: import org.apache.tools.ant.TypeAdapter;
0036: import org.apache.tools.ant.TaskContainer;
0037: import org.apache.tools.ant.UnknownElement;
0038: import org.apache.tools.ant.util.FileUtils;
0039: import org.apache.tools.ant.util.JAXPUtils;
0040: import org.xml.sax.AttributeList;
0041: import org.xml.sax.DocumentHandler;
0042: import org.xml.sax.HandlerBase;
0043: import org.xml.sax.InputSource;
0044: import org.xml.sax.Locator;
0045: import org.xml.sax.SAXException;
0046: import org.xml.sax.SAXParseException;
0047: import org.xml.sax.helpers.XMLReaderAdapter;
0048:
0049: /**
0050: * Original helper.
0051: *
0052: */
0053: public class ProjectHelperImpl extends ProjectHelper {
0054:
0055: /**
0056: * helper for path -> URI and URI -> path conversions.
0057: */
0058: private static final FileUtils FILE_UTILS = FileUtils
0059: .getFileUtils();
0060:
0061: /**
0062: * SAX 1 style parser used to parse the given file. This may
0063: * in fact be a SAX 2 XMLReader wrapped in an XMLReaderAdapter.
0064: */
0065: private org.xml.sax.Parser parser;
0066:
0067: /** The project to configure. */
0068: private Project project;
0069: /** The configuration file to parse. */
0070: private File buildFile;
0071: /**
0072: * Parent directory of the build file. Used for resolving entities
0073: * and setting the project's base directory.
0074: */
0075: private File buildFileParent;
0076: /**
0077: * Locator for the configuration file parser.
0078: * Used for giving locations of errors etc.
0079: */
0080: private Locator locator;
0081: /**
0082: * Target that all other targets will depend upon implicitly.
0083: *
0084: * <p>This holds all tasks and data type definitions that have
0085: * been placed outside of targets.</p>
0086: */
0087: private Target implicitTarget = new Target();
0088:
0089: /**
0090: * default constructor
0091: */
0092: public ProjectHelperImpl() {
0093: implicitTarget.setName("");
0094: }
0095:
0096: /**
0097: * Parses the project file, configuring the project as it goes.
0098: *
0099: * @param project project instance to be configured.
0100: * @param source the source from which the project is read.
0101: * @exception BuildException if the configuration is invalid or cannot
0102: * be read.
0103: */
0104: public void parse(Project project, Object source)
0105: throws BuildException {
0106: if (!(source instanceof File)) {
0107: throw new BuildException("Only File source supported by "
0108: + "default plugin");
0109: }
0110: File bFile = (File) source;
0111: FileInputStream inputStream = null;
0112: InputSource inputSource = null;
0113:
0114: this .project = project;
0115: this .buildFile = new File(bFile.getAbsolutePath());
0116: buildFileParent = new File(this .buildFile.getParent());
0117:
0118: try {
0119: try {
0120: parser = JAXPUtils.getParser();
0121: } catch (BuildException e) {
0122: parser = new XMLReaderAdapter(JAXPUtils.getXMLReader());
0123: }
0124:
0125: String uri = FILE_UTILS.toURI(bFile.getAbsolutePath());
0126: inputStream = new FileInputStream(bFile);
0127: inputSource = new InputSource(inputStream);
0128: inputSource.setSystemId(uri);
0129: project.log("parsing buildfile " + bFile + " with URI = "
0130: + uri, Project.MSG_VERBOSE);
0131: HandlerBase hb = new RootHandler(this );
0132: parser.setDocumentHandler(hb);
0133: parser.setEntityResolver(hb);
0134: parser.setErrorHandler(hb);
0135: parser.setDTDHandler(hb);
0136: parser.parse(inputSource);
0137: } catch (SAXParseException exc) {
0138: Location location = new Location(exc.getSystemId(), exc
0139: .getLineNumber(), exc.getColumnNumber());
0140:
0141: Throwable t = exc.getException();
0142: if (t instanceof BuildException) {
0143: BuildException be = (BuildException) t;
0144: if (be.getLocation() == Location.UNKNOWN_LOCATION) {
0145: be.setLocation(location);
0146: }
0147: throw be;
0148: }
0149:
0150: throw new BuildException(exc.getMessage(), t, location);
0151: } catch (SAXException exc) {
0152: Throwable t = exc.getException();
0153: if (t instanceof BuildException) {
0154: throw (BuildException) t;
0155: }
0156: throw new BuildException(exc.getMessage(), t);
0157: } catch (FileNotFoundException exc) {
0158: throw new BuildException(exc);
0159: } catch (UnsupportedEncodingException exc) {
0160: throw new BuildException(
0161: "Encoding of project file is invalid.", exc);
0162: } catch (IOException exc) {
0163: throw new BuildException("Error reading project file: "
0164: + exc.getMessage(), exc);
0165: } finally {
0166: FileUtils.close(inputStream);
0167: }
0168: }
0169:
0170: /**
0171: * The common superclass for all SAX event handlers used to parse
0172: * the configuration file. Each method just throws an exception,
0173: * so subclasses should override what they can handle.
0174: *
0175: * Each type of XML element (task, target, etc.) in Ant has
0176: * a specific subclass.
0177: *
0178: * In the constructor, this class takes over the handling of SAX
0179: * events from the parent handler and returns
0180: * control back to the parent in the endElement method.
0181: */
0182: static class AbstractHandler extends HandlerBase {
0183: // CheckStyle:VisibilityModifier OFF - bc
0184:
0185: /**
0186: * Previous handler for the document.
0187: * When the next element is finished, control returns
0188: * to this handler.
0189: */
0190: protected DocumentHandler parentHandler;
0191:
0192: /** Helper impl. With non-static internal classes, the compiler will generate
0193: this automatically - but this will fail with some compilers ( reporting
0194: "Expecting to find object/array on stack" ). If we pass it
0195: explicitly it'll work with more compilers.
0196: */
0197: ProjectHelperImpl helperImpl;
0198:
0199: // CheckStyle:VisibilityModifier ON
0200:
0201: /**
0202: * Creates a handler and sets the parser to use it
0203: * for the current element.
0204: *
0205: * @param helperImpl the ProjectHelperImpl instance associated
0206: * with this handler.
0207: *
0208: * @param parentHandler The handler which should be restored to the
0209: * parser at the end of the element.
0210: * Must not be <code>null</code>.
0211: */
0212: public AbstractHandler(ProjectHelperImpl helperImpl,
0213: DocumentHandler parentHandler) {
0214: this .parentHandler = parentHandler;
0215: this .helperImpl = helperImpl;
0216:
0217: // Start handling SAX events
0218: helperImpl.parser.setDocumentHandler(this );
0219: }
0220:
0221: /**
0222: * Handles the start of an element. This base implementation just
0223: * throws an exception.
0224: *
0225: * @param tag The name of the element being started.
0226: * Will not be <code>null</code>.
0227: * @param attrs Attributes of the element being started.
0228: * Will not be <code>null</code>.
0229: *
0230: * @exception SAXParseException if this method is not overridden, or in
0231: * case of error in an overridden version
0232: */
0233: public void startElement(String tag, AttributeList attrs)
0234: throws SAXParseException {
0235: throw new SAXParseException("Unexpected element \"" + tag
0236: + "\"", helperImpl.locator);
0237: }
0238:
0239: /**
0240: * Handles text within an element. This base implementation just
0241: * throws an exception.
0242: *
0243: * @param buf A character array of the text within the element.
0244: * Will not be <code>null</code>.
0245: * @param start The start element in the array.
0246: * @param count The number of characters to read from the array.
0247: *
0248: * @exception SAXParseException if this method is not overridden, or in
0249: * case of error in an overridden version
0250: */
0251: public void characters(char[] buf, int start, int count)
0252: throws SAXParseException {
0253: String s = new String(buf, start, count).trim();
0254:
0255: if (s.length() > 0) {
0256: throw new SAXParseException("Unexpected text \"" + s
0257: + "\"", helperImpl.locator);
0258: }
0259: }
0260:
0261: /**
0262: * Handles the end of an element. Any required clean-up is performed
0263: * by the finished() method and then the original handler is restored to
0264: * the parser.
0265: *
0266: * @param name The name of the element which is ending.
0267: * Will not be <code>null</code>.
0268: *
0269: * @exception SAXException in case of error (not thrown in
0270: * this implementation)
0271: */
0272: public void endElement(String name) throws SAXException {
0273: // Let parent resume handling SAX events
0274: helperImpl.parser.setDocumentHandler(parentHandler);
0275: }
0276: }
0277:
0278: /**
0279: * Handler for the root element. Its only child must be the "project" element.
0280: */
0281: static class RootHandler extends HandlerBase {
0282: // CheckStyle:VisibilityModifier OFF - bc
0283: ProjectHelperImpl helperImpl;
0284:
0285: // CheckStyle:VisibilityModifier ON
0286:
0287: public RootHandler(ProjectHelperImpl helperImpl) {
0288: this .helperImpl = helperImpl;
0289: }
0290:
0291: /**
0292: * Resolves file: URIs relative to the build file.
0293: *
0294: * @param publicId The public identifier, or <code>null</code>
0295: * if none is available. Ignored in this
0296: * implementation.
0297: * @param systemId The system identifier provided in the XML
0298: * document. Will not be <code>null</code>.
0299: */
0300: public InputSource resolveEntity(String publicId,
0301: String systemId) {
0302:
0303: helperImpl.project.log("resolving systemId: " + systemId,
0304: Project.MSG_VERBOSE);
0305:
0306: if (systemId.startsWith("file:")) {
0307: String path = FILE_UTILS.fromURI(systemId);
0308:
0309: File file = new File(path);
0310: if (!file.isAbsolute()) {
0311: file = FILE_UTILS.resolveFile(
0312: helperImpl.buildFileParent, path);
0313: helperImpl.project.log("Warning: '" + systemId
0314: + "' in " + helperImpl.buildFile
0315: + " should be expressed simply as '"
0316: + path.replace('\\', '/')
0317: + "' for compliance with other XML tools",
0318: Project.MSG_WARN);
0319: }
0320: try {
0321: InputSource inputSource = new InputSource(
0322: new FileInputStream(file));
0323: inputSource.setSystemId(FILE_UTILS.toURI(file
0324: .getAbsolutePath()));
0325: return inputSource;
0326: } catch (FileNotFoundException fne) {
0327: helperImpl.project.log(file.getAbsolutePath()
0328: + " could not be found", Project.MSG_WARN);
0329: }
0330: }
0331: // use default if not file or file not found
0332: return null;
0333: }
0334:
0335: /**
0336: * Handles the start of a project element. A project handler is created
0337: * and initialised with the element name and attributes.
0338: *
0339: * @param tag The name of the element being started.
0340: * Will not be <code>null</code>.
0341: * @param attrs Attributes of the element being started.
0342: * Will not be <code>null</code>.
0343: *
0344: * @exception SAXParseException if the tag given is not
0345: * <code>"project"</code>
0346: */
0347: public void startElement(String tag, AttributeList attrs)
0348: throws SAXParseException {
0349: if (tag.equals("project")) {
0350: new ProjectHandler(helperImpl, this ).init(tag, attrs);
0351: } else {
0352: throw new SAXParseException(
0353: "Config file is not of expected " + "XML type",
0354: helperImpl.locator);
0355: }
0356: }
0357:
0358: /**
0359: * Sets the locator in the project helper for future reference.
0360: *
0361: * @param locator The locator used by the parser.
0362: * Will not be <code>null</code>.
0363: */
0364: public void setDocumentLocator(Locator locator) {
0365: helperImpl.locator = locator;
0366: }
0367: }
0368:
0369: /**
0370: * Handler for the top level "project" element.
0371: */
0372: static class ProjectHandler extends AbstractHandler {
0373:
0374: /**
0375: * Constructor which just delegates to the superconstructor.
0376: *
0377: * @param parentHandler The handler which should be restored to the
0378: * parser at the end of the element.
0379: * Must not be <code>null</code>.
0380: */
0381: public ProjectHandler(ProjectHelperImpl helperImpl,
0382: DocumentHandler parentHandler) {
0383: super (helperImpl, parentHandler);
0384: }
0385:
0386: /**
0387: * Initialisation routine called after handler creation
0388: * with the element name and attributes. The attributes which
0389: * this handler can deal with are: <code>"default"</code>,
0390: * <code>"name"</code>, <code>"id"</code> and <code>"basedir"</code>.
0391: *
0392: * @param tag Name of the element which caused this handler
0393: * to be created. Should not be <code>null</code>.
0394: * Ignored in this implementation.
0395: * @param attrs Attributes of the element which caused this
0396: * handler to be created. Must not be <code>null</code>.
0397: *
0398: * @exception SAXParseException if an unexpected attribute is
0399: * encountered or if the <code>"default"</code> attribute
0400: * is missing.
0401: */
0402: public void init(String tag, AttributeList attrs)
0403: throws SAXParseException {
0404: String def = null;
0405: String name = null;
0406: String id = null;
0407: String baseDir = null;
0408:
0409: for (int i = 0; i < attrs.getLength(); i++) {
0410: String key = attrs.getName(i);
0411: String value = attrs.getValue(i);
0412:
0413: if (key.equals("default")) {
0414: def = value;
0415: } else if (key.equals("name")) {
0416: name = value;
0417: } else if (key.equals("id")) {
0418: id = value;
0419: } else if (key.equals("basedir")) {
0420: baseDir = value;
0421: } else {
0422: throw new SAXParseException(
0423: "Unexpected attribute \""
0424: + attrs.getName(i) + "\"",
0425: helperImpl.locator);
0426: }
0427: }
0428:
0429: if (def != null && !def.equals("")) {
0430: helperImpl.project.setDefaultTarget(def);
0431: } else {
0432: throw new BuildException(
0433: "The default attribute is required");
0434: }
0435:
0436: if (name != null) {
0437: helperImpl.project.setName(name);
0438: helperImpl.project.addReference(name,
0439: helperImpl.project);
0440: }
0441:
0442: if (id != null) {
0443: helperImpl.project.addReference(id, helperImpl.project);
0444: }
0445:
0446: if (helperImpl.project.getProperty("basedir") != null) {
0447: helperImpl.project.setBasedir(helperImpl.project
0448: .getProperty("basedir"));
0449: } else {
0450: if (baseDir == null) {
0451: helperImpl.project
0452: .setBasedir(helperImpl.buildFileParent
0453: .getAbsolutePath());
0454: } else {
0455: // check whether the user has specified an absolute path
0456: if ((new File(baseDir)).isAbsolute()) {
0457: helperImpl.project.setBasedir(baseDir);
0458: } else {
0459: File resolvedBaseDir = FILE_UTILS.resolveFile(
0460: helperImpl.buildFileParent, baseDir);
0461: helperImpl.project.setBaseDir(resolvedBaseDir);
0462: }
0463: }
0464: }
0465:
0466: helperImpl.project.addTarget("", helperImpl.implicitTarget);
0467: }
0468:
0469: /**
0470: * Handles the start of a top-level element within the project. An
0471: * appropriate handler is created and initialised with the details
0472: * of the element.
0473: *
0474: * @param name The name of the element being started.
0475: * Will not be <code>null</code>.
0476: * @param attrs Attributes of the element being started.
0477: * Will not be <code>null</code>.
0478: *
0479: * @exception SAXParseException if the tag given is not
0480: * <code>"taskdef"</code>, <code>"typedef"</code>,
0481: * <code>"property"</code>, <code>"target"</code>
0482: * or a data type definition
0483: */
0484: public void startElement(String name, AttributeList attrs)
0485: throws SAXParseException {
0486: if (name.equals("target")) {
0487: handleTarget(name, attrs);
0488: } else {
0489: handleElement(helperImpl, this ,
0490: helperImpl.implicitTarget, name, attrs);
0491: }
0492: }
0493:
0494: /**
0495: * Handles a target definition element by creating a target handler
0496: * and initialising is with the details of the element.
0497: *
0498: * @param tag The name of the element to be handled.
0499: * Will not be <code>null</code>.
0500: * @param attrs Attributes of the element to be handled.
0501: * Will not be <code>null</code>.
0502: *
0503: * @exception SAXParseException if an error occurs initialising
0504: * the handler
0505: */
0506: private void handleTarget(String tag, AttributeList attrs)
0507: throws SAXParseException {
0508: new TargetHandler(helperImpl, this ).init(tag, attrs);
0509: }
0510:
0511: }
0512:
0513: /**
0514: * Handler for "target" elements.
0515: */
0516: static class TargetHandler extends AbstractHandler {
0517: private Target target;
0518:
0519: /**
0520: * Constructor which just delegates to the superconstructor.
0521: *
0522: * @param parentHandler The handler which should be restored to the
0523: * parser at the end of the element.
0524: * Must not be <code>null</code>.
0525: */
0526: public TargetHandler(ProjectHelperImpl helperImpl,
0527: DocumentHandler parentHandler) {
0528: super (helperImpl, parentHandler);
0529: }
0530:
0531: /**
0532: * Initialisation routine called after handler creation
0533: * with the element name and attributes. The attributes which
0534: * this handler can deal with are: <code>"name"</code>,
0535: * <code>"depends"</code>, <code>"if"</code>,
0536: * <code>"unless"</code>, <code>"id"</code> and
0537: * <code>"description"</code>.
0538: *
0539: * @param tag Name of the element which caused this handler
0540: * to be created. Should not be <code>null</code>.
0541: * Ignored in this implementation.
0542: * @param attrs Attributes of the element which caused this
0543: * handler to be created. Must not be <code>null</code>.
0544: *
0545: * @exception SAXParseException if an unexpected attribute is encountered
0546: * or if the <code>"name"</code> attribute is missing.
0547: */
0548: public void init(String tag, AttributeList attrs)
0549: throws SAXParseException {
0550: String name = null;
0551: String depends = "";
0552: String ifCond = null;
0553: String unlessCond = null;
0554: String id = null;
0555: String description = null;
0556:
0557: for (int i = 0; i < attrs.getLength(); i++) {
0558: String key = attrs.getName(i);
0559: String value = attrs.getValue(i);
0560:
0561: if (key.equals("name")) {
0562: name = value;
0563: if (name.equals("")) {
0564: throw new BuildException(
0565: "name attribute must not" + " be empty",
0566: new Location(helperImpl.locator));
0567: }
0568: } else if (key.equals("depends")) {
0569: depends = value;
0570: } else if (key.equals("if")) {
0571: ifCond = value;
0572: } else if (key.equals("unless")) {
0573: unlessCond = value;
0574: } else if (key.equals("id")) {
0575: id = value;
0576: } else if (key.equals("description")) {
0577: description = value;
0578: } else {
0579: throw new SAXParseException(
0580: "Unexpected attribute \"" + key + "\"",
0581: helperImpl.locator);
0582: }
0583: }
0584:
0585: if (name == null) {
0586: throw new SAXParseException(
0587: "target element appears without a name attribute",
0588: helperImpl.locator);
0589: }
0590:
0591: target = new Target();
0592:
0593: // implicit target must be first on dependency list
0594: target.addDependency("");
0595:
0596: target.setName(name);
0597: target.setIf(ifCond);
0598: target.setUnless(unlessCond);
0599: target.setDescription(description);
0600: helperImpl.project.addTarget(name, target);
0601:
0602: if (id != null && !id.equals("")) {
0603: helperImpl.project.addReference(id, target);
0604: }
0605:
0606: // take care of dependencies
0607:
0608: if (depends.length() > 0) {
0609: target.setDepends(depends);
0610: }
0611: }
0612:
0613: /**
0614: * Handles the start of an element within a target.
0615: *
0616: * @param name The name of the element being started.
0617: * Will not be <code>null</code>.
0618: * @param attrs Attributes of the element being started.
0619: * Will not be <code>null</code>.
0620: *
0621: * @exception SAXParseException if an error occurs when initialising
0622: * the appropriate child handler
0623: */
0624: public void startElement(String name, AttributeList attrs)
0625: throws SAXParseException {
0626: handleElement(helperImpl, this , target, name, attrs);
0627: }
0628: }
0629:
0630: /**
0631: * Start a new DataTypeHandler if element is known to be a
0632: * data-type and a TaskHandler otherwise.
0633: *
0634: * <p>Factored out of TargetHandler.</p>
0635: *
0636: * @since Ant 1.6
0637: */
0638: private static void handleElement(ProjectHelperImpl helperImpl,
0639: DocumentHandler parent, Target target, String elementName,
0640: AttributeList attrs) throws SAXParseException {
0641: if (elementName.equals("description")) {
0642: new DescriptionHandler(helperImpl, parent);
0643: } else if (helperImpl.project.getDataTypeDefinitions().get(
0644: elementName) != null) {
0645: new DataTypeHandler(helperImpl, parent, target).init(
0646: elementName, attrs);
0647: } else {
0648: new TaskHandler(helperImpl, parent, target, null, target)
0649: .init(elementName, attrs);
0650: }
0651: }
0652:
0653: /**
0654: * Handler for "description" elements.
0655: */
0656: static class DescriptionHandler extends AbstractHandler {
0657:
0658: /**
0659: * Constructor which just delegates to the superconstructor.
0660: *
0661: * @param parentHandler The handler which should be restored to the
0662: * parser at the end of the element.
0663: * Must not be <code>null</code>.
0664: */
0665: public DescriptionHandler(ProjectHelperImpl helperImpl,
0666: DocumentHandler parentHandler) {
0667: super (helperImpl, parentHandler);
0668: }
0669:
0670: /**
0671: * Adds the text as description to the project.
0672: *
0673: * @param buf A character array of the text within the element.
0674: * Will not be <code>null</code>.
0675: * @param start The start element in the array.
0676: * @param count The number of characters to read from the array.
0677: */
0678: public void characters(char[] buf, int start, int count) {
0679: String text = new String(buf, start, count);
0680: String currentDescription = helperImpl.project
0681: .getDescription();
0682: if (currentDescription == null) {
0683: helperImpl.project.setDescription(text);
0684: } else {
0685: helperImpl.project.setDescription(currentDescription
0686: + text);
0687: }
0688: }
0689:
0690: }
0691:
0692: /**
0693: * Handler for all task elements.
0694: */
0695: static class TaskHandler extends AbstractHandler {
0696: /** Containing target, if any. */
0697: private Target target;
0698: /**
0699: * Container for the task, if any. If target is
0700: * non-<code>null</code>, this must be too.
0701: */
0702: private TaskContainer container;
0703: /**
0704: * Task created by this handler.
0705: */
0706: private Task task;
0707: /**
0708: * Wrapper for the parent element, if any. The wrapper for this
0709: * element will be added to this wrapper as a child.
0710: */
0711: private RuntimeConfigurable parentWrapper;
0712: /**
0713: * Wrapper for this element which takes care of actually configuring
0714: * the element, if this element is contained within a target.
0715: * Otherwise the configuration is performed with the configure method.
0716: * @see ProjectHelper#configure(Object,AttributeList,Project)
0717: */
0718: private RuntimeConfigurable wrapper = null;
0719:
0720: /**
0721: * Constructor.
0722: *
0723: * @param parentHandler The handler which should be restored to the
0724: * parser at the end of the element.
0725: * Must not be <code>null</code>.
0726: *
0727: * @param container Container for the element.
0728: * Must not be <code>null</code>.
0729: *
0730: * @param parentWrapper Wrapper for the parent element, if any.
0731: * May be <code>null</code>.
0732: *
0733: * @param target Target this element is part of.
0734: * Must not be <code>null</code>.
0735: */
0736: public TaskHandler(ProjectHelperImpl helperImpl,
0737: DocumentHandler parentHandler, TaskContainer container,
0738: RuntimeConfigurable parentWrapper, Target target) {
0739: super (helperImpl, parentHandler);
0740: this .container = container;
0741: this .parentWrapper = parentWrapper;
0742: this .target = target;
0743: }
0744:
0745: /**
0746: * Initialisation routine called after handler creation
0747: * with the element name and attributes. This configures
0748: * the element with its attributes and sets it up with
0749: * its parent container (if any). Nested elements are then
0750: * added later as the parser encounters them.
0751: *
0752: * @param tag Name of the element which caused this handler
0753: * to be created. Must not be <code>null</code>.
0754: *
0755: * @param attrs Attributes of the element which caused this
0756: * handler to be created. Must not be <code>null</code>.
0757: *
0758: * @exception SAXParseException in case of error (not thrown in
0759: * this implementation)
0760: */
0761: public void init(String tag, AttributeList attrs)
0762: throws SAXParseException {
0763: try {
0764: task = helperImpl.project.createTask(tag);
0765: } catch (BuildException e) {
0766: // swallow here, will be thrown again in
0767: // UnknownElement.maybeConfigure if the problem persists.
0768: }
0769:
0770: if (task == null) {
0771: task = new UnknownElement(tag);
0772: task.setProject(helperImpl.project);
0773: //XXX task.setTaskType(tag);
0774: task.setTaskName(tag);
0775: }
0776:
0777: task.setLocation(new Location(helperImpl.locator));
0778: helperImpl.configureId(task, attrs);
0779:
0780: task.setOwningTarget(target);
0781: container.addTask(task);
0782: task.init();
0783: wrapper = task.getRuntimeConfigurableWrapper();
0784: wrapper.setAttributes(attrs);
0785: if (parentWrapper != null) {
0786: parentWrapper.addChild(wrapper);
0787: }
0788: }
0789:
0790: /**
0791: * Adds text to the task, using the wrapper.
0792: *
0793: * @param buf A character array of the text within the element.
0794: * Will not be <code>null</code>.
0795: * @param start The start element in the array.
0796: * @param count The number of characters to read from the array.
0797: */
0798: public void characters(char[] buf, int start, int count) {
0799: wrapper.addText(buf, start, count);
0800: }
0801:
0802: /**
0803: * Handles the start of an element within a target. Task containers
0804: * will always use another task handler, and all other tasks
0805: * will always use a nested element handler.
0806: *
0807: * @param name The name of the element being started.
0808: * Will not be <code>null</code>.
0809: * @param attrs Attributes of the element being started.
0810: * Will not be <code>null</code>.
0811: *
0812: * @exception SAXParseException if an error occurs when initialising
0813: * the appropriate child handler
0814: */
0815: public void startElement(String name, AttributeList attrs)
0816: throws SAXParseException {
0817: if (task instanceof TaskContainer) {
0818: // task can contain other tasks - no other nested elements possible
0819: new TaskHandler(helperImpl, this , (TaskContainer) task,
0820: wrapper, target).init(name, attrs);
0821: } else {
0822: new NestedElementHandler(helperImpl, this , task,
0823: wrapper, target).init(name, attrs);
0824: }
0825: }
0826: }
0827:
0828: /**
0829: * Handler for all nested properties.
0830: */
0831: static class NestedElementHandler extends AbstractHandler {
0832: /** Parent object (task/data type/etc). */
0833: private Object parent;
0834: /** The nested element itself. */
0835: private Object child;
0836: /**
0837: * Wrapper for the parent element, if any. The wrapper for this
0838: * element will be added to this wrapper as a child.
0839: */
0840: private RuntimeConfigurable parentWrapper;
0841: /**
0842: * Wrapper for this element which takes care of actually configuring
0843: * the element, if a parent wrapper is provided.
0844: * Otherwise the configuration is performed with the configure method.
0845: * @see ProjectHelper#configure(Object,AttributeList,Project)
0846: */
0847: private RuntimeConfigurable childWrapper = null;
0848: /** Target this element is part of, if any. */
0849: private Target target;
0850:
0851: /**
0852: * Constructor.
0853: *
0854: * @param parentHandler The handler which should be restored to the
0855: * parser at the end of the element.
0856: * Must not be <code>null</code>.
0857: *
0858: * @param parent Parent of this element (task/data type/etc).
0859: * Must not be <code>null</code>.
0860: *
0861: * @param parentWrapper Wrapper for the parent element, if any.
0862: * Must not be <code>null</code>.
0863: *
0864: * @param target Target this element is part of.
0865: * Must not be <code>null</code>.
0866: */
0867: public NestedElementHandler(ProjectHelperImpl helperImpl,
0868: DocumentHandler parentHandler, Object parent,
0869: RuntimeConfigurable parentWrapper, Target target) {
0870: super (helperImpl, parentHandler);
0871:
0872: if (parent instanceof TypeAdapter) {
0873: this .parent = ((TypeAdapter) parent).getProxy();
0874: } else {
0875: this .parent = parent;
0876: }
0877: this .parentWrapper = parentWrapper;
0878: this .target = target;
0879: }
0880:
0881: /**
0882: * Initialisation routine called after handler creation
0883: * with the element name and attributes. This configures
0884: * the element with its attributes and sets it up with
0885: * its parent container (if any). Nested elements are then
0886: * added later as the parser encounters them.
0887: *
0888: * @param propType Name of the element which caused this handler
0889: * to be created. Must not be <code>null</code>.
0890: *
0891: * @param attrs Attributes of the element which caused this
0892: * handler to be created. Must not be <code>null</code>.
0893: *
0894: * @exception SAXParseException in case of error, such as a
0895: * BuildException being thrown during configuration.
0896: */
0897: public void init(String propType, AttributeList attrs)
0898: throws SAXParseException {
0899: Class parentClass = parent.getClass();
0900: IntrospectionHelper ih = IntrospectionHelper.getHelper(
0901: helperImpl.project, parentClass);
0902:
0903: try {
0904: String elementName = propType.toLowerCase(Locale.US);
0905: if (parent instanceof UnknownElement) {
0906: UnknownElement uc = new UnknownElement(elementName);
0907: uc.setProject(helperImpl.project);
0908: ((UnknownElement) parent).addChild(uc);
0909: child = uc;
0910: } else {
0911: child = ih.createElement(helperImpl.project,
0912: parent, elementName);
0913: }
0914:
0915: helperImpl.configureId(child, attrs);
0916:
0917: childWrapper = new RuntimeConfigurable(child, propType);
0918: childWrapper.setAttributes(attrs);
0919: parentWrapper.addChild(childWrapper);
0920: } catch (BuildException exc) {
0921: throw new SAXParseException(exc.getMessage(),
0922: helperImpl.locator, exc);
0923: }
0924: }
0925:
0926: /**
0927: * Adds text to the element, using the wrapper.
0928: *
0929: * @param buf A character array of the text within the element.
0930: * Will not be <code>null</code>.
0931: * @param start The start element in the array.
0932: * @param count The number of characters to read from the array.
0933: */
0934: public void characters(char[] buf, int start, int count) {
0935: childWrapper.addText(buf, start, count);
0936: }
0937:
0938: /**
0939: * Handles the start of an element within this one. Task containers
0940: * will always use a task handler, and all other elements
0941: * will always use another nested element handler.
0942: *
0943: * @param name The name of the element being started.
0944: * Will not be <code>null</code>.
0945: * @param attrs Attributes of the element being started.
0946: * Will not be <code>null</code>.
0947: *
0948: * @exception SAXParseException if an error occurs when initialising
0949: * the appropriate child handler
0950: */
0951: public void startElement(String name, AttributeList attrs)
0952: throws SAXParseException {
0953: if (child instanceof TaskContainer) {
0954: // taskcontainer nested element can contain other tasks - no other
0955: // nested elements possible
0956: new TaskHandler(helperImpl, this ,
0957: (TaskContainer) child, childWrapper, target)
0958: .init(name, attrs);
0959: } else {
0960: new NestedElementHandler(helperImpl, this , child,
0961: childWrapper, target).init(name, attrs);
0962: }
0963: }
0964: }
0965:
0966: /**
0967: * Handler for all data types directly subordinate to project or target.
0968: */
0969: static class DataTypeHandler extends AbstractHandler {
0970: /** Parent target, if any. */
0971: private Target target;
0972: /** The element being configured. */
0973: private Object element;
0974: /** Wrapper for this element, if it's part of a target. */
0975: private RuntimeConfigurable wrapper = null;
0976:
0977: /**
0978: * Constructor with a target specified.
0979: *
0980: * @param parentHandler The handler which should be restored to the
0981: * parser at the end of the element.
0982: * Must not be <code>null</code>.
0983: *
0984: * @param target The parent target of this element.
0985: * Must not be <code>null</code>.
0986: */
0987: public DataTypeHandler(ProjectHelperImpl helperImpl,
0988: DocumentHandler parentHandler, Target target) {
0989: super (helperImpl, parentHandler);
0990: this .target = target;
0991: }
0992:
0993: /**
0994: * Initialisation routine called after handler creation
0995: * with the element name and attributes. This configures
0996: * the element with its attributes and sets it up with
0997: * its parent container (if any). Nested elements are then
0998: * added later as the parser encounters them.
0999: *
1000: * @param propType Name of the element which caused this handler
1001: * to be created. Must not be <code>null</code>.
1002: *
1003: * @param attrs Attributes of the element which caused this
1004: * handler to be created. Must not be <code>null</code>.
1005: *
1006: * @exception SAXParseException in case of error, such as a
1007: * BuildException being thrown during configuration.
1008: */
1009: public void init(String propType, AttributeList attrs)
1010: throws SAXParseException {
1011: try {
1012: element = helperImpl.project.createDataType(propType);
1013: if (element == null) {
1014: throw new BuildException("Unknown data type "
1015: + propType);
1016: }
1017:
1018: wrapper = new RuntimeConfigurable(element, propType);
1019: wrapper.setAttributes(attrs);
1020: target.addDataType(wrapper);
1021: } catch (BuildException exc) {
1022: throw new SAXParseException(exc.getMessage(),
1023: helperImpl.locator, exc);
1024: }
1025: }
1026:
1027: /**
1028: * Adds text to the using the wrapper.
1029: *
1030: * @param buf A character array of the text within the element.
1031: * Will not be <code>null</code>.
1032: * @param start The start element in the array.
1033: * @param count The number of characters to read from the array.
1034: *
1035: * @see ProjectHelper#addText(Project,Object,char[],int,int)
1036: */
1037: public void characters(char[] buf, int start, int count) {
1038: wrapper.addText(buf, start, count);
1039: }
1040:
1041: /**
1042: * Handles the start of an element within this one.
1043: * This will always use a nested element handler.
1044: *
1045: * @param name The name of the element being started.
1046: * Will not be <code>null</code>.
1047: * @param attrs Attributes of the element being started.
1048: * Will not be <code>null</code>.
1049: *
1050: * @exception SAXParseException if an error occurs when initialising
1051: * the child handler
1052: */
1053: public void startElement(String name, AttributeList attrs)
1054: throws SAXParseException {
1055: new NestedElementHandler(helperImpl, this , element,
1056: wrapper, target).init(name, attrs);
1057: }
1058: }
1059:
1060: /**
1061: * Scans an attribute list for the <code>id</code> attribute and
1062: * stores a reference to the target object in the project if an
1063: * id is found.
1064: * <p>
1065: * This method was moved out of the configure method to allow
1066: * it to be executed at parse time.
1067: *
1068: * @see #configure(Object,AttributeList,Project)
1069: */
1070: private void configureId(Object target, AttributeList attr) {
1071: String id = attr.getValue("id");
1072: if (id != null) {
1073: project.addReference(id, target);
1074: }
1075: }
1076: }
|