All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xtce.toolkit.XTCEDatabaseCompressor Maven / Gradle / Ivy

Go to download

This project contains software to support the Object Management Group (OMG) Space Domain Task Force (SDTF) maintained XML Telemetry and Command Exchange (XTCE) specification.

There is a newer version: 1.1.6
Show newest version
/* Copyright 2017 David Overeem ([email protected])
 * 
 * Licensed 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.xtce.toolkit;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/** Class to implement the capability to compress the XML in an XTCE document
 * considering the schema features to reduce document size.
 *
 * This compression is still under construction.
 *
 * Options that may be used in this class are provided in the constructor using
 * the Properties argument.  All keys are expected to have a boolean value.
 * Any missing keys are assumed to be false/unselected.  The list of keys is:
 *
 * BASIC_DEFAULTS
 * IDENTITY_CALIBRATORS
 * UNNEEDED_LISTS
 * ENUM_2_BOOL
 * RAW_VALID_RANGES
 * ADVANCED_DEFAULTS
 * DUPLICATE_TYPES
 * START_BITS
 * XTCE_PREFIX
 *
 * @author dovereem
 *
 */

public class XTCEDatabaseCompressor extends XTCEDatabaseParser {

    /** Constructor
     *
     * Create and initialize an instance of the XTCE Database Compressor for
     * XML document compression.  The provided file is loaded and if
     * successful, the user can begin the conversion with the compress()
     * method, or select specific step methods.
     *
     * @param file File containing the XTCE database XML file to load and use
     * for the conversion operation.
     *
     * @param options Properties containing the compression options.
     *
     * @throws XTCEDatabaseException thrown in the event that the file cannot
     * be suitably loaded for conversion.
     *
     */

    public XTCEDatabaseCompressor( final File file, final Properties options )
        throws XTCEDatabaseException {

        messages_ = new ArrayList<>();
        options_  = options;

        setFilename( file );

        loadDatabase( getFilename(),
                      false, // do not validate on load
                      getOption( "XINCLUDE" ), // NOI18N
                      false ); // editable - loads the Document Object Model

    }

    /** Retrieve the messages that were generated, if any, during the file
     * compression activity.
     *
     * @return List of String objects containing the messages.  This return
     * will never be null, but it can be an empty list.
     *
     */

    public List getMessages() {
        return messages_;
    }

    /** Perform all of the compression steps except saving the result.
     * 
     * @return long containing the number of elements/attributes that were
     * modified during the compression operation.  This is not particularly
     * useful to know, but might be of interest to user display messages.
     *
     */

    public long compress() {

        long numberConverted = 0;

        numberConverted += compressEncodingAttributes();
        numberConverted += compressCalibrationTestAttributes();
        numberConverted += compressParameterPropertiesAttributes();
        numberConverted += compressListsAndSets();
        numberConverted += compressIdentityCalibrators();
        numberConverted += compressSimpleEnumerations();
        numberConverted += compressValidRangeTruisms();
        numberConverted += compressDuplicateTypes();
        numberConverted += compressContainerStartBits();
        //numberConverted += compressEmptyUnitSets();
        numberConverted += compressAdditionalAttributes();

        messages_.add( Long.toString( numberConverted ) +
                       " " + // NOI18N
                       XTCEFunctions.getText( "file_upgrade_count" ) ); // NOI18N

        return numberConverted;

    }

    /** Compress Raw Data Encoding attributes.
     *
     * bitOrder="mostSignificantBitFirst"
     * abstract="false"
     *
     * @return long containing the number of encoding attributes that were
     * changed on DataEncoding elements.
     *
     */

    public long compressEncodingAttributes() {

        long numberConverted = 0;

        if ( getOption( "BASIC_DEFAULTS" ) == false ) { // NOI18N
            return numberConverted;
        }

        try {

            String[] queries = { "//*[@bitOrder = 'mostSignificantBitFirst']/@bitOrder", // NOI18N
                                 "//*[@abstract = 'false']/@abstract" }; // NOI18N

            for ( String query : queries ) {

                NodeList nodes = evaluateXPathQuery( query );

                for ( int iii = 0; iii < nodes.getLength(); ++iii ) {

                    Attr    attrNode = (Attr)( nodes.item( iii ) );
                    Element element  = attrNode.getOwnerElement();

                    element.removeAttributeNode( attrNode );
                    ++numberConverted;

                }

            }

        } catch ( Exception ex ) {
            //ex.printStackTrace();
            messages_.add( XTCEFunctions.getText( "general_error_caps" ) + // NOI18N
                           " " + // NOI18N
                           ex.getLocalizedMessage() );
        }

        //System.out.println( "enc count = " + Long.toString( numberConverted ) );
        return numberConverted;

    }

    /** Compress ParameterProperties attributes and remove the element if there
     * is no additional content.
     *
     * dataSource="telemetered"
     * readOnly="false"
     *
     * @return long containing the number of ParameterProperties elements that
     * were modified or removed entirely.
     *
     */

    public long compressParameterPropertiesAttributes() {

        long numberConverted = 0;

        if ( getOption( "BASIC_DEFAULTS" ) == false ) { // NOI18N
            return numberConverted;
        }

        try {

            NodeList nodes =
                evaluateXPathQuery( "//xtce:ParameterProperties" ); // NOI18N

            for ( int iii = 0; iii < nodes.getLength(); ++iii ) {

                Element element  = (Element)( nodes.item( iii ) );
                boolean modified = false;

                if ( removeDefaultAttribute( element, "dataSource", "telemetered" ) ) { // NOI18N
                    modified = true;
                }

                if ( removeDefaultAttribute( element, "readOnly", "false" ) ) { // NOI18N
                    modified = true;
                }

                if ( element.getChildNodes().getLength() == 0 ) {
                    element.getParentNode().removeChild( element );
                    modified = true;
                }

                if ( modified == true ) {
                    ++numberConverted;
                }

            }

        } catch ( Exception ex ) {
            //ex.printStackTrace();
            messages_.add( XTCEFunctions.getText( "general_error_caps" ) + // NOI18N
                           " " + // NOI18N
                           ex.getLocalizedMessage() );
        }

        //System.out.println( "pprop count = " + Long.toString( numberConverted ) );
        return numberConverted;

    }

    /** Compress calibration test attributes used in several elements.
     *
     * useCalibratedValue="true"
     * comparisonOperator="=="
     * instance="0"
     *
     * @return long containing the number of removed attributes.
     *
     */

    public long compressCalibrationTestAttributes() {

        long numberConverted = 0;

        if ( getOption( "BASIC_DEFAULTS" ) == false ) { // NOI18N
            return numberConverted;
        }

        try {

            String[] queries = { "//*[@useCalibratedValue = 'true']/@useCalibratedValue", // NOI18N
                                 "//*[@comparisonOperator = '==']/@comparisonOperator", // NOI18N
                                 "//*[@instance = '0']/@instance" }; // NOI18N

            for ( String query : queries ) {

                NodeList nodes = evaluateXPathQuery( query );

                for ( int iii = 0; iii < nodes.getLength(); ++iii ) {
                    Attr    attrNode = (Attr)( nodes.item( iii ) );
                    Element parent   = attrNode.getOwnerElement();
                    parent.removeAttributeNode( attrNode );
                    ++numberConverted;
                }

            }

        } catch ( Exception ex ) {
            //ex.printStackTrace();
            messages_.add( XTCEFunctions.getText( "general_error_caps" ) + // NOI18N
                           " " + // NOI18N
                           ex.getLocalizedMessage() );
        }

        //System.out.println( "caltest count = " + Long.toString( numberConverted ) );
        return numberConverted;

    }

    /** Remove List and Set element variants that are unnecessary because there
     * is only 1 child element and that element is valid outside the grouping.
     *
     * @return long containing the number of List or Set elements removed.
     *
     */

    public long compressListsAndSets() {

        long numberConverted = 0;

        if ( getOption( "UNNEEDED_LISTS" ) == false ) { // NOI18N
            return numberConverted;
        }

        NodeList nodes;

        try {

            nodes = evaluateXPathQuery( "//xtce:ComparisonList" ); // NOI18N

            for ( int iii = 0; iii < nodes.getLength(); ++iii ) {

                Node     compare  = null;
                int      count    = 0;
                NodeList children = nodes.item( iii ).getChildNodes();

                for ( int jjj = 0; jjj < children.getLength(); ++jjj ) {

                    if ( children.item( jjj ).getNodeType() == Node.ELEMENT_NODE ) {
                        if ( compare == null ) {
                            compare = children.item( jjj );
                        }
                        ++count;
                    }

                }

                if ( count == 1 ) {
                    nodes.item( iii )
                         .getParentNode()
                         .replaceChild( compare, nodes.item( iii ) );
                    ++numberConverted;
                }

            }

        } catch ( Exception ex ) {
            //ex.printStackTrace();
            messages_.add( XTCEFunctions.getText( "general_error_caps" ) + // NOI18N
                           " " + // NOI18N
                           ex.getLocalizedMessage() );
        }

        //System.out.println( "list count = " + Long.toString( numberConverted ) );
        return numberConverted;

    }

    /** Remove identity calibrators that have no effect on the calibrated
     * value.
     *
     * This does not remove identity calibrators that are in the context
     * matching feature because those may be used to undo another context.
     *
     * @return long containing the number of calibrator elements removed.
     *
     */

    public long compressIdentityCalibrators() {

        long numberConverted = 0;

        if ( getOption( "IDENTITY_CALIBRATORS" ) == false ) { // NOI18N
            return numberConverted;
        }

        NodeList nodes;

        try {

            nodes = evaluateXPathQuery( "//xtce:DefaultCalibrator/xtce:PolynomialCalibrator" ); // NOI18N

            for ( int iii = 0; iii < nodes.getLength(); ++iii ) {

                double   coeff0   = 0.0;
                double   coeff1   = 1.0;
                boolean  highterm = false;
                NodeList children = nodes.item( iii ).getChildNodes();

                for ( int jjj = 0; jjj < children.getLength(); ++jjj ) {

                    if ( children.item( jjj ).getNodeType() != Node.ELEMENT_NODE ) {
                        continue;
                    }

                    Element termElement = (Element)( children.item( jjj ) );

                    if ( termElement.getAttribute( "exponent" ).equals( "0" ) == true ) { // NOI18N
                        coeff0 = Double.parseDouble( termElement.getAttribute( "coefficient" ) ); // NOI18N
                    } else if ( termElement.getAttribute( "exponent" ).equals( "1" ) == true ) { // NOI18N
                        coeff1 = Double.parseDouble( termElement.getAttribute( "coefficient" ) ); // NOI18N
                    } else {
                        highterm = true;
                        break;
                    }

                }

                if ( ( coeff0 == 0.0 ) && ( coeff1 == 1.0 ) && ( highterm == false ) ) {

                    Node defCalNode   = nodes.item( iii ).getParentNode();
                    Node encodingNode = defCalNode.getParentNode();

                    encodingNode.removeChild( defCalNode );
                    ++numberConverted;

                }

            }

        } catch ( Exception ex ) {
            //ex.printStackTrace();
            messages_.add( XTCEFunctions.getText( "general_error_caps" ) + // NOI18N
                           " " + // NOI18N
                           ex.getLocalizedMessage() );
        }

        //System.out.println( "poly count = " + Long.toString( numberConverted ) );
        return numberConverted;

    }

    /** Compress simple enumeration types to boolean types.
     *
     * @return long containing the number of calibrator elements removed.
     *
     */

    public long compressSimpleEnumerations() {

        long numberConverted = 0;

        if ( getOption( "ENUM_2_BOOL" ) == false ) { // NOI18N
            return numberConverted;
        }

        NodeList nodes;

        try {

            nodes = evaluateXPathQuery( "//xtce:EnumerationList" ); // NOI18N

            for ( int iii = 0; iii < nodes.getLength(); ++iii ) {

                String   zeroString = ""; // NOI18N
                String   oneString  = ""; // NOI18N
                boolean  isSimple   = true;
                NodeList children   = nodes.item( iii ).getChildNodes();

                for ( int jjj = 0; jjj < children.getLength(); ++jjj ) {

                    if ( children.item( jjj ).getNodeType() != Node.ELEMENT_NODE ) {
                        continue;
                    }

                    NamedNodeMap attrs = children.item( jjj ).getAttributes();

                    String num = attrs.getNamedItem( "value" ).getNodeValue(); // NOI18N

                    if ( Double.parseDouble( num ) == 0.0 ) {
                        zeroString = attrs.getNamedItem( "label" ).getNodeValue(); // NOI18N
                    } else if ( Double.parseDouble( num ) == 1.0 ) {
                        oneString = attrs.getNamedItem( "label" ).getNodeValue(); // NOI18N
                    } else {
                        isSimple = false;
                    }

                }

                if ( ( isSimple             == false ) ||
                     ( zeroString.isEmpty() == true  ) ||
                     ( oneString.isEmpty()  == true  ) ) {

                    continue;

                }

                Element typeElement =
                    (Element)( nodes.item( iii ).getParentNode() );

                String elementName = typeElement.getNodeName();

                if ( elementName.contains( "ParameterType" ) == true ) { // NOI18N
                    renameElement( typeElement, "BooleanParameterType" ); // NOI18N
                } else if ( elementName.contains( "ArgumentType" ) == true ) { // NOI18N
                    renameElement( typeElement, "BooleanArgumentType" ); // NOI18N
                } else {
                    continue;
                }

                if ( zeroString.equals( "False" ) == false ) { // NOI18N
                    typeElement.setAttribute( "zeroStringValue", zeroString ); // NOI18N
                }

                if ( oneString.equals( "True" ) == false ) { // NOI18N
                    typeElement.setAttribute( "oneStringValue", oneString ); // NOI18N
                }

                typeElement.removeChild( nodes.item( iii ) );

                ++numberConverted;

            }

        } catch ( Exception ex ) {
            //ex.printStackTrace();
            messages_.add( XTCEFunctions.getText( "general_error_caps" ) + // NOI18N
                           " " + // NOI18N
                           ex.getLocalizedMessage() );
        }

        //System.out.println( "enum count = " + Long.toString( numberConverted ) );
        return numberConverted;

    }

    /** Compress ValidRange specifications that are nothing more than truisms
     * when taking into account the size of the encoding.
     *
     * @return long containing the number ValidRange elements modified or
     * removed.
     *
     */

    public long compressValidRangeTruisms() {

        long numberConverted = 0;

        if ( getOption( "RAW_VALID_RANGES" ) == false ) { // NOI18N
            return numberConverted;
        }

        try {

            messages_.add( XTCEFunctions.getText( "general_warning" ) + // NOI18N
                           ": compressValidRangeTruisms() " + // NOI18N
                           XTCEFunctions.getText( "general_not_implemented" ) ); // NOI18N

        } catch ( Exception ex ) {
            //ex.printStackTrace();
            messages_.add( XTCEFunctions.getText( "general_error_caps" ) + // NOI18N
                           " " + // NOI18N
                           ex.getLocalizedMessage() );
        }

        //System.out.println( "range count = " + Long.toString( numberConverted ) );
        return numberConverted;

    }

    /** Compress Type elements for Parameter and Argument that can be
     * considered duplicate when refactoring is possible.
     *
     * @return long containing the number Types elements removed.
     *
     */

    public long compressDuplicateTypes() {

        long numberConverted = 0;

        if ( getOption( "DUPLICATE_TYPES" ) == false ) { // NOI18N
            return numberConverted;
        }

        try {

            messages_.add( XTCEFunctions.getText( "general_warning" ) + // NOI18N
                           ": compressDuplicateTypes() " + // NOI18N
                           XTCEFunctions.getText( "general_not_implemented" ) ); // NOI18N

        } catch ( Exception ex ) {
            //ex.printStackTrace();
            messages_.add( XTCEFunctions.getText( "general_error_caps" ) + // NOI18N
                           " " + // NOI18N
                           ex.getLocalizedMessage() );
        }

        //System.out.println( "types count = " + Long.toString( numberConverted ) );
        return numberConverted;

    }

    /** Compress location elements in containers where the start bit provided
     * is the same as it would have been using the default of 0 past end of
     * previous item.
     *
     * @return long containing the number location elements removed.
     *
     */

    public long compressContainerStartBits() {

        long numberConverted = 0;

        if ( getOption( "START_BITS" ) == false ) { // NOI18N
            return numberConverted;
        }

        try {

            messages_.add( XTCEFunctions.getText( "general_warning" ) + // NOI18N
                           ": compressContainerStartBits() " + // NOI18N
                           XTCEFunctions.getText( "general_not_implemented" ) ); // NOI18N

        } catch ( Exception ex ) {
            //ex.printStackTrace();
            messages_.add( XTCEFunctions.getText( "general_error_caps" ) + // NOI18N
                           " " + // NOI18N
                           ex.getLocalizedMessage() );
        }

        //System.out.println( "bits count = " + Long.toString( numberConverted ) );
        return numberConverted;

    }

    /** Compress additional attributes that have default values.
     *
     * IntegerDataEncoding/@sizeInBits="8"
     * IntegerDataEncoding/@encoding="unsigned"
     * FloatDataEncoding/@sizeInBits="32"
     * FloatDataEncoding/@encoding="IEEE754_1985"
     * StringDataEncoding/@encoding="UTF-8"
     * IntegerParameterType/@signed="true"
     * IntegerParameterType/@sizeInBits="32"
     * FloatParameterType/@sizeInBits="32"
     * BooleanParameterType/@zeroStringValue="False"
     * BooleanParameterType/@oneStringValue="True"
     *
     * @return long containing the number of removed attributes.
     *
     */

    public long compressAdditionalAttributes() {

        long numberConverted = 0;

        if ( getOption( "ADVANCED_DEFAULTS" ) == false ) { // NOI18N
            return numberConverted;
        }

        try {

            String[] queries = { "//xtce:IntegerDataEncoding[@sizeInBits = '8']/@sizeInBits", // NOI18N
                                 "//xtce:IntegerDataEncoding[@encoding = 'unsigned']/@encoding", // NOI18N
                                 "//xtce:FloatDataEncoding[@sizeInBits = '32']/@sizeInBits", // NOI18N
                                 "//xtce:FloatDataEncoding[@encoding = 'IEEE754_1985']/@encoding", // NOI18N
                                 "//xtce:StringDataEncoding[@encoding = 'UTF-8']/@encoding", // NOI18N
                                 "//xtce:IntegerParameterType[@signed = 'true']/@signed", // NOI18N
                                 "//xtce:IntegerParameterType[@sizeInBits = '32']/@sizeInBits", // NOI18N
                                 "//xtce:FloatParameterType[@sizeInBits = '32']/@sizeInBits", // NOI18N
                                 "//xtce:BooleanParameterType[@zeroStringValue = 'False']/@zeroStringValue", // NOI18N
                                 "//xtce:BooleanParameterType[@oneStringValue = 'True']/@oneStringValue" }; // NOI18N

            for ( String query : queries ) {

                NodeList nodes = evaluateXPathQuery( query );

                for ( int iii = 0; iii < nodes.getLength(); ++iii ) {
                    Attr    attrNode = (Attr)( nodes.item( iii ) );
                    Element parent   = attrNode.getOwnerElement();
                    parent.removeAttributeNode( attrNode );
                    ++numberConverted;
                }

            }

        } catch ( Exception ex ) {
            //ex.printStackTrace();
            messages_.add( XTCEFunctions.getText( "general_error_caps" ) + // NOI18N
                           " " + // NOI18N
                           ex.getLocalizedMessage() );
        }

        //System.out.println( "addldef count = " + Long.toString( numberConverted ) );
        return numberConverted;

    }

    /** Compress the empty UnitSet elements by removing them to reduce size.
     *
     * This method would only be applicable after migration to XTCE 1.2, so for
     * now it is not active here, but instead on the conversion class.
     *
     * @return long containing the number of UnitSet elements that were
     * removed.
     *
     */

    public long compressEmptyUnitSets() {

        long numberConverted = 0;

        if ( getOption( "BASIC_DEFAULTS" ) == false ) {
            return numberConverted;
        }

        try {

            NodeList nodes =
                evaluateXPathQuery( "//xtce:UnitSet" ); // NOI18N

            for ( int iii = 0; iii < nodes.getLength(); ++iii ) {
                if ( nodes.item( iii ).hasChildNodes() == false ) {
                    Node parent = nodes.item( iii ).getParentNode();
                    parent.removeChild( nodes.item( iii ) );
                    // dead text node no longer needs to be removed because
                    // of the normalize method called before saving
                    ++numberConverted;
                }
            }

        } catch ( Exception ex ) {
            //ex.printStackTrace();
            messages_.add( XTCEFunctions.getText( "general_error_caps" ) + // NOI18N
                           " " + // NOI18N
                           ex.getLocalizedMessage() );
        }

        //System.out.println( "unit count = " + Long.toString( numberConverted ) );
        return numberConverted;

    }

    /** Conclude the compression by saving the XML content to an
     * automatically generated filename.
     *
     * The file will be overwritten if it already exists.  The old file name
     * will be used to create the new file name.  First it will try to replace
     * the ".xml" extension with "-NEW.xml".  If that does not succeed, then
     * the file name will receive "-NEW.xml" at the end to make the new file
     * name.
     *
     * @return boolean indicating if the save operation was successful.
     *
     */

    public boolean save() {

        String oldFile = getFilename().getAbsolutePath();
        String newFile = oldFile.replaceFirst( "\\.xml$", "-NEW.xml" ); // NOI18N

        if ( oldFile.equals( newFile ) == true ) {
            newFile = oldFile + "-NEW.xml"; // NOI18N
        }

        return save( new File( newFile ) );

    }

    /** Conclude the compression by saving the XML content to a provided
     * file.
     *
     * The file will be overwritten if it already exists.
     *
     * @param xmlFile File containing the path and name to the target filename
     * to save.
     *
     * @return boolean indicating if the save operation was successful.
     *
     */

    public boolean save( final File xmlFile ) {

        try {

            if ( messages_.isEmpty() == false ) {
                updateHistorySet( messages_ );
            }

            saveDatabase( xmlFile );

        } catch ( Exception ex ) {

            messages_.add( XTCEFunctions.getText( "general_error_caps" ) + // NOI18N
                           " " + // NOI18N
                           ex.getLocalizedMessage() );

            return false;

        }

        setFilename( xmlFile );

        return true;

    }

    /** Retrieve an option from the Properties list of choices.
     *
     * The option may or may not be set by the user.  All options are false by
     * default.
     *
     * @param name String containing the name of the option.
     *
     * @return boolean indicating if this option is selected.
     *
     */

    private boolean getOption( final String name ) {

        try {

            if ( options_.get( name ) == null ) {
                return false;
            } else {
                return (boolean)( options_.get( name ) );
            }

        } catch ( Exception ex ) {
            // do nothing and assume the default return of false
        }

        return false;

    }

    // Private Data Members

    private final List messages_;
    private final Properties   options_;

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy