org.pentaho.reporting.tools.configeditor.model.ConfigDescriptionModel.java Source code

Java tutorial

Introduction

Here is the source code for org.pentaho.reporting.tools.configeditor.model.ConfigDescriptionModel.java

Source

/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2001 - 2013 Object Refinery Ltd, Hitachi Vantara and Contributors..  All rights reserved.
*/

package org.pentaho.reporting.tools.configeditor.model;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.libraries.base.config.Configuration;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
import org.pentaho.reporting.libraries.xmlns.common.AttributeList;
import org.pentaho.reporting.libraries.xmlns.parser.ParseException;
import org.pentaho.reporting.libraries.xmlns.writer.CharacterEntityParser;
import org.pentaho.reporting.libraries.xmlns.writer.DefaultTagDescription;
import org.pentaho.reporting.libraries.xmlns.writer.XmlWriter;
import org.pentaho.reporting.libraries.xmlns.writer.XmlWriterSupport;
import org.pentaho.reporting.tools.configeditor.ConfigEditorBoot;
import org.pentaho.reporting.tools.configeditor.Messages;
import org.pentaho.reporting.tools.configeditor.util.DOMUtilities;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.swing.*;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;

/**
 * This list model implementation collects all config description entries defined in JFreeReport. This model is used to
 * create a configuration key definition; it directly manipulates the metadata for the keys as stored in the
 * config-description.xml file.
 *
 * @author Thomas Morgner
 */
public class ConfigDescriptionModel extends AbstractListModel {
    private static final Log logger = LogFactory.getLog(ConfigDescriptionModel.class);

    /**
     * Compares an config description entry against an other entry. This simple implementation just compares the names of
     * the two entries.
     */
    private static class ConfigEntryComparator implements Comparator<ConfigDescriptionEntry>, Serializable {
        /**
         * DefaultConstructor.
         */
        protected ConfigEntryComparator() {
        }

        /**
         * Compares its two arguments for order.  Returns a negative integer, zero, or a positive integer as the first
         * argument is less than, equal to, or greater than the second.<p>
         *
         * @param o1 the first object to compare
         * @param o2 the second object to compare
         * @return an integer indicating the comparison result.
         */
        public int compare(final ConfigDescriptionEntry o1, final ConfigDescriptionEntry o2) {
            if (o1 == null && o2 == null) {
                return 0;
            }
            if (o1 == null) {
                return -1;
            }
            if (o2 == null) {
                return 1;
            }
            return o1.getKeyName().compareTo(o2.getKeyName());
        }
    }

    /**
     * The content of this list; all config description entries.
     */
    private final ArrayList<ConfigDescriptionEntry> content;

    /**
     * Provides access to externalized strings
     */
    private Messages messages;

    /**
     * Creates a new, initially empty ConfigDescriptionModel.
     */
    public ConfigDescriptionModel() {
        messages = Messages.getInstance();
        content = new ArrayList<ConfigDescriptionEntry>();
    }

    /**
     * Adds the given entry to the end of the list.
     *
     * @param entry the new entry.
     */
    public void add(final ConfigDescriptionEntry entry) {
        if (entry == null) {
            throw new NullPointerException(messages.getString("ConfigDescriptionModel.ERROR_0001_ENTRY_IS_NULL")); //$NON-NLS-1$
        }
        // Only add unique elements ...
        final int index = findEntry(entry.getKeyName());
        if (index == -1) {
            content.add(entry);
            updated();
        } else {
            content.set(index, entry);
        }
    }

    private int findEntry(final String key) {
        for (int i = 0; i < content.size(); i++) {
            final ConfigDescriptionEntry configDescriptionEntry = content.get(i);
            if (key.equals(configDescriptionEntry.getKeyName())) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Removes the given entry from the list.
     *
     * @param entry the entry that should be removed.
     */
    public void remove(final ConfigDescriptionEntry entry) {
        if (entry == null) {
            throw new NullPointerException(messages.getString("ConfigDescriptionModel.ERROR_0002_ENTRY_IS_NULL")); //$NON-NLS-1$
        }

        final int index = findEntry(entry.getKeyName());
        if (index != -1) {
            content.remove(index);
            updated();
        }
    }

    public void removeAll(final int[] indices) {
        if (indices.length == 0) {
            return;
        }

        final Object[] entries = new Object[indices.length];
        for (int i = indices.length - 1; i >= 0; i--) {
            entries[i] = content.get(indices[i]);
        }
        for (int i = 0; i < entries.length; i++) {
            content.remove(entries[i]);
        }
        updated();
    }

    /**
     * Returns the entry stored on the given list position.
     *
     * @param pos the position
     * @return the entry
     * @throws IndexOutOfBoundsException if the position is invalid.
     */
    public ConfigDescriptionEntry get(final int pos) {
        return content.get(pos);
    }

    /**
     * Fires an contents changed event for all elements in the list.
     */
    public void updated() {
        fireContentsChanged(this, 0, getSize());
    }

    /**
     * Returns the index of the given entry or -1, if the entry is not in the list.
     *
     * @param entry the entry whose position should be searched.
     * @return the position of the entry
     */
    public int indexOf(final ConfigDescriptionEntry entry) {
        if (entry == null) {
            throw new NullPointerException(messages.getString("ConfigDescriptionModel.ERROR_0003_ENTRY_IS_NULL")); //$NON-NLS-1$
        }
        return findEntry(entry.getKeyName());
    }

    /**
     * Checks whether the given entry is already contained in this list.
     *
     * @param entry the entry that should be checked.
     * @return true, if the entry is already added, false otherwise.
     */
    public boolean contains(final ConfigDescriptionEntry entry) {
        if (entry == null) {
            throw new NullPointerException(messages.getString("ConfigDescriptionModel.ERROR_0004_ENTRY_IS_NULL")); //$NON-NLS-1$
        }
        return findEntry(entry.getKeyName()) > -1;
    }

    /**
     * Sorts the entries of the list. Be aware that calling this method does not fire an updat event; you have to do this
     * manually.
     */
    public void sort() {
        Collections.sort(content, new ConfigEntryComparator());
        updated();
    }

    /**
     * Returns the contents of this model as object array.
     *
     * @return the contents of the model as array.
     */
    public ConfigDescriptionEntry[] toArray() {
        return content.toArray(new ConfigDescriptionEntry[content.size()]);
    }

    /**
     * Returns the length of the list.
     *
     * @return the length of the list
     */
    public int getSize() {
        return content.size();
    }

    /**
     * Returns the value at the specified index.
     *
     * @param index the requested index
     * @return the value at <code>index</code>
     */
    public Object getElementAt(final int index) {
        final ConfigDescriptionEntry entry = get(index);
        if (entry == null) {
            return null;
        }
        return entry.getKeyName();
    }

    /**
     * Imports all entries from the given report configuration. Only new entries will be added to the list. This does not
     * add report properties supplied via the System.properties.
     *
     * @param config the report configuration from where to add the entries.
     */
    public void importFromConfig(final Configuration config) {
        final Iterator it = config.findPropertyKeys(""); //$NON-NLS-1$
        while (it.hasNext()) {
            final String keyname = (String) it.next();
            if (System.getProperties().containsKey(keyname)) {
                continue;
            }

            final TextConfigDescriptionEntry entry = new TextConfigDescriptionEntry(keyname);
            if (contains(entry) == false) {
                add(entry);
            }
        }
    }

    /**
     * Loads the entries from the given xml file. The file must be in the format of the config-description.xml file.
     *
     * @param in the inputstream from where to read the file
     * @throws IOException                  if an error occured while reading the file
     * @throws SAXException                 if an XML parse error occurs.
     * @throws ParserConfigurationException if the XML parser could not be initialized.
     */
    public void load(final InputStream in) throws IOException, SAXException, ParserConfigurationException {
        content.clear();
        final Document doc = DOMUtilities.parseInputStream(in);
        final Element e = doc.getDocumentElement();
        final NodeList list = e.getElementsByTagName("key"); //$NON-NLS-1$
        for (int i = 0; i < list.getLength(); i++) {
            final Element keyElement = (Element) list.item(i);
            final String keyName = keyElement.getAttribute("name"); //$NON-NLS-1$
            final boolean keyGlobal = "true".equals(keyElement.getAttribute("global")); //$NON-NLS-1$
            final boolean keyHidden = "true".equals(keyElement.getAttribute("hidden")); //$NON-NLS-1$
            final String descr = getDescription(keyElement).trim();

            final NodeList enumNodes = keyElement.getElementsByTagName("enum"); //$NON-NLS-1$
            if (enumNodes.getLength() != 0) {
                final String[] alteratives = collectEnumEntries((Element) enumNodes.item(0));
                final EnumConfigDescriptionEntry en = new EnumConfigDescriptionEntry(keyName);
                en.setDescription(descr);
                en.setGlobal(keyGlobal);
                en.setHidden(keyHidden);
                en.setOptions(alteratives);
                add(en);
                continue;
            }

            final NodeList classNodes = keyElement.getElementsByTagName("class"); //$NON-NLS-1$
            if (classNodes.getLength() != 0) {
                final Element classElement = (Element) classNodes.item(0);
                final String className = classElement.getAttribute("instanceof"); //$NON-NLS-1$
                if (className == null) {
                    throw new ParseException(
                            messages.getString("ConfigDescriptionModel.ERROR_0005_MISSING_INSTANCEOF")); //$NON-NLS-1$
                }
                try {
                    final ClassLoader classLoader = ObjectUtilities.getClassLoader(getClass());
                    final Class baseClass = Class.forName(className, false, classLoader);
                    final ClassConfigDescriptionEntry ce = new ClassConfigDescriptionEntry(keyName);
                    ce.setBaseClass(baseClass);
                    ce.setDescription(descr);
                    ce.setGlobal(keyGlobal);
                    ce.setHidden(keyHidden);
                    add(ce);
                    continue;
                } catch (Exception ex) {
                    final String message = messages
                            .getString("ConfigDescriptionModel.ERROR_0006_BASE_CLASS_LOAD_FAILED"); //$NON-NLS-1$
                    ConfigDescriptionModel.logger.error(message, ex);
                    continue;
                }
            }

            final NodeList textNodes = keyElement.getElementsByTagName("text"); //$NON-NLS-1$
            if (textNodes.getLength() != 0) {
                final TextConfigDescriptionEntry textEntry = new TextConfigDescriptionEntry(keyName);
                textEntry.setDescription(descr);
                textEntry.setGlobal(keyGlobal);
                textEntry.setHidden(keyHidden);
                add(textEntry);
            }
        }
    }

    /**
     * A parser helper method which collects all enumeration entries from the given element.
     *
     * @param element the element from where to read the enumeration entries.
     * @return the entries as string array.
     */
    private String[] collectEnumEntries(final Element element) {
        final NodeList nl = element.getElementsByTagName("text"); //$NON-NLS-1$
        final String[] retval = new String[nl.getLength()];
        for (int i = 0; i < nl.getLength(); i++) {
            retval[i] = DOMUtilities.getText((Element) nl.item(i)).trim();
        }
        return retval;
    }

    /**
     * A parser helper method that returns the CDATA description of the given element.
     *
     * @param e the element from where to read the description.
     * @return the description text.
     */
    private String getDescription(final Element e) {
        final NodeList descr = e.getElementsByTagName("description"); //$NON-NLS-1$
        if (descr.getLength() == 0) {
            return ""; //$NON-NLS-1$
        }
        return DOMUtilities.getText((Element) descr.item(0));
    }

    /**
     * Saves the model into an xml file.
     *
     * @param out      the target output stream.
     * @param encoding the encoding of the content.
     * @throws IOException if an error occurs.
     * @noinspection IOResourceOpenedButNotSafelyClosed
     */
    public void save(final OutputStream out, final String encoding) throws IOException {
        // This print-writer will be flushed, but not closed, as closing the underlying stream is not desired here.
        final PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, encoding));
        final AttributeList attList = new AttributeList();
        attList.addNamespaceDeclaration("", ConfigEditorBoot.NAMESPACE); //$NON-NLS-1$

        final DefaultTagDescription tagDescription = new DefaultTagDescription();
        tagDescription.setDefaultNamespace(ConfigEditorBoot.NAMESPACE);
        tagDescription.setNamespaceHasCData(ConfigEditorBoot.NAMESPACE, false);
        tagDescription.setElementHasCData(ConfigEditorBoot.NAMESPACE, "text", true);
        tagDescription.setElementHasCData(ConfigEditorBoot.NAMESPACE, "description", true);

        final XmlWriter dwriter = new XmlWriter(writer, tagDescription);

        dwriter.writeXmlDeclaration(encoding);
        dwriter.writeTag(ConfigEditorBoot.NAMESPACE, "config-description", attList, XmlWriterSupport.OPEN); //$NON-NLS-1$

        final CharacterEntityParser parser = CharacterEntityParser.createXMLEntityParser();
        for (int i = 0; i < getSize(); i++) {
            final ConfigDescriptionEntry entry = get(i);
            final AttributeList p = new AttributeList();
            p.setAttribute(ConfigEditorBoot.NAMESPACE, "name", entry.getKeyName()); //$NON-NLS-1$
            p.setAttribute(ConfigEditorBoot.NAMESPACE, "global", String.valueOf(entry.isGlobal())); //$NON-NLS-1$
            p.setAttribute(ConfigEditorBoot.NAMESPACE, "hidden", String.valueOf(entry.isHidden())); //$NON-NLS-1$
            dwriter.writeTag(ConfigEditorBoot.NAMESPACE, "key", p, XmlWriterSupport.OPEN); //$NON-NLS-1$
            if (entry.getDescription() != null) {
                dwriter.writeTag(ConfigEditorBoot.NAMESPACE, "description", XmlWriterSupport.OPEN); //$NON-NLS-1$
                writer.write(parser.encodeEntities(entry.getDescription()));
                dwriter.writeCloseTag();
            }
            if (entry instanceof ClassConfigDescriptionEntry) {
                final ClassConfigDescriptionEntry ce = (ClassConfigDescriptionEntry) entry;
                if (ce.getBaseClass() != null) {
                    dwriter.writeTag(ConfigEditorBoot.NAMESPACE, "class", "instanceof", //$NON-NLS-1$ //$NON-NLS-2$
                            ce.getBaseClass().getName(), XmlWriterSupport.CLOSE);
                } else {
                    dwriter.writeTag(ConfigEditorBoot.NAMESPACE, "class", "instanceof", //$NON-NLS-1$ //$NON-NLS-2$
                            "java.lang.Object", XmlWriterSupport.CLOSE); //$NON-NLS-1$
                }
            } else if (entry instanceof TextConfigDescriptionEntry) {
                dwriter.writeTag(ConfigEditorBoot.NAMESPACE, "text", //$NON-NLS-1$
                        new AttributeList(), XmlWriterSupport.CLOSE);
            } else if (entry instanceof EnumConfigDescriptionEntry) {
                final EnumConfigDescriptionEntry en = (EnumConfigDescriptionEntry) entry;
                dwriter.writeTag(ConfigEditorBoot.NAMESPACE, "enum", XmlWriterSupport.OPEN); //$NON-NLS-1$

                final String[] alts = en.getOptions();
                if (alts != null) {
                    for (int optCount = 0; optCount < alts.length; optCount++) {
                        dwriter.writeTag(ConfigEditorBoot.NAMESPACE, "text", XmlWriterSupport.OPEN); //$NON-NLS-1$
                        dwriter.writeTextNormalized(alts[optCount], false);
                        dwriter.writeCloseTag();
                    }
                }
                dwriter.writeCloseTag();
            }
            dwriter.writeCloseTag();
        }
        dwriter.writeCloseTag();
        writer.flush();
    }

}