/* * [Configure.java] * * Summary: Configure HtmlReflow from htmlreflowconfig.xml config file and put configuration in persisted store. * * Copyright: (c) 2006-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 1.0 2010-01-17 initial release. */ package com.mindprod.htmlreflow; import com.mindprod.common18.Misc; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; import java.io.File; import static java.lang.System.*; /** * Configure HtmlReflow from htmlreflowconfig.xml config file and put configuration in persisted store. * * @author Roedy Green, Canadian Mind Products * @version 1.0 2010-01-17 initial release. * @since 2010-01-17 */ class Configure { /** * desired maxLineLength */ private static final int DESIRED_LINE_LENGTH_DEFAULT = 140; /** * default maxLineLength */ private static final int MAX_LINE_LENGTH_DEFAULT = 200; /** * extract an boolean value from the XML attributes, default yes * * @param attributes attributes for the tag * @param attributeName name of the attribute * * @return true or false */ private static boolean extractBooleanAttributeDefaultYes( final NamedNodeMap attributes, String attributeName ) { final Node node = attributes.getNamedItem( attributeName ); if ( node == null ) { // XSD default does not supply value for missing attribute. return true; } return Misc.parseBoolean( node.getNodeValue(), false ); } /** * extract an int value from the XML attributes, default 0 * * @param attributes attributes for the tag * @param attributeName name of the attribute * @param defaultValue default if attribute not specified * * @return numeric value */ private static int extractIntAttribute( final NamedNodeMap attributes, String attributeName, int defaultValue ) { final Node node = attributes.getNamedItem( attributeName ); if ( node == null ) { // XSD default does not supply value for missing attribute. return defaultValue; } final String value = node.getNodeValue(); if ( value == null ) { // XSD default does not supply 0 automatically. return defaultValue; } try { return Integer.parseInt( value ); } catch ( NumberFormatException e ) { return defaultValue; } } /** * extract an enum value from the XML attributes for lineSeparator * * @param attributes attributes for the tag * @param attributeName name of the attribute * * @return numeric value */ private static String extractLineSeparatorAttribute( final NamedNodeMap attributes, String attributeName ) { final Node node = attributes.getNamedItem( attributeName ); if ( node == null ) { // XSD default does not supply value for missing attribute. return System.getProperty( "line.separator" ); } final String value = node.getNodeValue(); if ( value == null || value.equals( "system" ) ) { // XSD default does not supply 0 automatically. return System.getProperty( "line.separator" ); } if ( value.equalsIgnoreCase( "Windows" ) ) { return "\r\n"; } if ( value.equalsIgnoreCase( "Unix" ) ) { return "\n"; } if ( value.equalsIgnoreCase( "Mac" ) ) { return "\r"; } return System.getProperty( "line.separator" ); } /** * extract an enum NLs value from the XML attributes, default 0 * * @param attributes attributes for the tag * @param attributeName name of the attribute * * @return numeric value */ private static int extractNlCountAttribute( final NamedNodeMap attributes, String attributeName ) { final Node node = attributes.getNamedItem( attributeName ); if ( node == null ) { // XSD default does not supply value for missing attribute. return 0; } final String value = node.getNodeValue(); if ( value == null || value.equalsIgnoreCase( "inline" ) ) { return 0; } if ( value.equalsIgnoreCase( "newline" ) ) { return 1; } if ( value.equalsIgnoreCase( "blankline" ) ) { return 2; } return 0; } /** * extract an String value from the XML attributes, default "" * * @param attributes attributes for the tag * @param attributeName name of the attribute * * @return numeric value */ private static String extractStringAttribute( final NamedNodeMap attributes, String attributeName ) { final Node node = attributes.getNamedItem( attributeName ); if ( node == null ) { // XSD default does not supply value for missing attribute. return ""; } final String value = node.getNodeValue(); if ( value == null ) { // XSD default does not supply default automatically. return ""; } return value; } /** * validate htmlreflow.xml configuration file with htmlreflow.xsd schema. * Current directory must contain htmlreflow.xsd and htmlreflow.xml * * @param args not used */ public static void main( String[] args ) { try { // build an XSD-aware SchemaFactory final SchemaFactory schemaFactory = SchemaFactory.newInstance( XMLConstants.W3C_XML_SCHEMA_NS_URI ); // hook up mindless org.xml.sax.ErrorHandler implementation. schemaFactory.setErrorHandler( new XSDErrorHandler() ); // get the custom xsd schema describing the required format for a htmlreflow.xml files. final Schema schemaXSD = schemaFactory.newSchema( new File( "htmlreflow.xsd" ) ); // Create a Validator capable of validating config files according to our custom schema. final Validator validator = schemaXSD.newValidator(); // Create a generic XML parser. final DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder(); // parse the htmlreflow.xml file as ordinary XML and get a DOM tree representation. final Document document = parser.parse( "htmlreflowconfig.xml" ); final DOMSource domSource = new DOMSource( document ); // Validate the htmlreflow.xml DOM tree against the stricter XSD schema validator.validate( domSource ); // Extract information from the htmlreflow.xml configuration document tree final Node lineTag = document.getElementsByTagName( "line" ).item( 0 ); final NamedNodeMap lineAttributes = lineTag.getAttributes(); final int desiredLineLength = extractIntAttribute( lineAttributes, "desiredlinelength", DESIRED_LINE_LENGTH_DEFAULT ); final int maxLineLength = extractIntAttribute( lineAttributes, "maxlinelength", MAX_LINE_LENGTH_DEFAULT ); final String lineSeparator = extractLineSeparatorAttribute( lineAttributes, "lineseparator" ); final NodeList tagNodes = document.getElementsByTagName( "tag" ); final int count = tagNodes.getLength(); TagConfig[] tags = new TagConfig[ count ]; for ( int i = 0; i < count; i++ ) { final NamedNodeMap tagAttributes = tagNodes.item( i ).getAttributes(); final String tagName = extractStringAttribute( tagAttributes, "tagname" ); final int beforeTag = extractNlCountAttribute( tagAttributes, "beforetag" ); final int tagIndent = extractIntAttribute( tagAttributes, "tagindent", 0 ); final int afterTag = extractNlCountAttribute( tagAttributes, "aftertag" ); final int nestedIndent = extractIntAttribute( tagAttributes, "nestedindent", 0 ); final int beforeSlashTag = extractNlCountAttribute( tagAttributes, "beforeslashtag" ); final int afterSlashTag = extractNlCountAttribute( tagAttributes, "afterslashtag" ); final boolean wrap = extractBooleanAttributeDefaultYes( tagAttributes, "wrap" ); tags[ i ] = new TagConfig( tagName, beforeTag, tagIndent, afterTag, nestedIndent, beforeSlashTag, afterSlashTag, wrap ); } // Persist configuration to the Preferences API Persist.setDesiredLineLength( desiredLineLength ); Persist.setMaxLineLength( maxLineLength ); Persist.setLineSeparator( lineSeparator ); Persist.setTags( tags ); Persist.saveConfig(); } catch ( Exception e ) { err.println(); e.printStackTrace( err ); err.println(); } } // end main } /** * error handler for Parsing XML config file */ class XSDErrorHandler implements ErrorHandler { /** * default constructor */ public XSDErrorHandler() { } /** * Receive notification of a recoverable error. *

*

This corresponds to the definition of "error" in section 1.2 * of the W3C XML 1.0 Recommendation. For example, a validating * parser would use this callback to report the violation of a * validity constraint. The default behaviour is to take no * action.

*

*

The SAX parser must continue to provide normal parsing * events after invoking this method: it should still be possible * for the application to process the document through to the end. * If the application cannot do so, then the parser should report * a fatal error even if the XML recommendation does not require * it to do so.

*

*

Filters may use this method to report other, non-XML errors * as well.

* * @param exception The error information encapsulated in a * SAX parse exception. * * @throws org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. * @see org.xml.sax.SAXParseException */ public void error( SAXParseException exception ) throws SAXException { err.println( exception ); } /** * Receive notification of a non-recoverable error. *

*

There is an apparent contradiction between the * documentation for this method and the documentation for {@link * org.xml.sax.ContentHandler#endDocument}. Until this ambiguity * is resolved in a future major release, clients should make no * assumptions about whether endDocument() will or will not be * invoked when the parser has reported a fatalError() or thrown * an exception.

*

*

This corresponds to the definition of "fatal error" in * section 1.2 of the W3C XML 1.0 Recommendation. For example, a * parser would use this callback to report the violation of a * well-formedness constraint.

*

*

The application must assume that the document is unusable * after the parser has invoked this method, and should continue * (if at all) only for the sake of collecting additional error * messages: in fact, SAX parsers are free to stop reporting any * other events once this method has been invoked.

* * @param exception The error information encapsulated in a * SAX parse exception. * * @throws org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. * @see org.xml.sax.SAXParseException */ public void fatalError( SAXParseException exception ) throws SAXException { err.println( exception ); } /** * Receive notification of a warning. *

*

SAX parsers will use this method to report conditions that * are not errors or fatal errors as defined by the XML * recommendation. The default behaviour is to take no * action.

*

*

The SAX parser must continue to provide normal parsing events * after invoking this method: it should still be possible for the * application to process the document through to the end.

*

*

Filters may use this method to report other, non-XML warnings * as well.

* * @param exception The warning information encapsulated in a * SAX parse exception. * * @throws org.xml.sax.SAXException Any SAX exception, possibly * wrapping another exception. * @see org.xml.sax.SAXParseException */ public void warning( SAXParseException exception ) throws SAXException { err.println( exception ); } }