de.akra.idocit.core.services.impl.EclipsePersistenceService.java Source code

Java tutorial

Introduction

Here is the source code for de.akra.idocit.core.services.impl.EclipsePersistenceService.java

Source

/*******************************************************************************
 * Copyright 2011, 2012 AKRA GmbH
 *
 * 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 de.akra.idocit.core.services.impl;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Serializable;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.PropertyResourceBundle;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.eclipse.core.resources.IFile;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.ui.PlatformUI;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.XStreamException;
import com.thoughtworks.xstream.io.xml.CompactWriter;

import de.akra.idocit.common.constants.Misc;
import de.akra.idocit.common.factories.XStreamFactory;
import de.akra.idocit.common.structure.Addressee;
import de.akra.idocit.common.structure.InterfaceArtifact;
import de.akra.idocit.common.structure.ThematicGrid;
import de.akra.idocit.common.structure.ThematicRole;
import de.akra.idocit.common.utils.DescribedItemNameComparator;
import de.akra.idocit.common.utils.StringUtils;
import de.akra.idocit.core.IDocItActivator;
import de.akra.idocit.core.constants.PreferenceStoreConstants;
import de.akra.idocit.core.exceptions.UnitializedIDocItException;
import de.akra.idocit.core.extensions.Parser;
import de.akra.idocit.core.extensions.ValidationReport;
import de.akra.idocit.core.listeners.IConfigurationChangeListener;
import de.akra.idocit.core.services.PersistenceService;
import de.akra.idocit.core.utils.ResourceUtils;

/**
 * Provides services to load and to write an {@link InterfaceArtifact}.
 * 
 * @author Dirk Meier-Eickhoff
 * @since 0.0.1
 * @version 0.0.2
 * 
 */
public class EclipsePersistenceService implements PersistenceService {
    private static final String ERR_MSG_LISTENER_NULL = "Listener must not be null";

    /*
     * Constants
     */
    private static final String XML_ALIAS_ADDRESSEE = "addressee";

    /*
     * Logger.
     */
    private static Logger logger = Logger.getLogger(EclipsePersistenceService.class.getName());

    private boolean isInitialized = false;

    private Set<IConfigurationChangeListener> addresseChangeListeners = new ConcurrentSkipListSet<IConfigurationChangeListener>(
            new ConfigurationChangeListenerComparator());
    private Set<IConfigurationChangeListener> thematicGridChangeListeners = new ConcurrentSkipListSet<IConfigurationChangeListener>(
            new ConfigurationChangeListenerComparator());
    private Set<IConfigurationChangeListener> thematicRoleChangeListeners = new ConcurrentSkipListSet<IConfigurationChangeListener>(
            new ConfigurationChangeListenerComparator());

    @Override
    public void init() {
        isInitialized = true;
        logger.info("The PersistenceService is now initialized.");
    }

    @Override
    public InterfaceArtifact loadInterface(IFile iFile) throws Exception {
        logger.entering(EclipsePersistenceService.class.getName(), "loadInterface");

        // there must be a file extension to determine the type
        if (iFile == null || iFile.getFileExtension().isEmpty()) {
            logger.log(Level.SEVERE, "iFile is not initialized or has no extension."
                    + (iFile != null ? " iFile=" + iFile.getFullPath().toOSString() : ""));
            return InterfaceArtifact.NOT_SUPPORTED_ARTIFACT;
        }

        // get Parser depending on the file extension
        Parser parser = ServiceManager.getInstance().getParsingService().getParser(iFile.getFileExtension());

        if (parser == null) {
            logger.log(Level.INFO, "Not supported type: " + iFile.getFileExtension());
            return InterfaceArtifact.NOT_SUPPORTED_ARTIFACT;
        }

        InterfaceArtifact result = parser.parse(iFile);

        logger.exiting(EclipsePersistenceService.class.getName(), "loadInterface", result);
        return result;
    }

    @Override
    public void writeInterface(InterfaceArtifact interfaceArtifact, IFile iFile) throws Exception {
        if (interfaceArtifact != null && iFile != null) {
            // get Parser depending on the file extension
            Parser parser = ServiceManager.getInstance().getParsingService().getParser(iFile.getFileExtension());
            if (parser != null) {
                parser.write(interfaceArtifact, iFile);
            } else {
                logger.log(Level.SEVERE, "Try to write into a not supported file.");
            }
        } else {
            logger.log(Level.SEVERE, "The input parameters must be initalized. interfaceStructure="
                    + interfaceArtifact + "; iFile=" + iFile);
            throw new IllegalArgumentException("The input parameters must be initalized.");
        }
    }

    /**
     * 
     * @return true, if the {@link IPreferenceStore} of Eclipse is loaded and it contains
     *         a value for {@link PreferenceStoreConstants#ADDRESSEES}.
     */
    @Override
    public boolean areAddresseesInitialized() {
        IPreferenceStore prefStore = PlatformUI.getPreferenceStore();

        return prefStore.contains(PreferenceStoreConstants.ADDRESSEES)
                && !"".equals(prefStore.getString(PreferenceStoreConstants.ADDRESSEES));
    }

    /**
     * 
     * @return true, if the {@link IPreferenceStore} of Eclipse is loaded and it contains
     *         a value for {@link PreferenceStoreConstants#THEMATIC_ROLES}.
     */
    @Override
    public boolean areThematicRolesInitialized() {
        IPreferenceStore prefStore = PlatformUI.getPreferenceStore();

        return prefStore.contains(PreferenceStoreConstants.THEMATIC_ROLES)
                && !"".equals(prefStore.getString(PreferenceStoreConstants.THEMATIC_ROLES));
    }

    private XStream configureXStreamForAddressee() {
        XStream stream = new XStream();
        stream.alias(XML_ALIAS_ADDRESSEE, Addressee.class);
        return stream;
    }

    /**
     * Converts a {@link PropertyResourceBundle} to a Map.
     * 
     * @param resBundle
     *            The {@link PropertyResourceBundle} to convert.
     * @return Map with item name mapping to it's description.
     */
    private Map<String, String> convertResourceBundleToMap(PropertyResourceBundle resBundle) {
        Map<String, String> roles = new HashMap<String, String>();
        Enumeration<String> keys = resBundle.getKeys();

        while (keys.hasMoreElements()) {
            String role = keys.nextElement();
            String description = resBundle.getString(role);

            roles.put(role, description);
        }

        return roles;
    }

    /**
     * Reads the in the plugin stored thematic roles.
     * 
     * @return Map of thematic role names mapping to their description.
     */
    private Map<String, String> getInitialThematicRoles() {
        return convertResourceBundleToMap(IDocItActivator.getDefault().getThematicRoleResourceBundle());
    }

    @Override
    public void persistAddressees(List<Addressee> addressees) {
        logger.fine("persist Addresses");
        // TODO delete old entries from preference store
        final IPreferenceStore prefStore = PlatformUI.getPreferenceStore();
        final XStream stream = configureXStreamForAddressee();

        for (final Addressee a : addressees) {
            a.setDescription(StringUtils.removeLineBreaks(a.getDescription()));
        }

        final String addresseeXML = stream.toXML(addressees);
        prefStore.putValue(PreferenceStoreConstants.ADDRESSEES, addresseeXML);

        logger.fine("notify addressee change listeners");
        notifyListeners(this.addresseChangeListeners);
    }

    @Override
    public void persistThematicRoles(List<ThematicRole> roles) {
        logger.fine("persist ThematicRoles");
        // TODO delete old entries from preference store
        final IPreferenceStore prefStore = PlatformUI.getPreferenceStore();
        final XStream stream = XStreamFactory.configureXStreamForThematicRoles();
        Collections.sort(roles, DescribedItemNameComparator.getInstance());

        for (final ThematicRole role : roles) {
            if (role.getDescription() != null) {
                role.setDescription(StringUtils.removeLineBreaks(role.getDescription()));
            }
        }

        final String rolesXML = stream.toXML(roles);
        prefStore.putValue(PreferenceStoreConstants.THEMATIC_ROLES, rolesXML);

        logger.fine("notify thematic role change listeners");
        notifyListeners(this.thematicRoleChangeListeners);
    }

    @Override
    public List<ThematicRole> readInitialThematicRoles() {
        List<ThematicRole> roles = new ArrayList<ThematicRole>();
        Map<String, String> initialRoles = getInitialThematicRoles();

        for (Entry<String, String> entry : initialRoles.entrySet()) {
            ThematicRole role = new ThematicRole(entry.getKey());
            role.setDescription(entry.getValue());

            roles.add(role);
        }

        Collections.sort(roles, DescribedItemNameComparator.getInstance());

        return roles;
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Addressee> readInitialAddressees() {
        XStream stream = XStreamFactory.configureXStreamForAddressee();
        List<Addressee> addressees = new ArrayList<Addressee>();

        try {
            addressees = (List<Addressee>) stream
                    .fromXML(ResourceUtils.getResourceInputStream(ResourceUtils.ADDRESSEE_RESOURCE_FILE));
        } catch (XStreamException e) {
            logger.log(Level.WARNING, "No addressees were loaded.", e);
        }

        return addressees;
    }

    /**
     * Removes the formatting characters from the given addressees.
     * 
     * @param grids
     *            The addressees to cleanup
     * @return The cleaned addressees
     */
    private static List<Addressee> removeFormattingCharsAddressee(List<Addressee> addressees) {
        for (Addressee addressee : addressees) {
            addressee.setDescription(StringUtils.cleanFormatting(addressee.getDescription()));
            addressee.setName(StringUtils.cleanFormatting(addressee.getName()));
        }

        return addressees;
    }

    /**
     * Load the {@link Addressee}s that are configured in the Eclipse preference pages for
     * iDocIt!.
     * 
     * @return List of {@link Addressee}s.
     */
    @Override
    @SuppressWarnings("unchecked")
    public List<Addressee> loadConfiguredAddressees() {
        IPreferenceStore prefStore = PlatformUI.getPreferenceStore();
        XStream stream = configureXStreamForAddressee();
        List<Addressee> addressees = new ArrayList<Addressee>();

        String prefVal = prefStore.getString(PreferenceStoreConstants.ADDRESSEES);
        if (!prefVal.isEmpty()) {
            try {
                addressees = (List<Addressee>) stream.fromXML(prefVal);
            } catch (XStreamException e) {
                logger.log(Level.WARNING, "No addressees were loaded.", e);
            }
        }

        return removeFormattingCharsAddressee(addressees);
    }

    /**
     * Load the {@link ThematicRole}s that are configured in the Eclipse preference pages
     * for iDocIt!.
     * 
     * @return List of {@link ThematicRole}s.
     */
    @Override
    @SuppressWarnings("unchecked")
    public List<ThematicRole> loadThematicRoles() {
        IPreferenceStore prefStore = PlatformUI.getPreferenceStore();
        XStream stream = XStreamFactory.configureXStreamForThematicRoles();
        List<ThematicRole> roles = new ArrayList<ThematicRole>();

        String prefVal = prefStore.getString(PreferenceStoreConstants.THEMATIC_ROLES);
        if (!prefVal.isEmpty()) {
            try {
                roles = (List<ThematicRole>) stream.fromXML(prefVal);
            } catch (XStreamException e) {
                logger.log(Level.WARNING, "No thematic role were loaded.", e);
            }
        }

        Collections.sort(roles, DescribedItemNameComparator.getInstance());

        return removeFormattingCharsRoles(roles);
    }

    /**
     * Removes the formatting characters from the given thematic roles.
     * 
     * @param grids
     *            The roles to cleanup
     * @return The cleaned roles
     */
    private static List<ThematicRole> removeFormattingCharsRoles(List<ThematicRole> roles) {
        for (ThematicRole role : roles) {
            role.setDescription(StringUtils.cleanFormatting(role.getDescription()));
            role.setName(StringUtils.cleanFormatting(role.getName()));
        }

        return roles;
    }

    /**
     * Removes the formatting characters from the given thematic grids.
     * 
     * @param grids
     *            The grids to cleanup
     * @return The cleaned grids
     */
    private static List<ThematicGrid> removeFormattingCharsThematicGrids(List<ThematicGrid> grids) {
        for (ThematicGrid grid : grids) {
            grid.setDescription(StringUtils.cleanFormatting(grid.getDescription()));
            grid.setName(StringUtils.cleanFormatting(grid.getName()));

            Map<ThematicRole, Boolean> cleanedRoles = new HashMap<ThematicRole, Boolean>();

            for (Entry<ThematicRole, Boolean> roleEntry : grid.getRoles().entrySet()) {
                if (roleEntry.getKey() != null) {
                    ThematicRole cleanedRole = new ThematicRole();
                    cleanedRole.setName(StringUtils.cleanFormatting(roleEntry.getKey().getName()));
                    cleanedRole.setDescription(StringUtils.cleanFormatting(roleEntry.getKey().getDescription()));

                    cleanedRoles.put(cleanedRole, roleEntry.getValue());
                } else {
                    logger.warning("No name for thematic role " + String.valueOf(roleEntry));
                }
            }

            grid.setRoles(cleanedRoles);
        }

        return grids;
    }

    /**
     * Load the {@link ThematicGrid}s that are configured in the Eclipse preference pages
     * for iDocIt!.
     * 
     * @return List of {@link ThematicGrid}s.
     * 
     * @throws UnitializedIDocItException
     *             If the default grids should be loaded, but their input-stream has not
     *             been initialized yet via {@link PersistenceService#init(InputStream)}.
     */
    @Override
    @SuppressWarnings("unchecked")
    public List<ThematicGrid> loadThematicGrids() throws UnitializedIDocItException {
        IPreferenceStore prefStore = PlatformUI.getPreferenceStore();
        String verbClassRoleAssocsXML = prefStore.getString(PreferenceStoreConstants.VERBCLASS_ROLE_MAPPING);

        if ((verbClassRoleAssocsXML != null) && (!"".equals(verbClassRoleAssocsXML.trim()))) {
            try {
                List<ThematicGrid> grids = (List<ThematicGrid>) XStreamFactory.configureXStreamForThematicGrid()
                        .fromXML(verbClassRoleAssocsXML);
                return removeFormattingCharsThematicGrids(grids);
            } catch (XStreamException e) {
                logger.log(Level.SEVERE, e.getMessage());
                throw new RuntimeException(
                        "The preference store of this workspace contains no configured thematic grids, but it is expected to do so! It is recommended to setup a new, consistent workspace.");
            }
        } else {
            if (isInitialized) {
                InputStream defaultThematicGrids = ResourceUtils
                        .getResourceInputStream(ResourceUtils.THEMATIC_GRIDS_RESOURCE_FILE);
                List<ThematicGrid> defaultGrids = (List<ThematicGrid>) XStreamFactory
                        .configureXStreamForThematicGrid().fromXML(defaultThematicGrids);

                // Keep the default grids in preference store for the next time.
                persistThematicGrids(removeFormattingCharsThematicGrids(defaultGrids));

                return defaultGrids;
            } else {
                throw new UnitializedIDocItException("The PersistenceService has not been initialized yet.");
            }
        }
    }

    @Override
    public void persistThematicGrids(List<ThematicGrid> verbClassRoleAssociations) {
        logger.fine("persist ThematicGrids");
        final IPreferenceStore prefStore = PlatformUI.getPreferenceStore();

        final XStream xs = XStreamFactory.configureXStreamForThematicGrid();
        final StringWriter sw = new StringWriter();
        xs.marshal(verbClassRoleAssociations, new CompactWriter(sw));

        final String verbClassRoleAssocsXML = sw.toString();

        prefStore.putValue(PreferenceStoreConstants.VERBCLASS_ROLE_MAPPING, verbClassRoleAssocsXML);

        logger.fine("notify thematic grid change listeners");
        notifyListeners(this.thematicGridChangeListeners);
    }

    @Override
    public void exportThematicGridsAsXml(final File destination, List<ThematicGrid> grids) throws IOException {
        XStream lvXStream = XStreamFactory.configureXStreamForThematicGrid();
        Writer lvWriter = null;

        try {
            lvWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(destination),
                    Charset.forName(Misc.DEFAULT_CHARSET)));
            lvXStream.toXML(grids, lvWriter);
        } finally {
            if (lvWriter != null) {
                lvWriter.close();
            }
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<ThematicGrid> importThematicGrids(final File source) throws IOException {
        Reader reader = null;
        List<ThematicGrid> grids = null;

        try {
            XStream xmlStream = XStreamFactory.configureXStreamForThematicGrid();
            reader = new BufferedReader(
                    new InputStreamReader(new FileInputStream(source), Charset.forName(Misc.DEFAULT_CHARSET)));
            grids = (List<ThematicGrid>) xmlStream.fromXML(reader);
        } finally {
            if (reader != null) {
                reader.close();
            }
        }

        return grids;
    }

    /**
     * Converts the given verbs into an unordered list (HTML). Before generating the
     * HTML-list, the verbs are sorted alphabetically.
     * 
     * @param verbs
     *            The verbs to export
     * @return The HTML-representation of the given verbs
     */
    private String convertVerbListIntoHtml(Set<String> verbs) {
        List<String> sortedVerbs = new ArrayList<String>();
        sortedVerbs.addAll(verbs);
        Collections.sort(sortedVerbs);

        StringBuffer buffer = new StringBuffer("\n\t\t\t\t\t<ul>");

        for (String verb : sortedVerbs) {
            buffer.append("\n\t\t\t\t\t\t<li>" + verb + "</li>");
        }

        buffer.append("\n\t\t\t\t\t</ul>");

        return buffer.toString();
    }

    /**
     * Converts the given map of {@link ThematicRole}s to booleans into an unordered list
     * (HTML). The booleans indicate whether the role is mandatory or optional. Mandatory
     * are printed in bold-font.
     * 
     * @param roles
     *            The {@link ThematicRole}s to export
     * @return The HTML-representation of the given roles
     */
    private String convertRoleMapIntoHtml(Map<ThematicRole, Boolean> roles) {
        List<ThematicRole> sortedVerbs = new ArrayList<ThematicRole>();
        sortedVerbs.addAll(roles.keySet());
        Collections.sort(sortedVerbs);

        StringBuffer buffer = new StringBuffer("\n\t\t\t\t\t<ul>");

        for (ThematicRole role : sortedVerbs) {
            if (roles.get(role).booleanValue()) {
                buffer.append("\n\t\t\t\t\t\t<li><b>" + role.getName() + "</b></li>");
            } else {
                buffer.append("\n\t\t\t\t\t\t<li>" + role.getName() + "</li>");
            }
        }

        buffer.append("\n\t\t\t\t\t</ul>");

        return buffer.toString();
    }

    @Override
    public void exportThematicGridsAsHtml(File destination, List<ThematicGrid> grids) throws IOException {
        BufferedWriter writer = null;

        try {
            writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(destination),
                    Charset.forName(Misc.DEFAULT_CHARSET)));
            writer.write("<html>\n\t<head/>\n\t<body>");

            for (ThematicGrid grid : grids) {
                writer.write("\n\t\t<table>");
                writer.write("\n\t\t\t<tr>\n\t\t\t\t<td colspan=\"2\" valign=\"top\"><h3>" + grid.getName()
                        + "</h3></td>\n\t\t\t</tr>");
                writer.write("\n\t\t\t<tr>\n\t\t\t\t<td colspan=\"2\" valign=\"top\">" + grid.getDescription()
                        + "</td>\n\t\t\t</tr>");
                writer.write("\n\t\t\t<tr>\n\t\t\t\t<td valign=\"top\"><u>Included verbs</u>"
                        + convertVerbListIntoHtml(grid.getVerbs()) + "\n\t\t\t\t</td>");
                writer.write("\n\t\t\t\t<td valign=\"top\"><u>Associated Thematic Roles</u>"
                        + convertRoleMapIntoHtml(grid.getRoles()) + "\n\t\t\t\t</td>\n\t\t\t</tr>");

                writer.write("\n\t\t</table>");
            }

            writer.write("\n\t<body>\n</html>");
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }

    @Override
    public ValidationReport validateInterfaceArtifact(InterfaceArtifact artifact, IFile iFile) throws Exception {
        if (artifact != null && iFile != null) {
            // get Parser depending on the file extension
            Parser parser = ServiceManager.getInstance().getParsingService().getParser(iFile.getFileExtension());
            if (parser != null) {
                return parser.validateArtifact(artifact);
            } else {
                String message = "No parser for file extension " + String.valueOf(iFile.getFileExtension())
                        + " found.";
                logger.log(Level.SEVERE, message);
                throw new IllegalStateException(message);
            }
        } else {
            logger.log(Level.SEVERE, "The input parameters must be initalized. interfaceStructure="
                    + String.valueOf(artifact) + "; iFile=" + String.valueOf(iFile));
            throw new IllegalArgumentException("The input parameters must be initalized.");
        }
    }

    @Override
    public void addAddresseChangeListener(final IConfigurationChangeListener listener) {
        if (listener == null) {
            throw new NullPointerException(ERR_MSG_LISTENER_NULL);
        }
        this.addresseChangeListeners.add(listener);
    }

    @Override
    public void removeAddresseChangeListener(final IConfigurationChangeListener listener) {
        if (listener != null) {
            this.addresseChangeListeners.remove(listener);
        }
    }

    @Override
    public void addThematicRoleChangeListener(final IConfigurationChangeListener listener) {
        if (listener == null) {
            throw new NullPointerException(ERR_MSG_LISTENER_NULL);
        }
        this.thematicRoleChangeListeners.add(listener);
    }

    @Override
    public void removeThematicRoleChangeListener(final IConfigurationChangeListener listener) {
        if (listener != null) {
            this.thematicRoleChangeListeners.remove(listener);
        }
    }

    @Override
    public void addThematicGridChangeListener(final IConfigurationChangeListener listener) {
        if (listener == null) {
            throw new NullPointerException(ERR_MSG_LISTENER_NULL);
        }
        this.thematicGridChangeListeners.add(listener);
    }

    @Override
    public void removeThematicGridChangeListener(final IConfigurationChangeListener listener) {
        if (listener != null) {
            this.thematicGridChangeListeners.remove(listener);
        }
    }

    @Override
    public void removeAllAddresseChangeListener() {
        this.addresseChangeListeners.clear();
    }

    @Override
    public void removeAllThematicRoleChangeListener() {
        this.thematicRoleChangeListeners.clear();
    }

    @Override
    public void removeAllThematicGridChangeListener() {
        this.thematicGridChangeListeners.clear();
    }

    /**
     * Fires for all <code>listeners</code> the configuration change event.
     * 
     * @param listeners
     *            [DESTINATION]
     * @thematicgrid Sending Operations
     */
    private void notifyListeners(final Collection<IConfigurationChangeListener> listeners) {
        for (final IConfigurationChangeListener l : listeners) {
            l.configurationChange();
        }
    }

    /**
     * Comparator for {@link IConfigurationChangeListener}s. It implements a rudimentary
     * compare method, so the listener can be used within {@link Set}.
     * 
     * @author Dirk Meier-Eickhoff
     * 
     */
    private static class ConfigurationChangeListenerComparator
            implements Comparator<IConfigurationChangeListener>, Serializable {

        private static final long serialVersionUID = 1L;

        /**
         * Compare only the object references for equality.
         */
        @Override
        public int compare(IConfigurationChangeListener o1, IConfigurationChangeListener o2) {
            if (o1 == o2) {
                return 0;
            } else {
                return -1;
            }
        }
    }
}