001: /*
002: * ========================================================================
003: *
004: * Copyright 2001-2003 The Apache Software Foundation.
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: *
018: * ========================================================================
019: */
020: package org.apache.cactus.build.documentation;
021:
022: import java.io.File;
023: import java.io.IOException;
024:
025: import javax.xml.parsers.DocumentBuilder;
026: import javax.xml.parsers.DocumentBuilderFactory;
027: import javax.xml.parsers.ParserConfigurationException;
028:
029: import org.apache.tools.ant.BuildException;
030: import org.apache.tools.ant.Project;
031: import org.apache.tools.ant.Task;
032: import org.apache.tools.ant.types.XMLCatalog;
033: import org.w3c.dom.Document;
034: import org.w3c.dom.Element;
035: import org.w3c.dom.NodeList;
036: import org.xml.sax.SAXException;
037:
038: /**
039: * Checks the sitemap for invalid resource links (currently only local).
040: *
041: * @version $Id: CheckSitemapTask.java 238809 2004-02-29 09:42:33Z vmassol $
042: */
043: public class CheckSitemapTask extends Task {
044: /**
045: * Location of the sitemap.xml file.
046: */
047: private File sitemap;
048:
049: /**
050: * Whether the task should fail when an error occurred.
051: */
052: private boolean failOnError = true;
053:
054: /**
055: * The output directory where files are generated.
056: */
057: private File outputDir;
058:
059: /**
060: * For resolving entities such as DTDs.
061: */
062: private XMLCatalog xmlCatalog = new XMLCatalog();
063:
064: /**
065: * Sets the location of the sitemap file.
066: *
067: * @param theSitemap The sitemap file
068: */
069: public void setSitemap(File theSitemap) {
070: this .sitemap = theSitemap;
071: }
072:
073: /**
074: * @param theOutputDir the location of the output directory where files are
075: * generated
076: */
077: public void setOutputDir(File theOutputDir) {
078: this .outputDir = theOutputDir;
079: }
080:
081: /**
082: * Sets whether the task should fail when an error occurs.
083: *
084: * @param theFailOnError Whether to fail on errors
085: */
086: public void setFailOnError(boolean theFailOnError) {
087: this .failOnError = theFailOnError;
088: }
089:
090: /**
091: * Add the catalog to our internal catalog
092: *
093: * @param theXmlCatalog the XMLCatalog instance to use to look up DTDs
094: */
095: public void addConfiguredXMLCatalog(XMLCatalog theXmlCatalog) {
096: this .xmlCatalog.addConfiguredXMLCatalog(theXmlCatalog);
097: }
098:
099: /**
100: * @see Task#init()
101: */
102: public void init() throws BuildException {
103: super .init();
104: // Initialize internal instance of XMLCatalog
105: this .xmlCatalog.setProject(getProject());
106: }
107:
108: /**
109: * Execute task.
110: *
111: * @see Task#execute()
112: */
113: public void execute() throws BuildException {
114: if (this .sitemap == null) {
115: throw new BuildException(
116: "The [sitemap] attribute must be set");
117: }
118: if (this .outputDir == null) {
119: throw new BuildException(
120: "The [outputDir] attribute must be set");
121: }
122:
123: if (!this .sitemap.exists()) {
124: throw new BuildException(
125: "The [sitemap] attribute must point to an existing file");
126: }
127:
128: log("Checking sitemap at [" + sitemap + "]", Project.MSG_INFO);
129: log("Generated doc output directory is [" + outputDir + "]",
130: Project.MSG_VERBOSE);
131:
132: try {
133: DocumentBuilder builder = getDocumentBuilder();
134: builder.setEntityResolver(this .xmlCatalog);
135: Document document = builder.parse(sitemap);
136: if (!checkSitemap(document) && (this .failOnError)) {
137: throw new BuildException("Found errors in sitemap.");
138: }
139: } catch (SAXException e) {
140: throw new BuildException(e);
141: } catch (IOException e) {
142: throw new BuildException(e);
143: } catch (ParserConfigurationException e) {
144: throw new BuildException(e);
145: }
146: }
147:
148: /**
149: * Instantiates and returns a JAXP DocumentBuilder.
150: *
151: * @return The DocumentBuilder instance
152: * @throws ParserConfigurationException If instantiating the builder threw
153: * a configuration exception
154: */
155: private DocumentBuilder getDocumentBuilder()
156: throws ParserConfigurationException {
157: DocumentBuilderFactory factory = DocumentBuilderFactory
158: .newInstance();
159: factory.setNamespaceAware(false);
160: factory.setValidating(false);
161: return factory.newDocumentBuilder();
162: }
163:
164: /**
165: * Checks the sitemap for valid links.
166: *
167: * @param theDocument The DOM representation of the Sitemap.
168: * @return Whether the check was successful
169: */
170: private boolean checkSitemap(Document theDocument) {
171: Element sitemap = (Element) theDocument.getElementsByTagName(
172: "sitemap").item(0);
173: NodeList resources = sitemap.getElementsByTagName("resource");
174: boolean success = true;
175: for (int i = 0; i < resources.getLength(); i++) {
176: Element resource = (Element) resources.item(i);
177: if (!checkSitemapResource(resource)) {
178: success = false;
179: }
180: }
181: return success;
182: }
183:
184: /**
185: * Checks a single resource in a sitemap.
186: *
187: * @param theElement The resource element
188: * @return Whether the check was successful
189: */
190: private boolean checkSitemapResource(Element theElement) {
191: boolean isResourcePresent = true;
192: String id = theElement.getAttribute("id");
193:
194: if (isResourceToBeChecked(theElement)) {
195: String target = theElement.getAttribute("target");
196:
197: if ((target == null) || (target.length() == 0)) {
198: log("Skipping remote resource [" + id + "]",
199: Project.MSG_VERBOSE);
200: } else {
201: isResourcePresent = checkLocalSitemapResource(id,
202: target);
203: }
204: } else {
205: log("This resource should not be checked: [" + id + "]",
206: Project.MSG_VERBOSE);
207: }
208: return isResourcePresent;
209: }
210:
211: /**
212: * @param theElement the resource for which to verify if it is to be checked
213: * for existence
214: * @return true if the resource must be checked or false otherwise
215: */
216: private boolean isResourceToBeChecked(Element theElement) {
217: // Checks are enabled by default
218: boolean isToBeChecked = true;
219:
220: if ((theElement.getAttribute("check") != null)
221: && (theElement.getAttribute("check").length() > 0)) {
222: isToBeChecked = Boolean.valueOf(
223: theElement.getAttribute("check")).booleanValue();
224: }
225: return isToBeChecked;
226: }
227:
228: /**
229: * Checks whether a specified local sitemap resource points to an existing
230: * file.
231: *
232: * @param theId The <code>id</code> attribute of the resource element
233: * @param theTarget The <code>target</code> attribute of the resource
234: * element, the relative path from the directory containing the sitemap file
235: * to the generated file
236: * @return Whether the file exists
237: */
238: private boolean checkLocalSitemapResource(String theId,
239: String theTarget) {
240: File targetFile = new File(this .outputDir, theTarget);
241: log("Checking resource [" + theId + "] at [" + theTarget + "]",
242: Project.MSG_DEBUG);
243: if (!targetFile.exists()) {
244: log("Sitemap resource [" + theId + "] not found under ["
245: + targetFile + "]", Project.MSG_ERR);
246: return false;
247: }
248: return true;
249: }
250:
251: }
|