001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.components.validation.impl;
018:
019: import java.io.IOException;
020: import java.util.ArrayList;
021: import java.util.Iterator;
022: import java.util.List;
023:
024: import org.apache.excalibur.source.Source;
025: import org.apache.excalibur.source.SourceResolver;
026: import org.apache.excalibur.source.SourceValidity;
027: import org.apache.excalibur.source.impl.validity.AggregatedValidity;
028: import org.xml.sax.EntityResolver;
029: import org.xml.sax.InputSource;
030: import org.xml.sax.SAXException;
031:
032: /**
033: * <p>An internal {@link InputSource} resolver that can be used while parsing
034: * schemas.</p>
035: *
036: * <p>This instance will track all resolved external sources and will store their
037: * validity information. An aggregated {@link SourceValidity} for all resolved
038: * sources can be retrieved when {@link #close() closing} this instance.</p>
039: *
040: */
041: public class ValidationResolver implements EntityResolver {
042:
043: /** <p>The {@link SourceResolver} to access {@link Source}s.</p> */
044: private final SourceResolver sourceResolver;
045: /** <p>The {@link EntityResolver} to resolve public IDs against catalogs.</p> */
046: private final EntityResolver entityResolver;
047: /** <p>The global {@link SourceValidity} of all resolved sources.</p> */
048: private final AggregatedValidity sourceValidity;
049:
050: /** <p>A {@link List} of {@link Source} to be released when closing.</p> */
051: private final List sources = new ArrayList();
052:
053: /** <p>A flag indicating whether this instance has been closed.</p> */
054: private boolean closed = false;
055:
056: /**
057: * <p>Create a new {@link ValidationResolver} instance.</p>
058: *
059: * @throws NullPointerException if one of the specified {@link SourceResolver}
060: * or {@link EntityResolver} was <b>null</b>.
061: */
062: public ValidationResolver(SourceResolver sourceResolver,
063: EntityResolver entityResolver) {
064: if (sourceResolver == null)
065: throw new NullPointerException("Null source");
066: if (entityResolver == null)
067: throw new NullPointerException("Null entity");
068: this .sourceValidity = new AggregatedValidity();
069: this .sourceResolver = sourceResolver;
070: this .entityResolver = entityResolver;
071: }
072:
073: /**
074: * <p>Resolve a {@link Source} into an {@link InputSource}.</p>
075: */
076: public InputSource resolveSource(Source source) throws IOException,
077: SAXException {
078: return this .resolveSource(source, null, null);
079: }
080:
081: /**
082: * <p>Resolve a {@link Source} into an {@link InputSource}, specifying a
083: * specific system identifier.</p>
084: */
085: public InputSource resolveSource(Source source, String systemId)
086: throws IOException, SAXException {
087: return this .resolveSource(source, systemId, null);
088: }
089:
090: /**
091: * <p>Resolve a {@link Source} into an {@link InputSource}, specifying both
092: * a specific system identifier and a public identifier.</p>
093: *
094: * <p>If the specified system identifier was <b>null</b> the returned
095: * {@link InputSource}'s {@link InputSource#getSystemId() system identifier}
096: * will be obtained calling the {@link Source#getURI()} method.</p>
097: */
098: public InputSource resolveSource(Source source, String systemId,
099: String publicId) throws IOException, SAXException {
100: if (this .closed)
101: throw new IllegalStateException("Resolver closed");
102:
103: /* Validate what we've been passed */
104: if (source == null)
105: throw new NullPointerException("Null source specified");
106:
107: /* Record the current source in the validities to return */
108: this .sourceValidity.add(source.getValidity());
109:
110: /* Ensure that we have a proper system id */
111: if (systemId == null)
112: systemId = source.getURI();
113:
114: /* Create a new input source and return it filled out entirely */
115: InputSource input = new InputSource(systemId);
116: input.setByteStream(source.getInputStream());
117: if (publicId != null)
118: input.setPublicId(publicId);
119: return input;
120: }
121:
122: /**
123: * <p>Resolve an entity identified by a specific system identifier as an
124: * {@link InputSource}.</p>
125: */
126: public InputSource resolveEntity(String systemId)
127: throws IOException, SAXException {
128: return this .resolveEntity(null, null, systemId);
129: }
130:
131: /**
132: * <p>Resolve an entity identified by a specific system and public identifier
133: * as an {@link InputSource}.</p>
134: */
135: public InputSource resolveEntity(String publicId, String systemId)
136: throws IOException, SAXException {
137: return this .resolveEntity(null, publicId, systemId);
138: }
139:
140: /**
141: * <p>Resolve an entity identified by a specific system and public identifier
142: * and relative to a specified base location as an {@link InputSource}.</p>
143: */
144: public InputSource resolveEntity(String base, String publicId,
145: String systemId) throws IOException, SAXException {
146: if (this .closed)
147: throw new IllegalStateException("Resolver closed");
148:
149: /* If the specified system id was null use the global entity resolver */
150: if (systemId == null) {
151: InputSource source = this .entityResolver.resolveEntity(
152: publicId, null);
153: if ((source == null) || (source.getSystemId() == null)) {
154: throw new IOException("Can't resolve \"" + publicId
155: + "\"");
156: }
157: systemId = source.getSystemId();
158: }
159:
160: /* Now that we have a valid system id, attempt to resolve it as a source */
161: final Source source;
162: if (base == null) {
163: source = this .sourceResolver.resolveURI(systemId);
164: } else {
165: source = this .sourceResolver.resolveURI(systemId, base,
166: null);
167: }
168:
169: /* Record this source as a source to release back to the resolver */
170: this .sources.add(source);
171:
172: /* Return the resolved input source back to the caller */
173: return this .resolveSource(source, systemId, publicId);
174: }
175:
176: /**
177: * <p>Close this {@link ValidationResolver} instance, releasing all created
178: * {@link Source}s back to the {@link SourceResolver} and returning an
179: * aggregated {@link SourceValidity}.</p>
180: */
181: public SourceValidity close() {
182:
183: /* Release all the sources that were opened using this source resolver */
184: Iterator iterator = this .sources.iterator();
185: while (iterator.hasNext()) {
186: this .sourceResolver.release((Source) iterator.next());
187: }
188:
189: /* Mark this instance as closed */
190: this .closed = true;
191:
192: /* Return the source validity associated with this instance */
193: return this .sourceValidity;
194: }
195:
196: /**
197: * <p>Ensure that when this object is garbage collected, the {@link #close()}
198: * method is executed.</p>
199: */
200: protected void finalize() throws Throwable {
201: try {
202: super.finalize();
203: } finally {
204: if (this.closed)
205: return;
206: this.close();
207: }
208: }
209: }
|