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