net.sf.jasperreports.engine.JRPropertiesMap.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.jasperreports.engine.JRPropertiesMap.java

Source

/*
 * JasperReports - Free Java Reporting Library.
 * Copyright (C) 2001 - 2019 TIBCO Software Inc. All rights reserved.
 * http://www.jaspersoft.com
 *
 * Unless you have purchased a commercial license agreement from Jaspersoft,
 * the following license terms apply:
 *
 * This program is part of JasperReports.
 *
 * JasperReports is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * JasperReports 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.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with JasperReports. If not, see <http://www.gnu.org/licenses/>.
 */
package net.sf.jasperreports.engine;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import net.sf.jasperreports.engine.design.events.JRPropertyChangeSupport;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Properties map of an JR element.
 * <p/>
 * The order of the properties (obtained by {@link #getPropertyNames() getPropertyNames()}
 * is the same as the order in which the properties were added.
 * 
 * @author Lucian Chirita (lucianc@users.sourceforge.net)
 */
public class JRPropertiesMap implements Serializable, Cloneable {
    private static final long serialVersionUID = JRConstants.SERIAL_VERSION_UID;

    private static final Log log = LogFactory.getLog(JRPropertiesMap.class);

    /**
     * @deprecated no longer used, {@link #setProperty(String, String)} now uses 
     * the actual property name for the change event
     */
    @Deprecated
    public static final String PROPERTY_VALUE = "value";

    private Map<String, String> propertiesMap;
    private List<String> propertiesList;

    private JRPropertiesMap base;

    /**
     * Creates a properties map.
     */
    public JRPropertiesMap() {
    }

    /**
     * Clones a properties map.
     * 
     * @param propertiesMap the original properties map
     */
    public JRPropertiesMap(JRPropertiesMap propertiesMap) {
        this();

        this.base = propertiesMap.base;

        //this copies all properties from base to this instance
        //FIXME in some cases we might want to keep the properties in base
        String[] propertyNames = propertiesMap.getPropertyNames();
        if (propertyNames != null && propertyNames.length > 0) {
            for (int i = 0; i < propertyNames.length; i++) {
                setProperty(propertyNames[i], propertiesMap.getProperty(propertyNames[i]));
            }
        }
    }

    protected synchronized void ensureInit() {
        if (propertiesMap == null) {
            init();
        }
    }

    private void init() {
        // start with small collections
        propertiesMap = new HashMap<String, String>(4, 0.75f);
        propertiesList = new ArrayList<String>(2);
    }

    /**
     * Returns the names of the properties.
     *  
     * @return the names of the properties
     */
    public String[] getPropertyNames() {
        String[] names;
        if (hasOwnProperties()) {
            if (base == null) {
                names = propertiesList.toArray(new String[propertiesList.size()]);
            } else {
                LinkedHashSet<String> namesSet = new LinkedHashSet<String>();
                collectPropertyNames(namesSet);
                names = namesSet.toArray(new String[namesSet.size()]);
            }
        } else if (base != null) {
            names = base.getPropertyNames();
        } else {
            names = new String[0];
        }
        return names;
    }

    public String[] getOwnPropertyNames() {
        String[] names;
        if (hasOwnProperties()) {
            names = propertiesList.toArray(new String[propertiesList.size()]);
        } else {
            names = new String[0];
        }
        return names;
    }

    protected void collectPropertyNames(Collection<String> names) {
        if (base != null) {
            base.collectPropertyNames(names);
        }

        if (propertiesList != null) {
            names.addAll(propertiesList);
        }
    }

    /**
     * Returns the value of a property.
     * 
     * @param propName the name of the property
     * @return the value
     */
    public String getProperty(String propName) {
        String val;
        if (hasOwnProperty(propName)) {
            val = getOwnProperty(propName);
        } else if (base != null) {
            val = base.getProperty(propName);
        } else {
            val = null;
        }
        return val;
    }

    /**
     * Decides whether the map contains a specified property.
     * 
     * The method returns true even if the property value is null.
     * 
     * @param propName the property name
     * @return <code>true</code> if and only if the map contains the property
     */
    public boolean containsProperty(String propName) {
        return hasOwnProperty(propName) || base != null && base.containsProperty(propName);
    }

    protected boolean hasOwnProperty(String propName) {
        return propertiesMap != null && propertiesMap.containsKey(propName);
    }

    protected String getOwnProperty(String propName) {
        return propertiesMap != null ? (String) propertiesMap.get(propName) : null;
    }

    /**
     * Adds/sets a property value.
     * 
     * @param propName the name of the property
     * @param value the value of the property
     */
    public void setProperty(String propName, String value) {
        Object old = getOwnProperty(propName);

        ensureInit();

        if (!hasOwnProperty(propName)) {
            propertiesList.add(propName);
        }
        propertiesMap.put(propName, value);

        if (hasEventSupport()) {
            getEventSupport().firePropertyChange(propName, old, value);
        }
    }

    /**
     * Removes a property.
     * 
     * @param propName the property name
     */
    public void removeProperty(String propName) {
        //FIXME base properties?
        if (hasOwnProperty(propName)) {
            String old = getOwnProperty(propName);
            propertiesList.remove(propName);
            propertiesMap.remove(propName);

            if (hasEventSupport()) {
                getEventSupport().firePropertyRemove(propName, old);
            }
        }
    }

    /**
     * Clones this property map.
     * 
     * @return a clone of this property map
     */
    public JRPropertiesMap cloneProperties() {
        return new JRPropertiesMap(this);
    }

    @Override
    public Object clone() {
        return this.cloneProperties();
    }

    @Override
    public String toString() {
        return propertiesMap == null ? "" : propertiesMap.toString();
    }

    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();

        if (propertiesList == null && propertiesMap != null)// an instance from an old version has been deserialized
        {
            //recreate the properties list and map
            propertiesList = new ArrayList<String>(propertiesMap.keySet());
            propertiesMap = new HashMap<String, String>(propertiesMap);
        }
    }

    /**
     * Checks whether there are any properties.
     * 
     * @return whether there are any properties
     */
    public boolean hasProperties() {
        return hasOwnProperties() || base != null && base.hasProperties();
    }

    public boolean isEmpty() {
        // only checking base for null and not whether base has any properties
        return !hasOwnProperties() && base == null;
    }

    /**
     * Checks whether this object has properties of its own
     * (i.e. not inherited from the base properties).
     * 
     * @return whether this object has properties of its own
     * @see #setBaseProperties(JRPropertiesMap)
     */
    public boolean hasOwnProperties() {
        return propertiesList != null && !propertiesList.isEmpty();
    }

    /**
     * Clones the properties map of a properties holder.
     * If the holder does not have any properties, null is returned.
     * 
     * @param propertiesHolder the properties holder
     * @return a clone of the holder's properties map, or <code>null</code>
     * if the holder does not have any properties
     */
    public static JRPropertiesMap getPropertiesClone(JRPropertiesHolder propertiesHolder) {
        JRPropertiesMap clone;
        if (propertiesHolder.hasProperties()) {
            clone = propertiesHolder.getPropertiesMap().cloneProperties();
        } else {
            clone = null;
        }
        return clone;
    }

    /**
     * Returns the base properties map, if any.
     * 
     * @return the base properties map
     * @see #setBaseProperties(JRPropertiesMap)
     */
    public JRPropertiesMap getBaseProperties() {
        return base;
    }

    /**
     * Sets the base properties map.
     * 
     * <p>
     * The base properties map are used as base/default properties for this
     * instance.  All of the {@link #containsProperty(String)}, 
     * {@link #getProperty(String)}, {@link #getPropertyNames()} and 
     * {@link #hasProperties()} methods include base properties as well.
     * </p>
     * 
     * @param base the base properties map
     */
    public void setBaseProperties(JRPropertiesMap base) {
        this.base = base;
    }

    /**
     * Loads a properties file from a location.
     * 
     * @param location the properties file URL
     * @return the properties file loaded as a in-memory properties map
     */
    public static JRPropertiesMap loadProperties(URL location) {
        boolean close = true;
        InputStream stream = null;
        try {
            stream = location.openStream();

            Properties props = new Properties();
            props.load(stream);

            close = false;
            stream.close();

            JRPropertiesMap properties = new JRPropertiesMap();
            for (Enumeration<?> names = props.propertyNames(); names.hasMoreElements();) {
                String name = (String) names.nextElement();
                String value = props.getProperty(name);
                properties.setProperty(name, value);
            }
            return properties;
        } catch (IOException e) {
            throw new JRRuntimeException(e);
        } finally {
            if (close && stream != null) {
                try {
                    stream.close();
                } catch (IOException e) {
                    if (log.isWarnEnabled()) {
                        log.warn("Error closing stream for " + location, e);
                    }
                }
            }
        }
    }

    private transient JRPropertyChangeSupport eventSupport;

    protected boolean hasEventSupport() {
        return eventSupport != null;
    }

    public JRPropertyChangeSupport getEventSupport() {
        synchronized (this) {
            if (eventSupport == null) {
                eventSupport = new JRPropertyChangeSupport(this);
            }
        }

        return eventSupport;
    }
}