/* * 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; } }