source: release-kits/lirk3/resources/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/helper/ProjectHelperImpl.java@ 14982

Last change on this file since 14982 was 14982, checked in by oranfry, 16 years ago

initial import of LiRK3

File size: 41.5 KB
Line 
1/*
2 * Copyright 2000-2004 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18package org.apache.tools.ant.helper;
19
20import java.io.File;
21import java.io.FileInputStream;
22import java.io.FileNotFoundException;
23import java.io.IOException;
24import java.io.UnsupportedEncodingException;
25import java.util.Locale;
26import org.apache.tools.ant.BuildException;
27import org.apache.tools.ant.IntrospectionHelper;
28import org.apache.tools.ant.Location;
29import org.apache.tools.ant.Project;
30import org.apache.tools.ant.ProjectHelper;
31import org.apache.tools.ant.RuntimeConfigurable;
32import org.apache.tools.ant.Target;
33import org.apache.tools.ant.Task;
34import org.apache.tools.ant.TypeAdapter;
35import org.apache.tools.ant.TaskContainer;
36import org.apache.tools.ant.UnknownElement;
37import org.apache.tools.ant.util.FileUtils;
38import org.apache.tools.ant.util.JAXPUtils;
39import org.xml.sax.AttributeList;
40import org.xml.sax.DocumentHandler;
41import org.xml.sax.HandlerBase;
42import org.xml.sax.InputSource;
43import org.xml.sax.Locator;
44import org.xml.sax.SAXException;
45import org.xml.sax.SAXParseException;
46import org.xml.sax.helpers.XMLReaderAdapter;
47
48/**
49 * Original helper.
50 *
51 */
52public class ProjectHelperImpl extends ProjectHelper {
53
54 /**
55 * SAX 1 style parser used to parse the given file. This may
56 * in fact be a SAX 2 XMLReader wrapped in an XMLReaderAdapter.
57 */
58 private org.xml.sax.Parser parser;
59
60 /** The project to configure. */
61 private Project project;
62 /** The configuration file to parse. */
63 private File buildFile;
64 /**
65 * Parent directory of the build file. Used for resolving entities
66 * and setting the project's base directory.
67 */
68 private File buildFileParent;
69 /**
70 * Locator for the configuration file parser.
71 * Used for giving locations of errors etc.
72 */
73 private Locator locator;
74 /**
75 * Target that all other targets will depend upon implicitly.
76 *
77 * <p>This holds all tasks and data type definitions that have
78 * been placed outside of targets.</p>
79 */
80 private Target implicitTarget = new Target();
81 /**
82 * helper for path -> URI and URI -> path conversions.
83 */
84 private static FileUtils fu = FileUtils.newFileUtils();
85
86 /**
87 * default constructor
88 */
89 public ProjectHelperImpl() {
90 implicitTarget.setName("");
91 }
92
93 /**
94 * Parses the project file, configuring the project as it goes.
95 *
96 * @param project project instance to be configured.
97 * @param source the source from which the project is read.
98 * @exception BuildException if the configuration is invalid or cannot
99 * be read.
100 */
101 public void parse(Project project, Object source) throws BuildException {
102 if (!(source instanceof File)) {
103 throw new BuildException("Only File source supported by "
104 + "default plugin");
105 }
106 File buildFile = (File) source;
107 FileInputStream inputStream = null;
108 InputSource inputSource = null;
109
110 this.project = project;
111 this.buildFile = new File(buildFile.getAbsolutePath());
112 buildFileParent = new File(this.buildFile.getParent());
113
114 try {
115 try {
116 parser = JAXPUtils.getParser();
117 } catch (BuildException e) {
118 parser = new XMLReaderAdapter(JAXPUtils.getXMLReader());
119 }
120
121
122 String uri = fu.toURI(buildFile.getAbsolutePath());
123 inputStream = new FileInputStream(buildFile);
124 inputSource = new InputSource(inputStream);
125 inputSource.setSystemId(uri);
126 project.log("parsing buildfile " + buildFile + " with URI = "
127 + uri, Project.MSG_VERBOSE);
128 HandlerBase hb = new RootHandler(this);
129 parser.setDocumentHandler(hb);
130 parser.setEntityResolver(hb);
131 parser.setErrorHandler(hb);
132 parser.setDTDHandler(hb);
133 parser.parse(inputSource);
134 } catch (SAXParseException exc) {
135 Location location =
136 new Location(exc.getSystemId(), exc.getLineNumber(),
137 exc.getColumnNumber());
138
139 Throwable t = exc.getException();
140 if (t instanceof BuildException) {
141 BuildException be = (BuildException) t;
142 if (be.getLocation() == Location.UNKNOWN_LOCATION) {
143 be.setLocation(location);
144 }
145 throw be;
146 }
147
148 throw new BuildException(exc.getMessage(), t, location);
149 } catch (SAXException exc) {
150 Throwable t = exc.getException();
151 if (t instanceof BuildException) {
152 throw (BuildException) t;
153 }
154 throw new BuildException(exc.getMessage(), t);
155 } catch (FileNotFoundException exc) {
156 throw new BuildException(exc);
157 } catch (UnsupportedEncodingException exc) {
158 throw new BuildException("Encoding of project file is invalid.",
159 exc);
160 } catch (IOException exc) {
161 throw new BuildException("Error reading project file: "
162 + exc.getMessage(), exc);
163 } finally {
164 if (inputStream != null) {
165 try {
166 inputStream.close();
167 } catch (IOException ioe) {
168 // ignore this
169 }
170 }
171 }
172 }
173
174 /**
175 * The common superclass for all SAX event handlers used to parse
176 * the configuration file. Each method just throws an exception,
177 * so subclasses should override what they can handle.
178 *
179 * Each type of XML element (task, target, etc.) in Ant has
180 * a specific subclass.
181 *
182 * In the constructor, this class takes over the handling of SAX
183 * events from the parent handler and returns
184 * control back to the parent in the endElement method.
185 */
186 static class AbstractHandler extends HandlerBase {
187
188 /**
189 * Previous handler for the document.
190 * When the next element is finished, control returns
191 * to this handler.
192 */
193 protected DocumentHandler parentHandler;
194
195 /** Helper impl. With non-static internal classes, the compiler will generate
196 this automatically - but this will fail with some compilers ( reporting
197 "Expecting to find object/array on stack" ). If we pass it
198 explicitly it'll work with more compilers.
199 */
200 ProjectHelperImpl helperImpl;
201
202 /**
203 * Creates a handler and sets the parser to use it
204 * for the current element.
205 *
206 * @param helperImpl the ProjectHelperImpl instance associated
207 * with this handler.
208 *
209 * @param parentHandler The handler which should be restored to the
210 * parser at the end of the element.
211 * Must not be <code>null</code>.
212 */
213 public AbstractHandler(ProjectHelperImpl helperImpl, DocumentHandler parentHandler) {
214 this.parentHandler = parentHandler;
215 this.helperImpl = helperImpl;
216
217 // Start handling SAX events
218 helperImpl.parser.setDocumentHandler(this);
219 }
220
221 /**
222 * Handles the start of an element. This base implementation just
223 * throws an exception.
224 *
225 * @param tag The name of the element being started.
226 * Will not be <code>null</code>.
227 * @param attrs Attributes of the element being started.
228 * Will not be <code>null</code>.
229 *
230 * @exception SAXParseException if this method is not overridden, or in
231 * case of error in an overridden version
232 */
233 public void startElement(String tag, AttributeList attrs) throws SAXParseException {
234 throw new SAXParseException("Unexpected element \"" + tag + "\"", helperImpl.locator);
235 }
236
237 /**
238 * Handles text within an element. This base implementation just
239 * throws an exception.
240 *
241 * @param buf A character array of the text within the element.
242 * Will not be <code>null</code>.
243 * @param start The start element in the array.
244 * @param count The number of characters to read from the array.
245 *
246 * @exception SAXParseException if this method is not overridden, or in
247 * case of error in an overridden version
248 */
249 public void characters(char[] buf, int start, int count) throws SAXParseException {
250 String s = new String(buf, start, count).trim();
251
252 if (s.length() > 0) {
253 throw new SAXParseException("Unexpected text \"" + s + "\"", helperImpl.locator);
254 }
255 }
256
257 /**
258 * Handles the end of an element. Any required clean-up is performed
259 * by the finished() method and then the original handler is restored to
260 * the parser.
261 *
262 * @param name The name of the element which is ending.
263 * Will not be <code>null</code>.
264 *
265 * @exception SAXException in case of error (not thrown in
266 * this implementation)
267 */
268 public void endElement(String name) throws SAXException {
269 // Let parent resume handling SAX events
270 helperImpl.parser.setDocumentHandler(parentHandler);
271 }
272 }
273
274 /**
275 * Handler for the root element. Its only child must be the "project" element.
276 */
277 static class RootHandler extends HandlerBase {
278 ProjectHelperImpl helperImpl;
279
280 public RootHandler(ProjectHelperImpl helperImpl) {
281 this.helperImpl = helperImpl;
282 }
283
284 /**
285 * Resolves file: URIs relative to the build file.
286 *
287 * @param publicId The public identifier, or <code>null</code>
288 * if none is available. Ignored in this
289 * implementation.
290 * @param systemId The system identifier provided in the XML
291 * document. Will not be <code>null</code>.
292 */
293 public InputSource resolveEntity(String publicId,
294 String systemId) {
295
296 helperImpl.project.log("resolving systemId: " + systemId, Project.MSG_VERBOSE);
297
298 if (systemId.startsWith("file:")) {
299 String path = fu.fromURI(systemId);
300
301 File file = new File(path);
302 if (!file.isAbsolute()) {
303 file = fu.resolveFile(helperImpl.buildFileParent, path);
304 }
305 try {
306 InputSource inputSource = new InputSource(new FileInputStream(file));
307 inputSource.setSystemId(fu.toURI(file.getAbsolutePath()));
308 return inputSource;
309 } catch (FileNotFoundException fne) {
310 helperImpl.project.log(file.getAbsolutePath() + " could not be found",
311 Project.MSG_WARN);
312 }
313 }
314 // use default if not file or file not found
315 return null;
316 }
317
318 /**
319 * Handles the start of a project element. A project handler is created
320 * and initialised with the element name and attributes.
321 *
322 * @param tag The name of the element being started.
323 * Will not be <code>null</code>.
324 * @param attrs Attributes of the element being started.
325 * Will not be <code>null</code>.
326 *
327 * @exception SAXParseException if the tag given is not
328 * <code>"project"</code>
329 */
330 public void startElement(String tag, AttributeList attrs) throws SAXParseException {
331 if (tag.equals("project")) {
332 new ProjectHandler(helperImpl, this).init(tag, attrs);
333 } else {
334 throw new SAXParseException("Config file is not of expected "
335 + "XML type", helperImpl.locator);
336 }
337 }
338
339 /**
340 * Sets the locator in the project helper for future reference.
341 *
342 * @param locator The locator used by the parser.
343 * Will not be <code>null</code>.
344 */
345 public void setDocumentLocator(Locator locator) {
346 helperImpl.locator = locator;
347 }
348 }
349
350 /**
351 * Handler for the top level "project" element.
352 */
353 static class ProjectHandler extends AbstractHandler {
354
355 /**
356 * Constructor which just delegates to the superconstructor.
357 *
358 * @param parentHandler The handler which should be restored to the
359 * parser at the end of the element.
360 * Must not be <code>null</code>.
361 */
362 public ProjectHandler(ProjectHelperImpl helperImpl, DocumentHandler parentHandler) {
363 super(helperImpl, parentHandler);
364 }
365
366 /**
367 * Initialisation routine called after handler creation
368 * with the element name and attributes. The attributes which
369 * this handler can deal with are: <code>"default"</code>,
370 * <code>"name"</code>, <code>"id"</code> and <code>"basedir"</code>.
371 *
372 * @param tag Name of the element which caused this handler
373 * to be created. Should not be <code>null</code>.
374 * Ignored in this implementation.
375 * @param attrs Attributes of the element which caused this
376 * handler to be created. Must not be <code>null</code>.
377 *
378 * @exception SAXParseException if an unexpected attribute is
379 * encountered or if the <code>"default"</code> attribute
380 * is missing.
381 */
382 public void init(String tag, AttributeList attrs) throws SAXParseException {
383 String def = null;
384 String name = null;
385 String id = null;
386 String baseDir = null;
387
388 for (int i = 0; i < attrs.getLength(); i++) {
389 String key = attrs.getName(i);
390 String value = attrs.getValue(i);
391
392 if (key.equals("default")) {
393 def = value;
394 } else if (key.equals("name")) {
395 name = value;
396 } else if (key.equals("id")) {
397 id = value;
398 } else if (key.equals("basedir")) {
399 baseDir = value;
400 } else {
401 throw new SAXParseException("Unexpected attribute \"" + attrs.getName(i) + "\"",
402 helperImpl.locator);
403 }
404 }
405
406 if (def != null && !def.equals("")) {
407 helperImpl.project.setDefaultTarget(def);
408 } else {
409 throw new BuildException("The default attribute is required");
410 }
411
412 if (name != null) {
413 helperImpl.project.setName(name);
414 helperImpl.project.addReference(name, helperImpl.project);
415 }
416
417 if (id != null) {
418 helperImpl.project.addReference(id, helperImpl.project);
419 }
420
421 if (helperImpl.project.getProperty("basedir") != null) {
422 helperImpl.project.setBasedir(helperImpl.project.getProperty("basedir"));
423 } else {
424 if (baseDir == null) {
425 helperImpl.project.setBasedir(helperImpl.buildFileParent.getAbsolutePath());
426 } else {
427 // check whether the user has specified an absolute path
428 if ((new File(baseDir)).isAbsolute()) {
429 helperImpl.project.setBasedir(baseDir);
430 } else {
431 File resolvedBaseDir = helperImpl.project.resolveFile(baseDir,
432 helperImpl.buildFileParent);
433 helperImpl.project.setBaseDir(resolvedBaseDir);
434 }
435 }
436 }
437
438 helperImpl.project.addTarget("", helperImpl.implicitTarget);
439 }
440
441 /**
442 * Handles the start of a top-level element within the project. An
443 * appropriate handler is created and initialised with the details
444 * of the element.
445 *
446 * @param name The name of the element being started.
447 * Will not be <code>null</code>.
448 * @param attrs Attributes of the element being started.
449 * Will not be <code>null</code>.
450 *
451 * @exception SAXParseException if the tag given is not
452 * <code>"taskdef"</code>, <code>"typedef"</code>,
453 * <code>"property"</code>, <code>"target"</code>
454 * or a data type definition
455 */
456 public void startElement(String name, AttributeList attrs) throws SAXParseException {
457 if (name.equals("target")) {
458 handleTarget(name, attrs);
459 } else {
460 handleElement(helperImpl, this, helperImpl.implicitTarget,
461 name, attrs);
462 }
463 }
464
465 /**
466 * Handles a target definition element by creating a target handler
467 * and initialising is with the details of the element.
468 *
469 * @param tag The name of the element to be handled.
470 * Will not be <code>null</code>.
471 * @param attrs Attributes of the element to be handled.
472 * Will not be <code>null</code>.
473 *
474 * @exception SAXParseException if an error occurs initialising
475 * the handler
476 */
477 private void handleTarget(String tag, AttributeList attrs) throws SAXParseException {
478 new TargetHandler(helperImpl, this).init(tag, attrs);
479 }
480
481 }
482
483 /**
484 * Handler for "target" elements.
485 */
486 static class TargetHandler extends AbstractHandler {
487 private Target target;
488
489 /**
490 * Constructor which just delegates to the superconstructor.
491 *
492 * @param parentHandler The handler which should be restored to the
493 * parser at the end of the element.
494 * Must not be <code>null</code>.
495 */
496 public TargetHandler(ProjectHelperImpl helperImpl, DocumentHandler parentHandler) {
497 super(helperImpl, parentHandler);
498 }
499
500 /**
501 * Initialisation routine called after handler creation
502 * with the element name and attributes. The attributes which
503 * this handler can deal with are: <code>"name"</code>,
504 * <code>"depends"</code>, <code>"if"</code>,
505 * <code>"unless"</code>, <code>"id"</code> and
506 * <code>"description"</code>.
507 *
508 * @param tag Name of the element which caused this handler
509 * to be created. Should not be <code>null</code>.
510 * Ignored in this implementation.
511 * @param attrs Attributes of the element which caused this
512 * handler to be created. Must not be <code>null</code>.
513 *
514 * @exception SAXParseException if an unexpected attribute is encountered
515 * or if the <code>"name"</code> attribute is missing.
516 */
517 public void init(String tag, AttributeList attrs) throws SAXParseException {
518 String name = null;
519 String depends = "";
520 String ifCond = null;
521 String unlessCond = null;
522 String id = null;
523 String description = null;
524
525 for (int i = 0; i < attrs.getLength(); i++) {
526 String key = attrs.getName(i);
527 String value = attrs.getValue(i);
528
529 if (key.equals("name")) {
530 name = value;
531 if (name.equals("")) {
532 throw new BuildException("name attribute must not"
533 + " be empty",
534 new Location(helperImpl.locator));
535 }
536 } else if (key.equals("depends")) {
537 depends = value;
538 } else if (key.equals("if")) {
539 ifCond = value;
540 } else if (key.equals("unless")) {
541 unlessCond = value;
542 } else if (key.equals("id")) {
543 id = value;
544 } else if (key.equals("description")) {
545 description = value;
546 } else if (key.equals("address")) {
547 target.setAddress(value);
548 } else {
549 throw new SAXParseException("Unexpected attribute \""
550 + key + "\"", helperImpl.locator);
551 }
552 }
553
554 if (name == null) {
555 throw new SAXParseException("target element appears without a name attribute",
556 helperImpl.locator);
557 }
558
559 target = new Target();
560
561 // implicit target must be first on dependency list
562 target.addDependency("");
563
564 target.setName(name);
565 target.setIf(ifCond);
566 target.setUnless(unlessCond);
567 target.setDescription(description);
568 helperImpl.project.addTarget(name, target);
569
570 if (id != null && !id.equals("")) {
571 helperImpl.project.addReference(id, target);
572 }
573
574 // take care of dependencies
575
576 if (depends.length() > 0) {
577 target.setDepends(depends);
578 }
579 }
580
581 /**
582 * Handles the start of an element within a target.
583 *
584 * @param name The name of the element being started.
585 * Will not be <code>null</code>.
586 * @param attrs Attributes of the element being started.
587 * Will not be <code>null</code>.
588 *
589 * @exception SAXParseException if an error occurs when initialising
590 * the appropriate child handler
591 */
592 public void startElement(String name, AttributeList attrs) throws SAXParseException {
593 handleElement(helperImpl, this, target, name, attrs);
594 }
595 }
596
597 /**
598 * Start a new DataTypeHandler if element is known to be a
599 * data-type and a TaskHandler otherwise.
600 *
601 * <p>Factored out of TargetHandler.</p>
602 *
603 * @since Ant 1.6
604 */
605 private static void handleElement(ProjectHelperImpl helperImpl,
606 DocumentHandler parent,
607 Target target, String elementName,
608 AttributeList attrs)
609 throws SAXParseException {
610 if (elementName.equals("description")) {
611 new DescriptionHandler(helperImpl, parent);
612 } else if (helperImpl.project.getDataTypeDefinitions()
613 .get(elementName) != null) {
614 new DataTypeHandler(helperImpl, parent, target)
615 .init(elementName, attrs);
616 } else {
617 new TaskHandler(helperImpl, parent, target, null, target)
618 .init(elementName, attrs);
619 }
620 }
621
622 /**
623 * Handler for "description" elements.
624 */
625 static class DescriptionHandler extends AbstractHandler {
626
627 /**
628 * Constructor which just delegates to the superconstructor.
629 *
630 * @param parentHandler The handler which should be restored to the
631 * parser at the end of the element.
632 * Must not be <code>null</code>.
633 */
634 public DescriptionHandler(ProjectHelperImpl helperImpl,
635 DocumentHandler parentHandler) {
636 super(helperImpl, parentHandler);
637 }
638
639 /**
640 * Adds the text as description to the project.
641 *
642 * @param buf A character array of the text within the element.
643 * Will not be <code>null</code>.
644 * @param start The start element in the array.
645 * @param count The number of characters to read from the array.
646 */
647 public void characters(char[] buf, int start, int count) {
648 String text = new String(buf, start, count);
649 String currentDescription = helperImpl.project.getDescription();
650 if (currentDescription == null) {
651 helperImpl.project.setDescription(text);
652 } else {
653 helperImpl.project.setDescription(currentDescription + text);
654 }
655 }
656
657 }
658
659 /**
660 * Handler for all task elements.
661 */
662 static class TaskHandler extends AbstractHandler {
663 /** Containing target, if any. */
664 private Target target;
665 /**
666 * Container for the task, if any. If target is
667 * non-<code>null</code>, this must be too.
668 */
669 private TaskContainer container;
670 /**
671 * Task created by this handler.
672 */
673 private Task task;
674 /**
675 * Wrapper for the parent element, if any. The wrapper for this
676 * element will be added to this wrapper as a child.
677 */
678 private RuntimeConfigurable parentWrapper;
679 /**
680 * Wrapper for this element which takes care of actually configuring
681 * the element, if this element is contained within a target.
682 * Otherwise the configuration is performed with the configure method.
683 * @see ProjectHelper#configure(Object,AttributeList,Project)
684 */
685 private RuntimeConfigurable wrapper = null;
686
687 /**
688 * Constructor.
689 *
690 * @param parentHandler The handler which should be restored to the
691 * parser at the end of the element.
692 * Must not be <code>null</code>.
693 *
694 * @param container Container for the element.
695 * Must not be <code>null</code>.
696 *
697 * @param parentWrapper Wrapper for the parent element, if any.
698 * May be <code>null</code>.
699 *
700 * @param target Target this element is part of.
701 * Must not be <code>null</code>.
702 */
703 public TaskHandler(ProjectHelperImpl helperImpl, DocumentHandler parentHandler,
704 TaskContainer container,
705 RuntimeConfigurable parentWrapper, Target target) {
706 super(helperImpl, parentHandler);
707 this.container = container;
708 this.parentWrapper = parentWrapper;
709 this.target = target;
710 }
711
712 /**
713 * Initialisation routine called after handler creation
714 * with the element name and attributes. This configures
715 * the element with its attributes and sets it up with
716 * its parent container (if any). Nested elements are then
717 * added later as the parser encounters them.
718 *
719 * @param tag Name of the element which caused this handler
720 * to be created. Must not be <code>null</code>.
721 *
722 * @param attrs Attributes of the element which caused this
723 * handler to be created. Must not be <code>null</code>.
724 *
725 * @exception SAXParseException in case of error (not thrown in
726 * this implementation)
727 */
728 public void init(String tag, AttributeList attrs) throws SAXParseException {
729 try {
730 task = helperImpl.project.createTask(tag);
731 } catch (BuildException e) {
732 // swallow here, will be thrown again in
733 // UnknownElement.maybeConfigure if the problem persists.
734 }
735
736 if (task == null) {
737 task = new UnknownElement(tag);
738 task.setProject(helperImpl.project);
739 //XXX task.setTaskType(tag);
740 task.setTaskName(tag);
741 }
742
743 task.setLocation(new Location(helperImpl.locator));
744 helperImpl.configureId(task, attrs);
745
746 task.setOwningTarget(target);
747 container.addTask(task);
748 task.init();
749 wrapper = task.getRuntimeConfigurableWrapper();
750 wrapper.setAttributes(attrs);
751 if (parentWrapper != null) {
752 parentWrapper.addChild(wrapper);
753 }
754 }
755
756 /**
757 * Adds text to the task, using the wrapper.
758 *
759 * @param buf A character array of the text within the element.
760 * Will not be <code>null</code>.
761 * @param start The start element in the array.
762 * @param count The number of characters to read from the array.
763 */
764 public void characters(char[] buf, int start, int count) {
765 wrapper.addText(buf, start, count);
766 }
767
768 /**
769 * Handles the start of an element within a target. Task containers
770 * will always use another task handler, and all other tasks
771 * will always use a nested element handler.
772 *
773 * @param name The name of the element being started.
774 * Will not be <code>null</code>.
775 * @param attrs Attributes of the element being started.
776 * Will not be <code>null</code>.
777 *
778 * @exception SAXParseException if an error occurs when initialising
779 * the appropriate child handler
780 */
781 public void startElement(String name, AttributeList attrs) throws SAXParseException {
782 if (task instanceof TaskContainer) {
783 // task can contain other tasks - no other nested elements possible
784 new TaskHandler(helperImpl, this, (TaskContainer) task,
785 wrapper, target).init(name, attrs);
786 } else {
787 new NestedElementHandler(helperImpl, this, task,
788 wrapper, target).init(name, attrs);
789 }
790 }
791 }
792
793 /**
794 * Handler for all nested properties.
795 */
796 static class NestedElementHandler extends AbstractHandler {
797 /** Parent object (task/data type/etc). */
798 private Object parent;
799 /** The nested element itself. */
800 private Object child;
801 /**
802 * Wrapper for the parent element, if any. The wrapper for this
803 * element will be added to this wrapper as a child.
804 */
805 private RuntimeConfigurable parentWrapper;
806 /**
807 * Wrapper for this element which takes care of actually configuring
808 * the element, if a parent wrapper is provided.
809 * Otherwise the configuration is performed with the configure method.
810 * @see ProjectHelper#configure(Object,AttributeList,Project)
811 */
812 private RuntimeConfigurable childWrapper = null;
813 /** Target this element is part of, if any. */
814 private Target target;
815
816 /**
817 * Constructor.
818 *
819 * @param parentHandler The handler which should be restored to the
820 * parser at the end of the element.
821 * Must not be <code>null</code>.
822 *
823 * @param parent Parent of this element (task/data type/etc).
824 * Must not be <code>null</code>.
825 *
826 * @param parentWrapper Wrapper for the parent element, if any.
827 * Must not be <code>null</code>.
828 *
829 * @param target Target this element is part of.
830 * Must not be <code>null</code>.
831 */
832 public NestedElementHandler(ProjectHelperImpl helperImpl,
833 DocumentHandler parentHandler,
834 Object parent,
835 RuntimeConfigurable parentWrapper,
836 Target target) {
837 super(helperImpl, parentHandler);
838
839 if (parent instanceof TypeAdapter) {
840 this.parent = ((TypeAdapter) parent).getProxy();
841 } else {
842 this.parent = parent;
843 }
844 this.parentWrapper = parentWrapper;
845 this.target = target;
846 }
847
848 /**
849 * Initialisation routine called after handler creation
850 * with the element name and attributes. This configures
851 * the element with its attributes and sets it up with
852 * its parent container (if any). Nested elements are then
853 * added later as the parser encounters them.
854 *
855 * @param propType Name of the element which caused this handler
856 * to be created. Must not be <code>null</code>.
857 *
858 * @param attrs Attributes of the element which caused this
859 * handler to be created. Must not be <code>null</code>.
860 *
861 * @exception SAXParseException in case of error, such as a
862 * BuildException being thrown during configuration.
863 */
864 public void init(String propType, AttributeList attrs) throws SAXParseException {
865 Class parentClass = parent.getClass();
866 IntrospectionHelper ih =
867 IntrospectionHelper.getHelper(parentClass);
868
869 try {
870 String elementName = propType.toLowerCase(Locale.US);
871 if (parent instanceof UnknownElement) {
872 UnknownElement uc = new UnknownElement(elementName);
873 uc.setProject(helperImpl.project);
874 ((UnknownElement) parent).addChild(uc);
875 child = uc;
876 } else {
877 child = ih.createElement(helperImpl.project, parent, elementName);
878 }
879
880 helperImpl.configureId(child, attrs);
881
882 childWrapper = new RuntimeConfigurable(child, propType);
883 childWrapper.setAttributes(attrs);
884 parentWrapper.addChild(childWrapper);
885 } catch (BuildException exc) {
886 throw new SAXParseException(exc.getMessage(), helperImpl.locator, exc);
887 }
888 }
889
890 /**
891 * Adds text to the element, using the wrapper.
892 *
893 * @param buf A character array of the text within the element.
894 * Will not be <code>null</code>.
895 * @param start The start element in the array.
896 * @param count The number of characters to read from the array.
897 */
898 public void characters(char[] buf, int start, int count) {
899 childWrapper.addText(buf, start, count);
900 }
901
902 /**
903 * Handles the start of an element within this one. Task containers
904 * will always use a task handler, and all other elements
905 * will always use another nested element handler.
906 *
907 * @param name The name of the element being started.
908 * Will not be <code>null</code>.
909 * @param attrs Attributes of the element being started.
910 * Will not be <code>null</code>.
911 *
912 * @exception SAXParseException if an error occurs when initialising
913 * the appropriate child handler
914 */
915 public void startElement(String name, AttributeList attrs) throws SAXParseException {
916 if (child instanceof TaskContainer) {
917 // taskcontainer nested element can contain other tasks - no other
918 // nested elements possible
919 new TaskHandler(helperImpl, this, (TaskContainer) child,
920 childWrapper, target).init(name, attrs);
921 } else {
922 new NestedElementHandler(helperImpl, this, child,
923 childWrapper, target).init(name, attrs);
924 }
925 }
926 }
927
928 /**
929 * Handler for all data types directly subordinate to project or target.
930 */
931 static class DataTypeHandler extends AbstractHandler {
932 /** Parent target, if any. */
933 private Target target;
934 /** The element being configured. */
935 private Object element;
936 /** Wrapper for this element, if it's part of a target. */
937 private RuntimeConfigurable wrapper = null;
938
939 /**
940 * Constructor with a target specified.
941 *
942 * @param parentHandler The handler which should be restored to the
943 * parser at the end of the element.
944 * Must not be <code>null</code>.
945 *
946 * @param target The parent target of this element.
947 * Must not be <code>null</code>.
948 */
949 public DataTypeHandler(ProjectHelperImpl helperImpl,
950 DocumentHandler parentHandler, Target target) {
951 super(helperImpl, parentHandler);
952 this.target = target;
953 }
954
955 /**
956 * Initialisation routine called after handler creation
957 * with the element name and attributes. This configures
958 * the element with its attributes and sets it up with
959 * its parent container (if any). Nested elements are then
960 * added later as the parser encounters them.
961 *
962 * @param propType Name of the element which caused this handler
963 * to be created. Must not be <code>null</code>.
964 *
965 * @param attrs Attributes of the element which caused this
966 * handler to be created. Must not be <code>null</code>.
967 *
968 * @exception SAXParseException in case of error, such as a
969 * BuildException being thrown during configuration.
970 */
971 public void init(String propType, AttributeList attrs) throws SAXParseException {
972 try {
973 element = helperImpl.project.createDataType(propType);
974 if (element == null) {
975 throw new BuildException("Unknown data type " + propType);
976 }
977
978 wrapper = new RuntimeConfigurable(element, propType);
979 wrapper.setAttributes(attrs);
980 target.addDataType(wrapper);
981 } catch (BuildException exc) {
982 throw new SAXParseException(exc.getMessage(), helperImpl.locator, exc);
983 }
984 }
985
986 /**
987 * Adds text to the using the wrapper.
988 *
989 * @param buf A character array of the text within the element.
990 * Will not be <code>null</code>.
991 * @param start The start element in the array.
992 * @param count The number of characters to read from the array.
993 *
994 * @see ProjectHelper#addText(Project,Object,char[],int,int)
995 */
996 public void characters(char[] buf, int start, int count) {
997 wrapper.addText(buf, start, count);
998 }
999
1000 /**
1001 * Handles the start of an element within this one.
1002 * This will always use a nested element handler.
1003 *
1004 * @param name The name of the element being started.
1005 * Will not be <code>null</code>.
1006 * @param attrs Attributes of the element being started.
1007 * Will not be <code>null</code>.
1008 *
1009 * @exception SAXParseException if an error occurs when initialising
1010 * the child handler
1011 */
1012 public void startElement(String name, AttributeList attrs) throws SAXParseException {
1013 new NestedElementHandler(helperImpl, this, element, wrapper, target).init(name, attrs);
1014 }
1015 }
1016
1017 /**
1018 * Scans an attribute list for the <code>id</code> attribute and
1019 * stores a reference to the target object in the project if an
1020 * id is found.
1021 * <p>
1022 * This method was moved out of the configure method to allow
1023 * it to be executed at parse time.
1024 *
1025 * @see #configure(Object,AttributeList,Project)
1026 */
1027 private void configureId(Object target, AttributeList attr) {
1028 String id = attr.getValue("id");
1029 if (id != null) {
1030 project.addReference(id, target);
1031 }
1032 }
1033}
Note: See TracBrowser for help on using the repository browser.