/* * 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.schemaeditor.view.wizards; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.directory.api.ldap.model.schema.AbstractSchemaObject; import org.apache.directory.api.ldap.model.schema.AttributeType; import org.apache.directory.api.ldap.model.schema.MutableAttributeType; import org.apache.directory.api.ldap.model.schema.MutableObjectClass; import org.apache.directory.api.ldap.model.schema.ObjectClass; import org.apache.directory.studio.schemaeditor.Activator; import org.apache.directory.studio.schemaeditor.model.Project; import org.apache.directory.studio.schemaeditor.model.Schema; import org.apache.directory.studio.schemaeditor.view.dialogs.MessageDialogWithTextarea; import org.apache.directory.studio.schemaeditor.view.wizards.MergeSchemasSelectionWizardPage.AttributeTypeFolder; import org.apache.directory.studio.schemaeditor.view.wizards.MergeSchemasSelectionWizardPage.AttributeTypeWrapper; import org.apache.directory.studio.schemaeditor.view.wizards.MergeSchemasSelectionWizardPage.ObjectClassFolder; import org.apache.directory.studio.schemaeditor.view.wizards.MergeSchemasSelectionWizardPage.ObjectClassWrapper; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.wizard.Wizard; import org.eclipse.osgi.util.NLS; import org.eclipse.ui.IImportWizard; import org.eclipse.ui.IWorkbench; /** * This class represents the wizard to merge schema projects. * * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> */ public class MergeSchemasWizard extends Wizard implements IImportWizard { // The pages of the wizard private MergeSchemasSelectionWizardPage selectionPage; private MergeSchemasOptionsWizardPage optionsPage; /** * {@inheritDoc} */ public void addPages() { // Creating pages selectionPage = new MergeSchemasSelectionWizardPage(); optionsPage = new MergeSchemasOptionsWizardPage(); // Adding pages addPage( selectionPage ); addPage( optionsPage ); } /** * {@inheritDoc} */ public boolean performFinish() { Object[] sourceObjects = selectionPage.getSelectedObjects(); boolean replaceUnknownSyntax = optionsPage.isReplaceUnknownSyntax(); boolean mergeDependencies = optionsPage.isMergeDependencies(); boolean pullUpAttributes = optionsPage.isPullUpAttributes(); List<String> errorMessages = new ArrayList<String>(); mergeObjects( sourceObjects, errorMessages, replaceUnknownSyntax, mergeDependencies, pullUpAttributes ); if ( !errorMessages.isEmpty() ) { StringBuilder sb = new StringBuilder(); for ( String errorMessage : errorMessages ) { sb.append( errorMessage ); sb.append( '\n' ); } new MessageDialogWithTextarea( getShell(), Messages.getString( "MergeSchemasWizard.MergeResultTitle" ), //$NON-NLS-1$ Messages.getString( "MergeSchemasWizard.MergeResultMessage" ), sb.toString() ).open(); //$NON-NLS-1$ } return true; } private void mergeObjects( Object[] sourceObjects, List<String> errorMessages, boolean replaceUnknownSyntax, boolean mergeDependencies, boolean pullUpAttributes ) { /* * List of already processed schema objects. Used to avoid that schema objects are process multiple time. */ Set<Object> processedObjects = new HashSet<Object>(); /* * List of created target schemas. */ Map<String, Schema> targetSchemas = new HashMap<String, Schema>(); Project targetProject = Activator.getDefault().getProjectsHandler().getOpenProject(); // merge all source objects to the target project for ( Object sourceObject : sourceObjects ) { if ( sourceObject instanceof Project ) { Project sourceProject = ( Project ) sourceObject; for ( Schema sourceSchema : sourceProject.getSchemaHandler().getSchemas() ) { Schema targetSchema = getTargetSchema( sourceSchema.getProject(), targetProject, targetSchemas ); mergeSchema( sourceSchema, targetProject, targetSchema, processedObjects, errorMessages, replaceUnknownSyntax, mergeDependencies, pullUpAttributes ); } } if ( sourceObject instanceof Schema ) { Schema sourceSchema = ( Schema ) sourceObject; Schema targetSchema = getTargetSchema( sourceSchema.getProject(), targetProject, targetSchemas ); mergeSchema( sourceSchema, targetProject, targetSchema, processedObjects, errorMessages, replaceUnknownSyntax, mergeDependencies, pullUpAttributes ); } if ( sourceObject instanceof AttributeTypeFolder ) { AttributeTypeFolder atf = ( AttributeTypeFolder ) sourceObject; Schema targetSchema = getTargetSchema( atf.schema.getProject(), targetProject, targetSchemas ); List<AttributeType> sourceAttributeTypes = atf.schema.getAttributeTypes(); for ( AttributeType sourceAttributeType : sourceAttributeTypes ) { mergeAttributeType( sourceAttributeType, targetProject, targetSchema, processedObjects, errorMessages, replaceUnknownSyntax, mergeDependencies, pullUpAttributes ); } } if ( sourceObject instanceof ObjectClassFolder ) { ObjectClassFolder ocf = ( ObjectClassFolder ) sourceObject; Schema targetSchema = getTargetSchema( ocf.schema.getProject(), targetProject, targetSchemas ); List<ObjectClass> sourceObjectClasses = ocf.schema.getObjectClasses(); for ( ObjectClass sourceObjectClass : sourceObjectClasses ) { mergeObjectClass( sourceObjectClass, targetProject, targetSchema, processedObjects, errorMessages, replaceUnknownSyntax, mergeDependencies, pullUpAttributes ); } } if ( sourceObject instanceof AttributeTypeWrapper ) { AttributeTypeWrapper atw = ( AttributeTypeWrapper ) sourceObject; Schema targetSchema = getTargetSchema( atw.folder.schema.getProject(), targetProject, targetSchemas ); mergeAttributeType( atw.attributeType, targetProject, targetSchema, processedObjects, errorMessages, replaceUnknownSyntax, mergeDependencies, pullUpAttributes ); } if ( sourceObject instanceof ObjectClassWrapper ) { ObjectClassWrapper ocw = ( ObjectClassWrapper ) sourceObject; Schema targetSchema = getTargetSchema( ocw.folder.schema.getProject(), targetProject, targetSchemas ); mergeObjectClass( ocw.objectClass, targetProject, targetSchema, processedObjects, errorMessages, replaceUnknownSyntax, mergeDependencies, pullUpAttributes ); } } //add created target schemas to project for ( Schema targetSchema : targetSchemas.values() ) { if ( !targetProject.getSchemaHandler().getSchemas().contains( targetSchema ) ) { targetProject.getSchemaHandler().addSchema( targetSchema ); } } } private Schema getTargetSchema( Project sourceProject, Project targetProject, Map<String, Schema> targetSchemas ) { String targetSchemaName = "merge-from-" + sourceProject.getName(); //$NON-NLS-1$ Schema targetSchema = targetProject.getSchemaHandler().getSchema( targetSchemaName ); if ( targetSchema != null ) { targetProject.getSchemaHandler().removeSchema( targetSchema ); } else if ( targetSchemas.containsKey( targetSchemaName ) ) { targetSchema = targetSchemas.get( targetSchemaName ); } else { targetSchema = new Schema( targetSchemaName ); targetSchema.setProject( targetProject ); } targetSchemas.put( targetSchemaName, targetSchema ); return targetSchema; } /** * Merges all attribute types and object classes and form the given sourceSchema to the targetSchema. */ private void mergeSchema( Schema sourceSchema, Project targetProject, Schema targetSchema, Set<Object> processedObjects, List<String> errorMessages, boolean replaceUnknownSyntax, boolean mergeDependencies, boolean pullUpAttributes ) { List<AttributeType> sourceAttributeTypes = sourceSchema.getAttributeTypes(); for ( AttributeType sourceAttributeType : sourceAttributeTypes ) { mergeAttributeType( sourceAttributeType, targetProject, targetSchema, processedObjects, errorMessages, replaceUnknownSyntax, mergeDependencies, pullUpAttributes ); } List<ObjectClass> sourceObjectClasses = sourceSchema.getObjectClasses(); for ( ObjectClass sourceObjectClass : sourceObjectClasses ) { mergeObjectClass( sourceObjectClass, targetProject, targetSchema, processedObjects, errorMessages, replaceUnknownSyntax, mergeDependencies, pullUpAttributes ); } } /** * Merges the given attribute type to the targetSchema. */ private void mergeAttributeType( AttributeType sourceAttributeType, Project targetProject, Schema targetSchema, Set<Object> processedObjects, List<String> errorMessages, boolean replaceUnknownSyntax, boolean mergeDependencies, boolean pullUpAttributes ) { if ( processedObjects.contains( sourceAttributeType ) ) { return; } processedObjects.add( sourceAttributeType ); // check if attribute (identified by OID or name) already exists in the project AttributeType targetAttributeType = targetProject.getSchemaHandler().getAttributeType( sourceAttributeType.getOid() ); if ( targetAttributeType == null ) { for ( String name : sourceAttributeType.getNames() ) { targetAttributeType = targetProject.getSchemaHandler().getAttributeType( name ); if ( targetAttributeType != null ) { break; } } } // check if OID or alias name already exist in target project boolean oidOrAliasAlreadyTaken = targetProject.getSchemaHandler().isOidAlreadyTaken( sourceAttributeType.getOid() ); if ( !oidOrAliasAlreadyTaken ) { for ( String name : sourceAttributeType.getNames() ) { oidOrAliasAlreadyTaken = targetProject.getSchemaHandler().isAliasAlreadyTakenForAttributeType( name ); if ( oidOrAliasAlreadyTaken ) { break; } } } if ( targetAttributeType != null ) { errorMessages.add( NLS.bind( Messages.getString( "MergeSchemasWizard.AttributeTypeExistsInTargetProject" ), //$NON-NLS-1$ getIdString( sourceAttributeType ) ) ); } else { if ( oidOrAliasAlreadyTaken ) { errorMessages.add( NLS.bind( Messages.getString( "MergeSchemasWizard.OidOrAliasAlreadyTaken" ), //$NON-NLS-1$ getIdString( sourceAttributeType ) ) ); } else { // remove attribute type if already there from previous merge AttributeType at = targetSchema.getAttributeType( sourceAttributeType.getOid() ); if ( at != null ) { targetSchema.removeAttributeType( at ); } // clone attribute type MutableAttributeType clonedAttributeType = new MutableAttributeType( sourceAttributeType.getOid() ); clonedAttributeType.setNames( sourceAttributeType.getNames() ); clonedAttributeType.setDescription( sourceAttributeType.getDescription() ); clonedAttributeType.setSuperiorOid( sourceAttributeType.getSuperiorOid() ); clonedAttributeType.setUsage( sourceAttributeType.getUsage() ); clonedAttributeType.setSyntaxOid( sourceAttributeType.getSyntaxOid() ); clonedAttributeType.setSyntaxLength( sourceAttributeType.getSyntaxLength() ); clonedAttributeType.setObsolete( sourceAttributeType.isObsolete() ); clonedAttributeType.setCollective( sourceAttributeType.isCollective() ); clonedAttributeType.setSingleValued( sourceAttributeType.isSingleValued() ); clonedAttributeType.setUserModifiable( sourceAttributeType.isUserModifiable() ); clonedAttributeType.setEqualityOid( sourceAttributeType.getEqualityOid() ); clonedAttributeType.setOrderingOid( sourceAttributeType.getOrderingOid() ); clonedAttributeType.setSubstringOid( sourceAttributeType.getSubstringOid() ); clonedAttributeType.setSchemaName( targetSchema.getSchemaName() ); // if no/unknown syntax: set "Directory String" syntax and appropriate matching rules if ( replaceUnknownSyntax ) { if ( clonedAttributeType.getSyntaxOid() == null || targetProject.getSchemaHandler().getSyntax( clonedAttributeType.getSyntaxOid() ) == null ) { errorMessages.add( NLS.bind( Messages.getString( "MergeSchemasWizard.ReplacedSyntax" ), //$NON-NLS-1$ new String[] { getIdString( sourceAttributeType ), clonedAttributeType.getSyntaxOid(), "1.3.6.1.4.1.1466.115.121.1.15 (Directory String)" } ) ); //$NON-NLS-1$ clonedAttributeType.setSyntaxOid( "1.3.6.1.4.1.1466.115.121.1.15" ); //$NON-NLS-1$ clonedAttributeType.setEqualityOid( "caseIgnoreMatch" ); //$NON-NLS-1$ clonedAttributeType.setOrderingOid( null ); clonedAttributeType.setSubstringOid( "caseIgnoreSubstringsMatch" ); //$NON-NLS-1$ } } // TODO: if unknown (single) matching rule: set appropriate matching rule according to syntax // TODO: if no (all) matching rules: set appropriate matching rules according to syntax // merge dependencies: super attribute type if ( mergeDependencies ) { String superiorName = clonedAttributeType.getSuperiorOid(); if ( superiorName != null ) { AttributeType superiorAttributeType = Activator.getDefault().getSchemaHandler() .getAttributeType( superiorName ); if ( superiorAttributeType != null ) { mergeAttributeType( superiorAttributeType, targetProject, targetSchema, processedObjects, errorMessages, replaceUnknownSyntax, mergeDependencies, pullUpAttributes ); } } } targetSchema.addAttributeType( clonedAttributeType ); } } } /** * Merges the given object class to the targetSchema. */ private void mergeObjectClass( ObjectClass sourceObjectClass, Project targetProject, Schema targetSchema, Set<Object> processedObjects, List<String> errorMessages, boolean replaceUnknownSyntax, boolean mergeDependencies, boolean pullUpAttributes ) { if ( processedObjects.contains( sourceObjectClass ) ) { return; } processedObjects.add( sourceObjectClass ); // check if object class (identified by OID or alias name) already exists in the target project ObjectClass targetObjectClass = targetProject.getSchemaHandler() .getObjectClass( sourceObjectClass.getOid() ); if ( targetObjectClass == null ) { for ( String name : sourceObjectClass.getNames() ) { targetObjectClass = targetProject.getSchemaHandler().getObjectClass( name ); if ( targetObjectClass != null ) { break; } } } // check if OID or alias name already exist in target project boolean oidOrAliasAlreadyTaken = targetProject.getSchemaHandler().isOidAlreadyTaken( sourceObjectClass.getOid() ); if ( !oidOrAliasAlreadyTaken ) { for ( String name : sourceObjectClass.getNames() ) { oidOrAliasAlreadyTaken = targetProject.getSchemaHandler().isAliasAlreadyTakenForObjectClass( name ); if ( oidOrAliasAlreadyTaken ) { break; } } } if ( targetObjectClass != null ) { errorMessages.add( NLS.bind( Messages.getString( "MergeSchemasWizard.ObjectClassExistsInTargetProject" ), //$NON-NLS-1$ getIdString( sourceObjectClass ) ) ); } else { if ( oidOrAliasAlreadyTaken ) { errorMessages.add( NLS.bind( Messages.getString( "MergeSchemasWizard.OidOrAliasAlreadyTaken" ), //$NON-NLS-1$ getIdString( sourceObjectClass ) ) ); } else { // remove object class if already there from previous merge ObjectClass oc = targetSchema.getObjectClass( sourceObjectClass.getOid() ); if ( oc != null ) { targetSchema.removeObjectClass( oc ); } // create object class MutableObjectClass clonedObjectClass = new MutableObjectClass( sourceObjectClass.getOid() ); clonedObjectClass.setOid( sourceObjectClass.getOid() ); clonedObjectClass.setNames( sourceObjectClass.getNames() ); clonedObjectClass.setDescription( sourceObjectClass.getDescription() ); clonedObjectClass.setSuperiorOids( sourceObjectClass.getSuperiorOids() ); clonedObjectClass.setType( sourceObjectClass.getType() ); clonedObjectClass.setObsolete( sourceObjectClass.isObsolete() ); clonedObjectClass.setMustAttributeTypeOids( sourceObjectClass.getMustAttributeTypeOids() ); clonedObjectClass.setMayAttributeTypeOids( sourceObjectClass.getMayAttributeTypeOids() ); clonedObjectClass.setSchemaName( targetSchema.getSchemaName() ); // merge dependencies: super object classes and must/may attributes if ( mergeDependencies ) { List<String> superClassesNames = clonedObjectClass.getSuperiorOids(); if ( superClassesNames != null ) { for ( String superClassName : superClassesNames ) { if ( superClassName != null ) { ObjectClass superSourceObjectClass = Activator.getDefault().getSchemaHandler() .getObjectClass( superClassName ); ObjectClass superTargetObjectClass = targetProject.getSchemaHandler() .getObjectClass( superClassName ); if ( superSourceObjectClass != null ) { if ( superTargetObjectClass == null ) { mergeObjectClass( superSourceObjectClass, targetProject, targetSchema, processedObjects, errorMessages, replaceUnknownSyntax, mergeDependencies, pullUpAttributes ); } else { // pull-up may and must attributes to this OC if super already exists in target if ( pullUpAttributes ) { pullUpAttributes( clonedObjectClass, superSourceObjectClass, superTargetObjectClass ); } } } } } } List<String> mustNamesList = clonedObjectClass.getMustAttributeTypeOids(); List<String> mayNamesList = clonedObjectClass.getMayAttributeTypeOids(); List<String> attributeNames = new ArrayList<String>(); if ( mustNamesList != null ) { attributeNames.addAll( mustNamesList ); } if ( mayNamesList != null ) { attributeNames.addAll( mayNamesList ); } for ( String attributeName : attributeNames ) { if ( attributeName != null ) { AttributeType attributeType = Activator.getDefault().getSchemaHandler() .getAttributeType( attributeName ); if ( attributeType != null ) { mergeAttributeType( attributeType, targetProject, targetSchema, processedObjects, errorMessages, replaceUnknownSyntax, mergeDependencies, pullUpAttributes ); } } } } targetSchema.addObjectClass( clonedObjectClass ); } } } private void pullUpAttributes( MutableObjectClass targetObjectClass, ObjectClass sourceSuperObjectClass, ObjectClass targetSuperObjectClass ) { // must Set<String> sourceMustAttributeNames = new HashSet<String>(); fetchAttributes( sourceMustAttributeNames, sourceSuperObjectClass, true ); Set<String> targetMustAttributeNames = new HashSet<String>(); fetchAttributes( targetMustAttributeNames, targetSuperObjectClass, true ); sourceMustAttributeNames.removeAll( targetMustAttributeNames ); if ( !sourceMustAttributeNames.isEmpty() ) { sourceMustAttributeNames.addAll( targetObjectClass.getMustAttributeTypeOids() ); targetObjectClass.setMustAttributeTypeOids( new ArrayList<String>( sourceMustAttributeNames ) ); } // may Set<String> sourceMayAttributeNames = new HashSet<String>(); fetchAttributes( sourceMayAttributeNames, sourceSuperObjectClass, false ); Set<String> targetMayAttributeNames = new HashSet<String>(); fetchAttributes( targetMayAttributeNames, targetSuperObjectClass, false ); sourceMayAttributeNames.removeAll( targetMayAttributeNames ); if ( !sourceMayAttributeNames.isEmpty() ) { sourceMayAttributeNames.addAll( targetObjectClass.getMayAttributeTypeOids() ); targetObjectClass.setMayAttributeTypeOids( new ArrayList<String>( sourceMayAttributeNames ) ); } } private void fetchAttributes( Set<String> attributeNameList, ObjectClass oc, boolean must ) { List<String> attributeNames = must ? oc.getMustAttributeTypeOids() : oc.getMayAttributeTypeOids(); attributeNameList.addAll( attributeNames ); for ( String superClassName : oc.getSuperiorOids() ) { ObjectClass superObjectClass = Activator.getDefault().getSchemaHandler().getObjectClass( superClassName ); fetchAttributes( attributeNameList, superObjectClass, must ); } } private String getIdString( AbstractSchemaObject schemaObject ) { StringBuilder sb = new StringBuilder(); sb.append( '[' ); if ( schemaObject.getNames() != null ) { for ( String name : schemaObject.getNames() ) { sb.append( name ); sb.append( ',' ); } } sb.append( schemaObject.getOid() ); sb.append( ']' ); return sb.toString(); } /** * {@inheritDoc} */ public void init( IWorkbench workbench, IStructuredSelection selection ) { } }