blob: 6038c03c2fa46e39d194580c27a1b2e0d8651544 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.core.startup;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.ModuleInstaller;
import org.netbeans.Util;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.modules.Dependency;
import org.openide.modules.SpecificationVersion;
import org.openide.xml.XMLUtil;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
/**
* Parser and interpreter for automatic module dependencies.
* Public for possible access from AU (see #29577).
* Usage: see implementation of {@link ModuleInstaller#refineDependencies}.
* @author Jesse Glick, with help from NB XML module
* @see "#30161"
* @see <a href="http://www.netbeans.org/dtds/module-auto-deps-1_0.dtd"><samp>-//NetBeans//DTD Module Automatic Dependencies 1.0//EN</samp></a>
* @since org.netbeans.core/1 1.12
*/
public final class AutomaticDependencies {
private static final Logger LOG = Logger.getLogger(AutomaticDependencies.class.getName());
private static AutomaticDependencies INSTANCE;
private AutomaticDependencies() {}
/**
* Create an empty list of transformations.
* @return an empty list
*/
public static AutomaticDependencies empty() {
return new AutomaticDependencies();
}
/** Create default list of transformations.
* This is now all handled from declarative configuration files:
* in the system filesystem, ModuleAutoDeps/*.xml may be added
* according to the DTD "-//NetBeans//DTD Module Automatic Dependencies 1.0//EN".
*
* @since 1.39
* @return the default list
*/
public static AutomaticDependencies getDefault() {
if (INSTANCE != null) {
return INSTANCE;
}
FileObject depsFolder = FileUtil.getConfigFile("ModuleAutoDeps");
if (depsFolder != null) {
FileObject[] kids = depsFolder.getChildren();
List<URL> urls = new ArrayList<URL>(Math.max(kids.length, 1));
for (FileObject kid : kids) {
if (kid.hasExt("xml")) { // NOI18N
urls.add(kid.toURL());
}
}
try {
INSTANCE = AutomaticDependencies.parse(urls.toArray(new URL[urls.size()]));
} catch (IOException e) {
Util.err.log(Level.WARNING, null, e);
} catch (SAXException e) {
Util.err.log(Level.WARNING, null, e);
}
}
if (INSTANCE == null) {
// Parsing failed, or no files.
INSTANCE = AutomaticDependencies.empty();
}
if (Util.err.isLoggable(Level.FINE)) {
Util.err.fine("Auto deps: " + INSTANCE);
}
return INSTANCE;
}
/**
* Create a list of transformations based on some XML files.
* They must be valid (this method may not validate them).
* Doctype must be <samp>&lt;transformations&gt;</samp> from
* <samp>-//NetBeans//DTD Module Automatic Dependencies 1.0//EN</samp>.
* @param urls the XML files
* @throws SAXException if malformed
* @throws IOException if unloadable
* @return a list of parsed transformations
*/
public static AutomaticDependencies parse(URL[] urls) throws SAXException, IOException {
LOG.log(Level.FINE, "Parsing automatic dependencies {0}", Arrays.asList(urls));
AutomaticDependencies h = new AutomaticDependencies();
Parser p = new Parser(h.new Handler());
for (URL url : urls) {
String id = url.toExternalForm();
InputStream inS = null;
try {
InputSource is = new InputSource(id);
inS = new BufferedInputStream(url.openStream());
is.setByteStream(inS);
p.parse(is);
} catch (SAXException e) {
throw new SAXException("While parsing: " + id, e);
} catch (IOException e) {
IOException exc = new IOException("While parsing: " + id);
exc.initCause(e);
throw exc;
}
finally {
if (inS != null) {
inS.close();
}
}
}
return h;
}
/**
* For testing purposes only. Should dump file structure.
* @param x list of URLs to parse
*/
public static void main(String[] x) throws Exception {
URL[] urls = new URL[x.length];
for (int i = 0; i < x.length; i++) {
urls[i] = new URL(x[i]);
}
parse(urls); // warm up classes
long time = System.currentTimeMillis();
System.out.println(parse(urls));
long taken = System.currentTimeMillis() - time;
System.out.println("Time taken: " + taken + " msec");
}
// ------------- INTERPRETATION --------------
/**
* A struct for holding information on the result of dependency refinement.
* @see #refineDependenciesAndReport
* @since org.netbeans.core/1 1.19
*/
public static final class Report {
private final String cnb;
private final Set<Dependency> added;
private final Set<Dependency> removed;
private final Set<String> messages;
Report(String cnb, Set<Dependency> added, Set<Dependency> removed, Set<String> messages) {
this.cnb = cnb;
this.added = added;
this.removed = removed;
this.messages = messages;
}
/**
* Get a set of dependencies that were added.
* @return a set of {@link Dependency} (may be empty)
*/
public Set<Dependency> getAdded() {
return added;
}
/**
* Get a set of dependencies that were removed.
* @return a set of {@link Dependency} (may be empty)
*/
public Set<Dependency> getRemoved() {
return removed;
}
/**
* Get a set of messages describing why transformations occurred.
* These messages are not necessarily localized but are intended for developers.
* @return a set of <code>String</code> messages (may be empty)
*/
public Set<String> getMessages() {
return messages;
}
/**
* Check if any changes were made.
* @return true if there is anything to report
*/
public boolean isModified() {
return !added.isEmpty() || !removed.isEmpty();
}
/**
* @since org.netbeans.core.startup 1.21
*/
public @Override String toString() {
return "had to upgrade dependencies for module " + cnb + ": added = " + getAdded() +
" removed = " + getRemoved() + "; details: " + getMessages(); // NOI18N
}
}
/**
* Interpret the transformations on a module, and report the changes.
* The module's dependencies may be added to, changed, or removed, according
* to the configuration of this transformations list.
* @param cnb the code name base of the module being considered
* @param dependencies a mutable set of type {@link Dependency}; call-by-reference
* @since org.netbeans.core/1 1.19
*/
public Report refineDependenciesAndReport(String cnb, Set<Dependency> dependencies) {
Set<Dependency> oldDependencies = new HashSet<Dependency>(dependencies);
// First, collect all deps of each type by name, for quick access.
// Also note that transformations apply *in parallel* so we cannot
// check triggers of a transformation based on the in-progress set.
Map<String,Dependency> modDeps = new HashMap<String,Dependency>();
Map<String,Dependency> tokDeps = new HashMap<String,Dependency>();
Map<String,Dependency> pkgDeps = new HashMap<String,Dependency>();
for (Dependency d: dependencies) {
switch (d.getType()) {
case Dependency.TYPE_MODULE:
String dcnb = (String)Util.parseCodeName(d.getName())[0];
modDeps.put(dcnb, d);
break;
case Dependency.TYPE_PACKAGE:
String name = packageBaseName(d.getName());
pkgDeps.put(name, d);
break;
case Dependency.TYPE_REQUIRES:
case Dependency.TYPE_NEEDS:
case Dependency.TYPE_RECOMMENDS:
tokDeps.put(d.getName(), d);
break;
case Dependency.TYPE_JAVA:
// ignored
break;
default:
throw new IllegalStateException(d.toString());
}
}
Set<String> messages = new TreeSet<String>();
// Now go through transformations and see if they apply.
for (TransformationGroup g: groups) {
if (g.isExcluded(cnb)) {
continue;
}
Set<Dependency> oldRunningDependencies = new HashSet<Dependency>(dependencies);
for (Transformation t: g.transformations) {
t.apply(modDeps, tokDeps, pkgDeps, dependencies);
}
if (!oldRunningDependencies.equals(dependencies)) {
messages.add(g.description);
}
}
if (!oldDependencies.equals(dependencies)) {
assert !messages.isEmpty();
Set<Dependency> added = new HashSet<Dependency>(dependencies);
added.removeAll(oldDependencies);
oldDependencies.removeAll(dependencies);
return new Report(cnb, added, oldDependencies, messages);
} else {
assert messages.isEmpty();
return new Report(cnb, Collections.<Dependency>emptySet(), Collections.<Dependency>emptySet(), Collections.<String>emptySet());
}
}
/**
* Interpret the transformations on a module.
* The module's dependencies may be added to, changed, or removed, according
* to the configuration of this transformations list.
* Does the same thing as {@link #refineDependenciesAndReport} but does not report on the details.
* @param cnb the code name base of the module being considered
* @param dependencies a mutable set of type {@link Dependency}; call-by-reference
*/
public void refineDependencies(String cnb, Set<Dependency> dependencies) {
refineDependenciesAndReport(cnb, dependencies);
}
/**
* Variant of {@link #refineDependenciesAndReport} with simple signature
* intended for use from {@code org.netbeans.nbbuild.ParseProjectXml}.
* @param cnb the code name base of the module being considered
* @param dependencies a mutable set of dependencies in the format given by
* {@link Dependency#toString} and {@link Dependency#create} on {@link Dependency#TYPE_MODULE}
* @return a message listing some changes, or null if there were no changes
* @since org.netbeans.core.startup 1.21
*/
public String refineDependenciesSimple(String cnb, Set<String> dependencies) {
Set<Dependency> deps = new HashSet<Dependency>();
for (String d : dependencies) {
deps.addAll(Dependency.create(Dependency.TYPE_MODULE, d));
}
Report r = refineDependenciesAndReport(cnb, deps);
if (r.isModified()) {
dependencies.clear();
for (Dependency d : deps) {
dependencies.add(d.toString().replaceFirst("^module ", ""));
}
return r.toString();
} else {
return null;
}
}
// ---------------- STRUCTS --------------------
private final List<TransformationGroup> groups = new ArrayList<TransformationGroup>();
public String toString() {
return "AutomaticDependencies[" + groups + "]";
}
private static final class Exclusion {
public Exclusion() {}
public String codenamebase;
public boolean prefix;
public String toString() {
return "Exclusion[" + codenamebase + ",prefix=" + prefix + "]";
}
/**
* Does this exclusion apply to the given code name base?
*/
public boolean matches(String cnb) {
return cnb.equals(codenamebase) ||
(prefix && cnb.startsWith(codenamebase + ".")); // NOI18N
}
}
private static final class TransformationGroup {
public TransformationGroup() {}
public String description;
public final List<Exclusion> exclusions = new ArrayList<Exclusion>();
public final List<Transformation> transformations = new ArrayList<Transformation>();
public String toString() {
return "TransformationGroup[" + exclusions + "," + transformations + "]";
}
/**
* Is the given code name base excluded from this group of transformations?
*/
public boolean isExcluded(String cnb) {
for (Exclusion e : exclusions) {
if (e.matches(cnb)) {
return true;
}
}
return false;
}
}
private static final class Transformation {
public Transformation() {}
public Dep trigger;
public String triggerType;
public final List<Dep> results = new ArrayList<Dep>(); // List<Dep>
public String toString() {
return "Transformation[trigger=" + trigger + ",triggerType=" + triggerType + ",results=" + results + "]";
}
/**
* Transform some dependencies.
*/
public void apply(Map<String, Dependency> modDeps,
Map<String, Dependency> tokDeps, Map<String, Dependency> pkgDeps,
Set<Dependency> dependencies) {
Dependency d = trigger.applies(modDeps, tokDeps, pkgDeps, dependencies, triggerType);
if (d != null) {
// It matched.
if (triggerType.equals("cancel")) {
// Remove it now.
dependencies.remove(d);
} else if (triggerType.equals("older")) {
// Fine, don't.
} else {
throw new IllegalStateException(triggerType);
}
// Add in results.
for (Dep nue : results) {
nue.update(modDeps, tokDeps, pkgDeps, dependencies);
}
}
}
}
private abstract static class Dep {
public Dep() {}
public final String toString() {
return manifestKey() + ": " + toManifestForm();
}
/**
* The form of the dependency in a manifest (value only).
*/
public abstract String toManifestForm();
/**
* The form of the dependency in a manifest (key part).
*/
public abstract String manifestKey();
/**
* The type of dependency according to {@link Dependency}.
*/
public abstract int type();
/**
* Make a real dependency from this pattern.
*/
public final Dependency createDependency() {
return Dependency.create(type(), toManifestForm()).iterator().next();
}
/**
* Check whether this dependency pattern applies as a trigger.
* @return the triggered actual dependency if so, else null
*/
public abstract Dependency applies(Map<String, Dependency> modDeps,
Map<String, Dependency> tokDeps, Map<String, Dependency> pkgDeps,
Set<Dependency> dependencies, String type);
/**
* Update actual dependencies assuming a trigger matched.
* This dependency pattern is to be added to the dependencies set
* (possibly upgrading an existing dependency).
*/
public abstract void update(Map<String, Dependency> modDeps,
Map<String, Dependency> tokDeps, Map<String, Dependency> pkgDeps,
Set<Dependency> dependencies);
}
private static final class ModuleDep extends Dep {
public ModuleDep() {}
public String codenamebase;
public int major = -1;
public SpecificationVersion spec = null;
public String toManifestForm() {
return codenamebase + (major == -1 ? "" : "/" + major) + (spec == null ? "" : " > " + spec);
}
public String manifestKey() {
return "OpenIDE-Module-Module-Dependencies";
}
public int type() {
return Dependency.TYPE_MODULE;
}
public Dependency applies(Map<String, Dependency> modDeps,
Map<String, Dependency> tokDeps, Map<String, Dependency> pkgDeps,
Set<Dependency> dependencies, String type) {
Dependency d = modDeps.get(codenamebase);
if (d == null) return null;
if (type.equals("cancel")) {
// That's enough.
return d;
} else if (type.equals("older")) {
// Compare. Check that d < this
return older(d) ? d : null;
} else {
throw new IllegalArgumentException(type);
}
}
/**
* Is the given dependency older than this pattern?
*/
private boolean older(Dependency d) {
if (d.getType() != Dependency.TYPE_MODULE) throw new IllegalArgumentException();
if (d.getComparison() == Dependency.COMPARE_IMPL) {
// #46961; do not upgrade impl deps like this.
return false;
}
Integer dRelI = (Integer)Util.parseCodeName(d.getName())[1];
int dRel = (dRelI == null) ? -1 : dRelI.intValue();
if (dRel < major) return true;
if (dRel > major) return false;
if (spec == null) return false;
String dSpec = d.getVersion();
if (dSpec == null) return true;
assert d.getComparison() == Dependency.COMPARE_SPEC : d.getComparison();
return new SpecificationVersion(dSpec).compareTo(spec) < 0;
}
public void update(Map<String, Dependency> modDeps, Map<String, Dependency> tokDeps, Map<String, Dependency> pkgDeps, Set<Dependency> dependencies) {
Dependency d = modDeps.get(codenamebase);
if (d != null && older(d)) {
dependencies.remove(d);
Dependency nue = createDependency();
assert !nue.equals(d) : "older() claimed to be true on itself for " + d;
dependencies.add(nue);
} else if (d == null) {
dependencies.add(createDependency());
}
}
}
/**
* Find actual package name from a package dep name.
* "javax.tv" -> "javax.tv"
* "javax.tv[Tuner]" -> "javax.tv"
* "[javax.tv.Tuner]" -> "javax.tv"
*/
private static String packageBaseName(String name) {
int i = name.indexOf('[');
if (i == -1) {
return name;
} else if (i > 0) {
return name.substring(0, i);
} else {
int i2 = name.lastIndexOf('.');
return name.substring(1, i2);
}
}
private static final class PackageDep extends Dep {
public PackageDep() {}
public String name;
public String bname;
public SpecificationVersion spec = null;
public String toManifestForm() {
return name + (spec == null ? "" : " > " + spec);
}
public String manifestKey() {
return "OpenIDE-Module-Package-Dependencies";
}
public int type() {
return Dependency.TYPE_PACKAGE;
}
/**
* Is the given dependency older than this pattern?
*/
private boolean older(Dependency d) {
if (d.getType() != Dependency.TYPE_PACKAGE) throw new IllegalArgumentException();
if (d.getComparison() == Dependency.COMPARE_IMPL) {
// #46961; do not upgrade impl deps like this.
return false;
}
if (spec == null) return false;
String dSpec = d.getVersion();
if (dSpec == null) return true;
assert d.getComparison() == Dependency.COMPARE_SPEC : d.getComparison();
return new SpecificationVersion(dSpec).compareTo(spec) < 0;
}
public Dependency applies(Map<String, Dependency> modDeps,
Map<String, Dependency> tokDeps, Map<String, Dependency> pkgDeps,
Set<Dependency> dependencies, String type) {
Dependency d = pkgDeps.get(bname);
if (d == null) {
return null;
}
if (type.equals("cancel")) {
// That's enough.
return d;
} else if (type.equals("older")) {
if (spec == null) throw new IllegalStateException();
// Compare. Check that d < this
return older(d) ? d : null;
} else {
throw new IllegalStateException(type);
}
}
public void update(Map<String, Dependency> modDeps,
Map<String, Dependency> tokDeps, Map<String, Dependency> pkgDeps,
Set<Dependency> dependencies) {
Dependency d = pkgDeps.get(bname);
if (d != null && older(d)) {
dependencies.remove(d);
dependencies.add(createDependency());
} else if (d == null) {
dependencies.add(createDependency());
}
}
}
private static final class TokenDep extends Dep {
public TokenDep() {}
public String name;
public String toManifestForm() {
return name;
}
public String manifestKey() {
return "OpenIDE-Module-Requires";
}
public int type() {
return Dependency.TYPE_REQUIRES;
}
public Dependency applies(Map<String, Dependency> modDeps,
Map<String, Dependency> tokDeps, Map<String, Dependency> pkgDeps,
Set<Dependency> dependencies, String type) {
Dependency d = tokDeps.get(name);
if (d == null) {
return null;
}
if (type.equals("cancel")) {
// That's enough.
return d;
} else {
// older is not supported
throw new IllegalStateException(type);
}
}
public void update(Map<String, Dependency> modDeps,
Map<String, Dependency> tokDeps, Map<String, Dependency> pkgDeps,
Set<Dependency> dependencies) {
if (tokDeps.get(name) == null) {
dependencies.add(createDependency());
}
}
}
// ---------------------- PARSING -----------------------
private final class Handler {
private TransformationGroup currentGroup = null;
private Transformation currentTransformation = null;
private boolean inTrigger = false;
Handler() {}
public void start_trigger(final Attributes meta) throws SAXException {
inTrigger = true;
currentTransformation.triggerType = meta.getValue("type");
}
public void end_trigger() throws SAXException {
inTrigger = false;
}
public void start_transformation(final Attributes meta) throws SAXException {
currentTransformation = new Transformation();
}
public void end_transformation() throws SAXException {
currentGroup.transformations.add(currentTransformation);
currentTransformation = null;
}
private void handleDep(Dep d) throws SAXException {
if (inTrigger) {
currentTransformation.trigger = d;
} else {
currentTransformation.results.add(d);
}
}
public void handle_module_dependency(final Attributes meta) throws SAXException {
ModuleDep d = new ModuleDep();
String major = meta.getValue("major");
if (major != null) {
d.major = Integer.parseInt(major);
}
d.codenamebase = meta.getValue("codenamebase");
String s = meta.getValue("spec");
d.spec = (s == null) ? null : new SpecificationVersion(s);
handleDep(d);
}
public void handle_token_dependency(final Attributes meta) throws SAXException {
TokenDep d = new TokenDep();
d.name = meta.getValue("name");
handleDep(d);
}
public void handle_package_dependency(final Attributes meta) throws SAXException {
PackageDep d = new PackageDep();
d.name = meta.getValue("name");
d.bname = packageBaseName(d.name);
if (inTrigger) {
if (!d.name.equals(d.bname)) throw new SAXException("Cannot use test class in trigger");
}
String s = meta.getValue("spec");
d.spec = (s == null) ? null : new SpecificationVersion(s);
handleDep(d);
}
public void start_transformationgroup(final Attributes meta) throws SAXException {
currentGroup = new TransformationGroup();
}
public void end_transformationgroup() throws SAXException {
groups.add(currentGroup);
currentGroup = null;
}
public void start_result(final Attributes meta) throws SAXException {
// do nothing
}
public void end_result() throws SAXException {
// do nothing
}
public void handle_exclusion(final Attributes meta) throws SAXException {
Exclusion excl = new Exclusion();
excl.codenamebase = meta.getValue("codenamebase");
excl.prefix = Boolean.valueOf(meta.getValue("prefix")).booleanValue();
currentGroup.exclusions.add(excl);
}
public void handle_description(final String data, final Attributes meta) throws SAXException {
currentGroup.description = data;
}
public void start_transformations(final Attributes meta) throws SAXException {
if (!"1.0".equals(meta.getValue("version"))) throw new SAXException("Unsupported DTD");
// do nothing
}
public void end_transformations() throws SAXException {
// do nothing
}
public void start_results(final Attributes meta) throws SAXException {
// do nothing
}
public void end_results() throws SAXException {
// do nothing
}
}
private static final class Parser implements ContentHandler, ErrorHandler, EntityResolver {
private java.lang.StringBuffer buffer;
private Handler handler;
private java.util.Stack<Object[]> context;
public Parser(final Handler handler) {
this.handler = handler;
buffer = new StringBuffer(111);
context = new java.util.Stack<Object[]>();
}
public final void setDocumentLocator(Locator locator) {
}
public final void startDocument() throws SAXException {
}
public final void endDocument() throws SAXException {
}
public final void startElement(java.lang.String ns, java.lang.String name, java.lang.String qname, Attributes attrs) throws SAXException {
dispatch(true);
context.push(new Object[] {qname, new org.xml.sax.helpers.AttributesImpl(attrs)});
if ("trigger-dependency".equals(qname)) {
handler.start_trigger(attrs);
} else if ("transformation".equals(qname)) {
handler.start_transformation(attrs);
} else if ("module-dependency".equals(qname)) {
handler.handle_module_dependency(attrs);
} else if ("transformationgroup".equals(qname)) {
handler.start_transformationgroup(attrs);
} else if ("result".equals(qname)) {
handler.start_result(attrs);
} else if ("exclusion".equals(qname)) {
handler.handle_exclusion(attrs);
} else if ("token-dependency".equals(qname)) {
handler.handle_token_dependency(attrs);
} else if ("package-dependency".equals(qname)) {
handler.handle_package_dependency(attrs);
} else if ("transformations".equals(qname)) {
handler.start_transformations(attrs);
} else if ("implies".equals(qname)) {
handler.start_results(attrs);
}
}
public final void endElement(java.lang.String ns, java.lang.String name, java.lang.String qname) throws SAXException {
dispatch(false);
context.pop();
if ("trigger-dependency".equals(qname)) {
handler.end_trigger();
} else if ("transformation".equals(qname)) {
handler.end_transformation();
} else if ("transformationgroup".equals(qname)) {
handler.end_transformationgroup();
} else if ("result".equals(qname)) {
handler.end_result();
} else if ("transformations".equals(qname)) {
handler.end_transformations();
} else if ("implies".equals(qname)) {
handler.end_results();
}
}
public final void characters(char[] chars, int start, int len) throws SAXException {
buffer.append(chars, start, len);
}
public final void ignorableWhitespace(char[] chars, int start, int len) throws SAXException {
}
public final void processingInstruction(java.lang.String target, java.lang.String data) throws SAXException {
}
public final void startPrefixMapping(final java.lang.String prefix, final java.lang.String uri) throws SAXException {
}
public final void endPrefixMapping(final java.lang.String prefix) throws SAXException {
}
public final void skippedEntity(java.lang.String name) throws SAXException {
}
private void dispatch(final boolean fireOnlyIfMixed) throws SAXException {
if (fireOnlyIfMixed && buffer.length() == 0) return; //skip it
Object[] ctx = context.peek();
String here = (String) ctx[0];
Attributes attrs = (Attributes) ctx[1];
if ("description".equals(here)) {
if (fireOnlyIfMixed) throw new IllegalStateException("Unexpected characters() event! (Missing DTD?)");
handler.handle_description(buffer.length() == 0 ? null : buffer.toString(), attrs);
} else {
//do not care
}
buffer.delete(0, buffer.length());
}
/**
* The recognizer entry method taking an InputSource.
* @param input InputSource to be parsed.
* @throws IOException on I/O error.
* @throws SAXException propagated exception thrown by a DocumentHandler.
*/
public void parse(final InputSource input) throws SAXException, IOException {
XMLReader parser = XMLUtil.createXMLReader(false, false); // fastest mode
parser.setContentHandler(this);
parser.setErrorHandler(this);
parser.setEntityResolver(this);
parser.parse(input);
}
public void error(SAXParseException ex) throws SAXException {
//if (context.isEmpty()) System.err.println("Missing DOCTYPE.");
throw ex;
}
public void fatalError(SAXParseException ex) throws SAXException {
throw ex;
}
public void warning(SAXParseException ex) throws SAXException {
// ignore
}
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
// Not validating, so skip DTD.
return new InputSource(new ByteArrayInputStream(new byte[0]));
}
}
}