org.apache.directory.studio.templateeditor.EntryTemplatePluginUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.directory.studio.templateeditor.EntryTemplatePluginUtils.java

Source

/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you 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.apache.directory.studio.templateeditor;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.collections.map.MultiValueMap;
import org.apache.directory.api.ldap.model.schema.ObjectClass;
import org.apache.directory.api.ldap.model.schema.ObjectClassTypeEnum;
import org.apache.directory.studio.ldapbrowser.core.model.IEntry;
import org.apache.directory.studio.ldapbrowser.core.model.schema.Schema;
import org.eclipse.core.runtime.Status;

import org.apache.directory.studio.templateeditor.model.Template;

/**
 * This class is a helper class for the Entry Template plugin.
 *
 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
 */
public class EntryTemplatePluginUtils {
    /** The line separator */
    public static final String LINE_SEPARATOR = System.getProperty("line.separator"); //$NON-NLS-1$

    /** The default schema */
    private static final Schema DEFAULT_SCHEMA = Schema.DEFAULT_SCHEMA;

    /**
     * Logs the given message and exception with the ERROR status level.
     * 
     * @param exception
     *      the exception, can be <code>null</code>
     * @param message
     *      the message
     * @param args
     *      the arguments to use when formatting the message
     */
    public static void logError(Throwable exception, String message, Object... args) {
        EntryTemplatePlugin.getDefault().getLog()
                .log(new Status(Status.ERROR, EntryTemplatePlugin.getDefault().getBundle().getSymbolicName(),
                        Status.OK, MessageFormat.format(message, args), exception));
    }

    /**
     * Logs the given message and exception with the WARNING status level.
     * 
     * @param exception
     *      the exception, can be <code>null</code>
     * @param message
     *      the message
     * @param args
     *      the arguments to use when formatting the message
     */
    public static void logWarning(Throwable exception, String message, Object... args) {
        EntryTemplatePlugin.getDefault().getLog()
                .log(new Status(Status.WARNING, EntryTemplatePlugin.getDefault().getBundle().getSymbolicName(),
                        Status.OK, MessageFormat.format(message, args), exception));
    }

    /**
     * Logs the given message and exception with the INFO status level.
     * 
     * @param exception
     *      the exception, can be <code>null</code>
     * @param message
     *      the message
     * @param args
     *      the arguments to use when formatting the message
     */
    public static void logInfo(Throwable exception, String message, Object... args) {
        EntryTemplatePlugin.getDefault().getLog()
                .log(new Status(Status.INFO, EntryTemplatePlugin.getDefault().getBundle().getSymbolicName(),
                        Status.OK, MessageFormat.format(message, args), exception));
    }

    /**
     * Logs the given message and exception with the OK status level.
     * 
     * @param exception
     *      the exception, can be <code>null</code>
     * @param message
     *      the message
     * @param args
     *      the arguments to use when formatting the message
     */
    public static void logOk(Throwable exception, String message, Object... args) {
        EntryTemplatePlugin.getDefault().getLog()
                .log(new Status(Status.OK, EntryTemplatePlugin.getDefault().getBundle().getSymbolicName(),
                        Status.OK, MessageFormat.format(message, args), exception));
    }

    /**
     * Copies a file from the given streams.
     *
     * @param source
     *      the source file
     * @param destination
     *      the destination file
     * @throws IOException
     *      if an error occurs when copying the file
     */
    public static void copyFile(File source, File destination) throws IOException {
        copyFile(new FileInputStream(source), new FileOutputStream(destination));
    }

    /**
     * Copies the input stream to the output stream.
     *
     * @param inputStream
     *      the input stream
     * @param outputStream
     *      the output stream
     * @throws IOException
     *      if an error occurs when copying the stream
     */
    public static void copyFile(InputStream inputStream, OutputStream outputStream) throws IOException {
        byte[] buf = new byte[1024];
        int i = 0;
        while ((i = inputStream.read(buf)) != -1) {
            outputStream.write(buf, 0, i);
        }
    }

    /**
     * Gets a list of templates matching the given entry.
     * 
     * @param entry
     *      the entry
     * @return
     *      a list of templates matching the given entry
     */
    public static List<Template> getMatchingTemplates(IEntry entry) {
        if (entry != null) {
            // Looking for the highest (most specialized one) structural object class in the entry
            ObjectClass highestStructuralObjectClass = getHighestStructuralObjectClassFromEntry(entry);
            if (highestStructuralObjectClass != null) {
                // We were able to determine the highest object class in the entry.

                // Based on that information, we will use the entry's schema to retrieve the list of matching templates
                return getTemplatesFromHighestObjectClass(highestStructuralObjectClass,
                        entry.getBrowserConnection().getSchema());
            } else {
                // We were not able to determine the highest object class in the entry.
                // This means that either the schema information we received from the server is not sufficient,
                // or the list of object classes in the entry is not complete.

                // In that case we can't use the schema information to determine the list of templates.
                // Instead we're going to gather all the templates associated with each object class description.
                return getTemplatesFromObjectClassDescriptions(entry.getObjectClassDescriptions());
            }
        }

        return new ArrayList<Template>();
    }

    /**
     * Gets the highest (most specialized one) object class description of the given entry 
     * if it can be found, or <code>null</code> if not.
     *
     * @param entry
     *      the entry
     * @return
     *      the highest object class description of the given entry if it can be found, 
     *      or <code>null</code> if not
     */
    private static ObjectClass getHighestStructuralObjectClassFromEntry(IEntry entry) {
        if (entry != null) {
            if ((entry.getBrowserConnection() != null) && (entry.getBrowserConnection().getSchema() != null)) {
                // Getting the schema from the entry
                Schema schema = entry.getBrowserConnection().getSchema();

                // Getting object class descriptions
                Collection<ObjectClass> objectClassDescriptions = entry.getObjectClassDescriptions();
                if (objectClassDescriptions != null) {
                    // Creating the candidates list based on the initial list
                    List<ObjectClass> candidatesList = new ArrayList<ObjectClass>();

                    // Adding each structural object class description to the list
                    for (ObjectClass objectClassDescription : objectClassDescriptions) {
                        if (objectClassDescription.getType() == ObjectClassTypeEnum.STRUCTURAL) {
                            candidatesList.add(objectClassDescription);
                        }
                    }

                    // Looping on the given collection of ObjectClassDescription until the end of the list, 
                    // or until the candidates list is reduced to one.
                    Iterator<ObjectClass> iterator = objectClassDescriptions.iterator();
                    while ((candidatesList.size() > 1) && (iterator.hasNext())) {
                        ObjectClass ocd = iterator.next();
                        removeSuperiors(ocd, candidatesList, schema);
                    }

                    // Looking if we've found the highest object class description
                    if (candidatesList.size() == 1) {
                        return candidatesList.get(0);
                    }
                }
            }
        }

        return null;
    }

    /**
     * Recursively removes superiors of the given object class description from the list.
     *
     * @param ocd
     *      the object class description
     * @param ocdList
     *      the list of object class description
     * @param schema
     *      the schema
     */
    private static void removeSuperiors(ObjectClass ocd, List<ObjectClass> ocdList, Schema schema) {
        if (ocd != null) {
            for (String superior : ocd.getSuperiorOids()) {
                // Getting the ObjectClassDescription associated with the superior
                ObjectClass superiorOcd = getObjectClass(superior, schema);

                // Removing it from the list and recursively removing its superiors
                ocdList.remove(superiorOcd);
                removeSuperiors(superiorOcd, ocdList, schema);
            }
        }
    }

    /**
     * Gets the list of matching templates for the given object class description.
     * <p>
     * To do this, we're using a "Breadth First Search" algorithm to go through all
     * the superiors (and the superiors of these superiors, etc.).
     *
     * @param objectClassDescription
     *      the object class description
     * @param schema
     *      the associated schema
     * @return
     *      the list of matching templates for the given object class description
     */
    private static List<Template> getTemplatesFromHighestObjectClass(ObjectClass objectClassDescription,
            Schema schema) {
        // Creating a set to hold all the matching templates
        List<Template> matchingTemplates = new ArrayList<Template>();

        // Getting the templates manager
        TemplatesManager manager = EntryTemplatePlugin.getDefault().getTemplatesManager();

        // Getting the list of all the available templates
        Template[] templates = manager.getTemplates();

        // Creating a MultiValueMap that holds the templates ordered by ObjectClassDescription object
        MultiValueMap templatesByOcd = new MultiValueMap();

        // Populating this map
        for (Template template : templates) {
            templatesByOcd.put(getObjectClass(template.getStructuralObjectClass(), schema), template);
        }

        // Initializing the LIFO queue with the highest ObjectClassDescription object
        LinkedList<ObjectClass> ocdQueue = new LinkedList<ObjectClass>();
        ocdQueue.add(objectClassDescription);

        // Looking if we need to test a new ObjectClassDescription object
        while (!ocdQueue.isEmpty()) {
            // Dequeuing the last object for testing
            ObjectClass currentOcd = ocdQueue.removeLast();

            // Adds the templates for the current object class description to the list of matching templates
            addTemplatesForObjectClassDescription(currentOcd, matchingTemplates, manager);

            // Adding each superior object to the queue
            List<String> currentOcdSups = currentOcd.getSuperiorOids();
            if (currentOcdSups != null) {
                for (String currentOcdSup : currentOcdSups) {
                    ocdQueue.addFirst(getObjectClass(currentOcdSup, schema));
                }
            }
        }

        return matchingTemplates;
    }

    /**
     * Gets the list of matching templates for the given object class descriptions.
     *
     * @param objectClasses
     *      the object classes
     * @return
     *      the list of matching templates for the given object class description
     */
    private static List<Template> getTemplatesFromObjectClassDescriptions(Collection<ObjectClass> objectClasses) {
        if (objectClasses != null) {
            // Creating a set to hold all the matching templates
            List<Template> matchingTemplates = new ArrayList<Template>();

            // Getting the templates manager
            TemplatesManager manager = EntryTemplatePlugin.getDefault().getTemplatesManager();

            for (ObjectClass objectClassDescription : objectClasses) {
                // Adds the templates for the current object class description to the list of matching templates
                addTemplatesForObjectClassDescription(objectClassDescription, matchingTemplates, manager);
            }

            return matchingTemplates;
        }

        return null;
    }

    /**
     * Adds the templates found for the given object class description to the given templates set.
     *
     * @param ocd
     *      the object class description
     * @param matchingTemplates
     *      the list of matching templates
     * @param manager
     *      the manager
     */
    private static void addTemplatesForObjectClassDescription(ObjectClass ocd, List<Template> matchingTemplates,
            TemplatesManager manager) {
        // Creating a list of containing the names and OID of the current ObjectClassDescription object
        List<String> namesAndOid = new ArrayList<String>();
        for (String name : ocd.getNames()) {
            namesAndOid.add(name);
        }
        String currentOcdOid = ocd.getOid();
        if ((currentOcdOid != null) && (!"".equals(currentOcdOid))) //$NON-NLS-1$
        {
            namesAndOid.add(currentOcdOid);
        }

        // Looping on the names and OID to find all corresponding templates
        for (String nameOrOid : namesAndOid) {
            // Getting the default template and complete list of templates for the given name or OID
            Template currentOcdDefaultTemplate = manager.getDefaultTemplate(nameOrOid);
            List<Template> currentOcdTemplates = manager.getTemplatesByObjectClass(nameOrOid);

            // Adding the default template
            if (currentOcdDefaultTemplate != null) {
                if (!matchingTemplates.contains(currentOcdDefaultTemplate)) {
                    matchingTemplates.add(currentOcdDefaultTemplate);
                }
            }

            // Adding the other templates
            if (currentOcdTemplates != null) {
                for (Template template : currentOcdTemplates) {
                    // Adding the template only if it is different from the default one (which is already added)
                    if ((!template.equals(currentOcdDefaultTemplate)) && (manager.isEnabled(template))
                            && (!matchingTemplates.contains(template))) {
                        matchingTemplates.add(template);
                    }
                }
            }
        }
    }

    /**
     * Gets the object class description of the given name or OID found in the default schema.
     * <p>
     * If no object class description is found in the default schema, a new object class description
     * is created with the given name or OID and returned.
     *
     * @param nameOrOid
     *      the name or OID
     * @return
     *      the object class description of the given name or OID found in the default schema,
     *      or a new object class description created with the given name or OID if none can be found
     */
    public static ObjectClass getObjectClassDescriptionFromDefaultSchema(String nameOrOid) {
        return getObjectClass(nameOrOid, DEFAULT_SCHEMA);
    }

    /**
     * Gets the object class description of the given name or OID found in the given schema.
     * <p>
     * If no object class description is found in the given schema, a new object class description
     * is created with the given name or OID and returned.
     *
     * @param nameOrOid
     *      the name or OID
     * @param schema
     *      the schema
     * @return
     *      the object class description of the given name or OID found in the given schema,
     *      or a new object class description created with the given name or OID if none can be found
     */
    private static ObjectClass getObjectClass(String nameOrOid, Schema schema) {
        ObjectClass ocd = null;

        // Looking for the object class description in the given schema
        if (schema != null) {
            ocd = schema.getObjectClassDescription(nameOrOid);
        }

        // Creating a new object class description if none could be found in the given schema
        if (ocd == null) {
            ocd = new ObjectClass(null);
            ocd.setNames(Arrays.asList(new String[] { nameOrOid.toLowerCase() }));
        }

        return ocd;
    }
}