/*
* 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.model.schemachecker;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.collections.MultiMap;
import org.apache.commons.collections.map.MultiValueMap;
import org.apache.directory.api.ldap.model.exception.LdapSchemaException;
import org.apache.directory.api.ldap.model.schema.AttributeType;
import org.apache.directory.api.ldap.model.schema.LdapSyntax;
import org.apache.directory.api.ldap.model.schema.MatchingRule;
import org.apache.directory.api.ldap.model.schema.ObjectClass;
import org.apache.directory.api.ldap.model.schema.SchemaManager;
import org.apache.directory.api.ldap.model.schema.SchemaObject;
import org.apache.directory.api.ldap.schema.manager.impl.DefaultSchemaManager;
import org.apache.directory.studio.schemaeditor.Activator;
import org.apache.directory.studio.schemaeditor.controller.ProjectsHandlerAdapter;
import org.apache.directory.studio.schemaeditor.controller.SchemaHandler;
import org.apache.directory.studio.schemaeditor.controller.SchemaHandlerAdapter;
import org.apache.directory.studio.schemaeditor.controller.SchemaHandlerListener;
import org.apache.directory.studio.schemaeditor.model.Project;
import org.apache.directory.studio.schemaeditor.model.Schema;
import org.apache.directory.studio.schemaeditor.model.schemamanager.SchemaEditorSchemaLoader;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
/**
* This class represents the SchemaChecker.
* <p>
* It is used to check the schema integrity.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public class SchemaChecker
{
/** The SchemaChecker instance */
private static SchemaChecker instance;
/** The schema manager */
private SchemaManager schemaManager;
/** The errors map */
private MultiMap errorsMap = new MultiValueMap();
/** The warnings list */
private List<SchemaWarning> warningsList = new ArrayList<SchemaWarning>();
/** The warnings map */
private MultiMap warningsMap = new MultiValueMap();
/** The lock object used to synchronize accesses to the errors and warnings maps*/
private static Object lock = new Object();
/** The 'listening to modifications' flag*/
private boolean listeningToModifications = false;
/** The listeners List */
private List<SchemaCheckerListener> listeners = new ArrayList<SchemaCheckerListener>();
/** The SchemaHandlerListener */
private SchemaHandlerListener schemaHandlerListener = new SchemaHandlerAdapter()
{
public void attributeTypeAdded( AttributeType at )
{
synchronized ( this )
{
recheckWholeSchema();
}
}
public void attributeTypeModified( AttributeType at )
{
synchronized ( this )
{
recheckWholeSchema();
}
}
public void attributeTypeRemoved( AttributeType at )
{
synchronized ( this )
{
recheckWholeSchema();
}
}
public void objectClassAdded( ObjectClass oc )
{
synchronized ( this )
{
recheckWholeSchema();
}
}
public void objectClassModified( ObjectClass oc )
{
synchronized ( this )
{
recheckWholeSchema();
}
}
public void objectClassRemoved( ObjectClass oc )
{
synchronized ( this )
{
recheckWholeSchema();
}
}
public void schemaAdded( Schema schema )
{
synchronized ( this )
{
recheckWholeSchema();
}
}
public void schemaRemoved( Schema schema )
{
synchronized ( this )
{
recheckWholeSchema();
}
}
public void schemaRenamed( Schema schema )
{
// Nothing to do, this is a simple renaming
}
};
/**
* Creates a new instance of SchemaChecker.
*/
private SchemaChecker()
{
Activator.getDefault().getProjectsHandler().addListener( new ProjectsHandlerAdapter()
{
public void openProjectChanged( Project oldProject, Project newProject )
{
if ( oldProject != null )
{
oldProject.getSchemaHandler().removeListener( schemaHandlerListener );
}
if ( newProject != null )
{
newProject.getSchemaHandler().addListener( schemaHandlerListener );
}
}
} );
}
/**
* Gets the singleton instance of the ProjectsHandler.
*
* @return
* the singleton instance of the ProjectsHandler
*/
public static SchemaChecker getInstance()
{
if ( instance == null )
{
instance = new SchemaChecker();
}
return instance;
}
/**
* Enables modifications listening.
*/
public void enableModificationsListening()
{
synchronized ( this )
{
if ( !listeningToModifications )
{
Activator.getDefault().getSchemaHandler().addListener( schemaHandlerListener );
listeningToModifications = true;
recheckWholeSchema();
}
}
}
/**
* Disables modifications listening.
*/
public void disableModificationsListening()
{
synchronized ( this )
{
if ( listeningToModifications )
{
Activator.getDefault().getSchemaHandler().removeListener( schemaHandlerListener );
listeningToModifications = false;
}
}
}
/**
* Reloads the content of the schema checker
*/
public void reload()
{
synchronized ( this )
{
recheckWholeSchema();
}
}
/**
* Returns true if the SchemaChecker is listening to modifications,
* false if not.
*
* @return
* true if the SchemaChecker is listening to modifications,
* false if not
*/
public boolean isListeningToModifications()
{
return listeningToModifications;
}
/**
* Checks the whole schema.
*/
private void recheckWholeSchema()
{
Job job = new Job( "Checking Schema" )
{
protected IStatus run( IProgressMonitor monitor )
{
// Checks the whole schema via the schema manager
try
{
schemaManager = new DefaultSchemaManager( new SchemaEditorSchemaLoader() );
schemaManager.loadAllEnabled();
}
catch ( Exception e )
{
// TODO Auto-generated catch block
e.printStackTrace();
}
// Updates errors and warnings
updateErrorsAndWarnings();
// Notify listeners
notifyListeners();
monitor.done();
return Status.OK_STATUS;
}
};
job.schedule();
}
/**
* Updates the errors and warnings.
*/
private synchronized void updateErrorsAndWarnings()
{
synchronized ( lock )
{
// Errors
errorsMap.clear();
indexErrors();
// Warnings
createWarnings();
warningsMap.clear();
indexWarnings();
}
}
/**
* Indexes the errors.
*/
private void indexErrors()
{
for ( Throwable error : schemaManager.getErrors() )
{
if ( error instanceof LdapSchemaException )
{
LdapSchemaException ldapSchemaException = ( LdapSchemaException ) error;
SchemaObject source = ldapSchemaException.getSourceObject();
if ( source != null )
{
SchemaHandler schemaHandler = Activator.getDefault().getSchemaHandler();
if ( source instanceof AttributeType )
{
source = schemaHandler.getAttributeType( source.getOid() );
}
else if ( source instanceof LdapSyntax )
{
source = schemaHandler.getSyntax( source.getOid() );
}
else if ( source instanceof MatchingRule )
{
source = schemaHandler.getMatchingRule( source.getOid() );
}
else if ( source instanceof ObjectClass )
{
source = schemaHandler.getObjectClass( source.getOid() );
}
errorsMap.put( source, ldapSchemaException );
}
}
}
}
/**
* Creates the warnings.
*/
private void createWarnings()
{
// Clearing previous warnings
warningsList.clear();
// Getting the schema handler to check for schema objects without names (aliases)
SchemaHandler schemaHandler = Activator.getDefault().getSchemaHandler();
if ( schemaHandler != null )
{
// Checking attribute types
for ( AttributeType attributeType : schemaHandler.getAttributeTypes() )
{
checkSchemaObjectNames( attributeType );
}
// Checking object classes
for ( ObjectClass objectClass : schemaHandler.getObjectClasses() )
{
checkSchemaObjectNames( objectClass );
}
}
}
/**
* Checks the names of the given schema object.
*
* @param schemaObject the schema object to check
*/
private void checkSchemaObjectNames( SchemaObject schemaObject )
{
if ( ( schemaObject.getNames() == null ) || ( schemaObject.getNames().size() == 0 ) )
{
warningsList.add( new NoAliasWarning( schemaObject ) );
}
}
/**
* Indexes the warnings.
*/
private void indexWarnings()
{
for ( SchemaWarning warning : warningsList )
{
warningsMap.put( warning.getSource(), warning );
}
}
/**
* Gets the errors.
*
* @return
* the errors
*/
public List<Throwable> getErrors()
{
if ( schemaManager != null )
{
return schemaManager.getErrors();
}
else
{
return new ArrayList<Throwable>();
}
}
/**
* Gets the warnings.
*
* @return
* the warnings
*/
public List<SchemaWarning> getWarnings()
{
synchronized ( lock )
{
return warningsList;
}
}
/**
* Adds a SchemaCheckerListener.
*
* @param listener
* the listener
*/
public void addListener( SchemaCheckerListener listener )
{
if ( !listeners.contains( listener ) )
{
listeners.add( listener );
}
}
/**
* Removes a SchemaCheckerListener.
*
* @param listener
* the listener
*/
public void removeListener( SchemaCheckerListener listener )
{
listeners.remove( listener );
}
/**
* Notifies the listeners.
*/
private void notifyListeners()
{
for ( SchemaCheckerListener listener : listeners )
{
listener.schemaCheckerUpdated();
}
}
/**
* Gets the errors associated with the given Schema Object
*
* @param so
* the Schema Object
* @return
* the associated errors
*/
public List<?> getErrors( SchemaObject so )
{
synchronized ( lock )
{
return ( List<?> ) errorsMap.get( so );
}
}
/**
* Returns whether the given Schema Object has errors.
*
* @param so
* the Schema Object
* @return
* true if the given Schema Object has errors.
*/
public boolean hasErrors( SchemaObject so )
{
List<?> errors = getErrors( so );
if ( errors == null )
{
return false;
}
else
{
return errors.size() > 0;
}
}
/**
* Gets the warnings associated with the given Schema Object
*
* @param so
* the Schema Object
* @return
* the associated warnings
*/
@SuppressWarnings("unchecked")
public List<Object> getWarnings( SchemaObject so )
{
return ( List<Object> ) warningsMap.get( so );
}
/**
* Returns whether the given Schema Object has warnings.
*
* @param so
* the Schema Object
* @return
* true if the given Schema Object has errors.
*/
public boolean hasWarnings( SchemaObject so )
{
List<?> warnings = getWarnings( so );
if ( warnings == null )
{
return false;
}
else
{
return warnings.size() > 0;
}
}
}