Package org.apache.directory.ldapstudio.schemas.model

Source Code of org.apache.directory.ldapstudio.schemas.model.Schema

/*
*  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.ldapstudio.schemas.model;


import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Hashtable;

import org.apache.directory.ldapstudio.schemas.controller.Application;
import org.apache.directory.ldapstudio.schemas.io.SchemaParser;
import org.apache.directory.ldapstudio.schemas.io.SchemaWriter;
import org.apache.directory.ldapstudio.schemas.view.preferences.SchemaPreferencePage;
import org.apache.directory.server.core.tools.schema.AttributeTypeLiteral;
import org.apache.directory.server.core.tools.schema.ObjectClassLiteral;
import org.apache.log4j.Logger;
import org.eclipse.core.runtime.preferences.ConfigurationScope;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;


public class Schema implements SchemaElementListener
{
    private static Logger logger = Logger.getLogger( Schema.class );

    private String name;
    private URL url;
    //the schema elements stored in highly optimised and strongly typed
    //hash tables (stored by name).
    private Hashtable<String, ObjectClass> objectClassTable;
    private Hashtable<String, AttributeType> attributeTypeTable;
    private ArrayList<SchemaListener> listeners;
    //we change this to true if the schema has been modified since last save
    private boolean hasBeenModified = false;

    /**
     * coreSchema = a schema that can't be deleted, it's a core schema of the ApacheDS server
     * userSchema = any other schema
     */
    public enum SchemaType
    {
        userSchema, coreSchema
    }

    /**
     * The type of this schema
     */
    public SchemaType type;


    /******************************************
     *                 Utility                *
     ******************************************/

    /**
     * Gets the filename WITHOUT EXTENSION of the schema designated by this url
     * @param url url pointing to a .schema file
     * @return the filename or null if bad url
     */
    public static String URLtoFileName( URL url )
    {
        try
        {
            //It's time for sun to provide a standard method to access a file's name !
            String separator = "/"; //$NON-NLS-1$
            //if it's a ressource located in a bundle, we use slashes
            if ( url.getProtocol().equals( "bundleresource" ) ) //$NON-NLS-1$
                separator = "/"; //$NON-NLS-1$
            else
            {
                //if not, let's find this platform specific separator !
                separator = File.separator;

                //no wait ! If it's a backslash, the regex motor will explode. We
                //have to put not 2, not 3 but 4 backslashes !! That's right !
                if ( separator.equals( "\\" ) ) { //$NON-NLS-1$
                    separator = "\\\\"; //$NON-NLS-1$
                }
            }

            String path = url.getPath();
            String[] splFileName = path.split( separator );
            String fileNoPath = splFileName[splFileName.length - 1];

            if ( fileNoPath.endsWith( ".schema" ) ) //$NON-NLS-1$
            {
                String[] fileName = fileNoPath.split( "\\." ); //$NON-NLS-1$
                return fileName[0];
            }
        }
        catch ( Exception e )
        {
            logger.debug( "error when converting " + url + " to filename" ); //$NON-NLS-1$ //$NON-NLS-2$
        }
        return null;
    }


    /**
     * Converts a local path in a URL object
     * @param path the local path
     * @return the url corresponding to the path
     * @throws SchemaCreationException if error with the given path
     */
    public static URL localPathToURL( String path ) throws SchemaCreationException
    {
        URL tempURL = null;
        try
        {
            tempURL = new URL( "file", "localhost", -1, path ); //$NON-NLS-1$ //$NON-NLS-2$
        }
        catch ( MalformedURLException e )
        {
            throw new SchemaCreationException( "malformed path:" + path, e ); //$NON-NLS-1$
        }
        return tempURL;

    }


    /**
     * Tests if a given file is a schema file, it does not read the content of the schema
     * but rather tries to determine the extension of the file
     * @param url the input file
     * @return returns true if its a .schema file, if not it returns false
     */
    private static boolean isASchemaFile( URL url )
    {
        return URLtoFileName( url ) != null;
    }


    /******************************************
     *              Constructors              *
     ******************************************/

    /**
     * Creates a new user schema of the specified name
     * @param name the name of the schema
     */
    public Schema( String name )
    {
        this( name, null, SchemaType.userSchema );
    }


    /**
     * Creates a new schema of the specified name and type
     * @param type the type of the schema
     * @param name the name of the schema
     */
    public Schema( SchemaType type, String name )
    {
        this( name, null, type );
    }


    /**
     * Creates a new schema object loaded from a .schema file
     * @param path the path to the .schema file
     * @param type the type of the schema
     * @throws SchemaCreationException if error during parsing of the .schema file
     */
    public Schema( String path, SchemaType type ) throws SchemaCreationException
    {
        this( localPathToURL( path ), type );
    }


    /**
     * Creates a new schema object loaded from a .schema file
     * @param url the URL to the .schema file
     * @param type the type of the schema
     * @throws SchemaCreationException if error during parsing of the .schema file
     */
    public Schema( URL url, SchemaType type ) throws SchemaCreationException
    {
        this( URLtoFileName( url ), url, type );

        //we only load .schema files
        if ( !isASchemaFile( url ) )
            throw new SchemaCreationException( "not a .schema file: " + url, null ); //$NON-NLS-1$

        //launch parsing of the .schema file right now
        try
        {
            read();
        }
        catch ( IOException e )
        {
            throw new SchemaCreationException( "error opening " + url.toString(), e ); //$NON-NLS-1$
        }
        catch ( ParseException e )
        {
            throw new SchemaCreationException( "error during parsing of " + url.toString(), e ); //$NON-NLS-1$
        }
    }


    /**
     * General constructor (called by all the other constructors, ensure correct
     * initialization of all the instance variables)
     * @param name the name of the schema
     * @param url the url of the schema
     * @param type the type of the schema
     */
    private Schema( String name, URL url, SchemaType type )
    {
        this.name = name;
        this.url = url;
        this.type = type;

        objectClassTable = new Hashtable<String, ObjectClass>();
        attributeTypeTable = new Hashtable<String, AttributeType>();
        listeners = new ArrayList<SchemaListener>();

        //we created a new schema, so it has to be saved later on
        this.modified();
    }


    /******************************************
     *               Accessors                *
     ******************************************/

    /**
     * @return the name of the schema
     */
    public String getName()
    {
        return name;
    }


    /**
     * @return the url of the schema if it has been specified
     */
    public URL getURL()
    {
        return url;
    }


    /**
     * Accessor to the objectClasses defined by this schema
     * @return as an (name, objectClass) hashtable
     */
    public Hashtable<String, ObjectClass> getObjectClassesAsHashTable()
    {
        return objectClassTable;
    }


    /**
     * Accessor to the attributeTypes defined by this schema
     * @return as an (name, attributeType) hashtable
     */
    public Hashtable<String, AttributeType> getAttributeTypesAsHashTable()
    {
        return attributeTypeTable;
    }


    /**
     * Accessor to the objectClasses defined by this schema
     * @return as an array
     */
    public ObjectClass[] getObjectClassesAsArray()
    {
        return objectClassTable.values().toArray( new ObjectClass[]
            {} );
    }


    /**
     * Accessor to the attributeTypes defined by this schema
     * @return as an array
     */
    public AttributeType[] getAttributeTypesAsArray()
    {
        return attributeTypeTable.values().toArray( new AttributeType[]
            {} );
    }


    /******************************************
     *                  Logic                 *
     ******************************************/

    /**
     * Checks if the schema has been modified since last save
     * @return true if modified
     */
    public boolean hasBeenModified()
    {
        return hasBeenModified;
    }


    /**
     * Checks if the schema has pending modifications in editors that have not
     * been already applied
     * @return true if the schema has pending modifications
     */
    public boolean hasPendingModification()
    {
        ObjectClass[] OCs = getObjectClassesAsArray();
        for ( ObjectClass objectClass : OCs )
        {
            if ( objectClass.hasPendingModifications() )
            {
                return true;
            }
        }

        AttributeType[] ATs = getAttributeTypesAsArray();
        for ( AttributeType attributeType : ATs )
        {
            if ( attributeType.hasPendingModifications() )
            {
                return true;
            }
        }
        return false;
    }


    /**
     * Apply the pending modifications to the model (this instance)
     */
    public void applyPendingModifications()
    {
        ObjectClass[] OCs = getObjectClassesAsArray();
        for ( ObjectClass objectClass : OCs )
        {
            if ( objectClass.hasPendingModifications() )
            {
                objectClass.applyPendingModifications();
            }
        }

        AttributeType[] ATs = getAttributeTypesAsArray();
        for ( AttributeType attributeType : ATs )
        {
            if ( attributeType.hasPendingModifications() )
            {
                attributeType.applyPendingModifications();
            }
        }
    }


    /**
     * Close the editors associated to this schema WITHOUT applying the
     * modifications
     */
    public void closeAssociatedEditors()
    {
        ObjectClass[] OCs = getObjectClassesAsArray();
        for ( ObjectClass objectClass : OCs )
        {
            objectClass.closeAssociatedEditor();
        }

        AttributeType[] ATs = getAttributeTypesAsArray();
        for ( AttributeType attributeType : ATs )
        {
            attributeType.closeAssociatedEditor();
        }
    }


    /**
     * Use this method to indicate that the schema has been modified.
     * Surgeons warning: internal use only
     */
    private void modified()
    {
        this.hasBeenModified = true;
    }


    /**
     * Use this method to indicate that the schema has been saved.
     * Surgeons warning: internal use only
     */
    private void saved()
    {
        this.hasBeenModified = false;
    }


    /**
     * Adds the specified objectClass to the schema (overwriting the previous associations)
     * @param oc the objectClass
     */
    public void addObjectClass( ObjectClass oc )
    {
        for ( String alias : oc.getNames() )
            objectClassTable.put( alias, oc );
        oc.addListener( this );
        this.modified();
        notifyChanged( LDAPModelEvent.Reason.OCAdded, oc );
    }


    /**
     * Adds the specified attributeType to the schema (overwriting the previous associations)
     * @param at the attributeType
     */
    public void addAttributeType( AttributeType at )
    {
        for ( String alias : at.getNames() )
            attributeTypeTable.put( alias, at );
        at.addListener( this );
        this.modified();
        notifyChanged( LDAPModelEvent.Reason.ATAdded, at );
    }


    /**
     * Removes the specified objectClass from the schema
     * @param oc the objectClass
     */
    public void removeObjectClass( ObjectClass oc )
    {
        for ( String alias : oc.getNames() )
            objectClassTable.remove( alias );
        oc.removeListener( this );
        this.modified();
        notifyChanged( LDAPModelEvent.Reason.OCRemoved, oc );
    }


    /**
     * Removes the specified attributeType from the schema
     * @param at the attributeType
     */
    public void removeAttributeType( AttributeType at )
    {
        for ( String alias : at.getNames() )
            attributeTypeTable.remove( alias );
        at.removeListener( this );
        this.modified();
        notifyChanged( LDAPModelEvent.Reason.ATRemoved, at );
    }


    /******************************************
     *                  I/O                   *
     ******************************************/

    /**
     * Read the schema from the already specified .schema file
     * @throws ParseException if error during parsing of the .schema file
     * @throws IOException if error opening the .schema file
     *
     */
    public void read() throws IOException, ParseException
    {
        SchemaParser parser = null;
        parser = SchemaParser.parserFromURL( url );

        if ( parser == null )
            throw new FileNotFoundException( "Schema model object: no path or url specified !" ); //$NON-NLS-1$

        parser.parse();

        ObjectClassLiteral[] objectClasses = parser.getObjectClasses();
        AttributeTypeLiteral[] attributeTypes = parser.getAttributeTypes();

        for ( AttributeTypeLiteral literal : attributeTypes )
        {
            AttributeType AT = new AttributeType( literal, this );
            AT.addListener( this );
            for ( String alias : literal.getNames() )
                attributeTypeTable.put( alias, AT );
        }

        for ( ObjectClassLiteral literal : objectClasses )
        {
            ObjectClass OC = new ObjectClass( literal, this );
            OC.addListener( this );
            for ( String alias : literal.getNames() )
                objectClassTable.put( alias, OC );
        }

        //this schema has been loaded from a file, so we can consider we are
        //synchronised with the filesystem
        this.saved();
    }


    /**
     * Save the schema on disk, do not ask for confirmation
     * @throws Exception if error during writting of the file
     */
    public void save() throws Exception
    {
        save( false );
    }


    /**
     * Save the schema on disk
     * @param askForConfirmation if true, will ask form confirmation before saving
     * @throws Exception if error during writting of the file
     */
    public void save( boolean askForConfirmation ) throws Exception
    {
        if ( this.type == SchemaType.coreSchema )
            return;

        if ( this.hasPendingModification() )
        {
            MessageBox messageBox = new MessageBox( PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
                SWT.YES | SWT.NO | SWT.ICON_QUESTION );
            messageBox
                .setMessage( Messages.getString( "Schema.The_schema" ) + this.getName() + Messages.getString( "Schema.Has_pending_modifications_in_editors_Do_you_want_to_apply_them" ) ); //$NON-NLS-1$ //$NON-NLS-2$
            if ( messageBox.open() == SWT.YES )
            {
                this.applyPendingModifications();
            }
            else
            {
                this.closeAssociatedEditors();
            }
        }

        if ( !this.hasBeenModified() )
            return;

        if ( askForConfirmation )
        {
            MessageBox messageBox = new MessageBox( new Shell(), SWT.YES | SWT.NO | SWT.ICON_QUESTION );
            messageBox
                .setMessage( Messages.getString( "Schema.The_schema" ) + this.getName() + Messages.getString( "Schema.Has_been_modified_Do_you_want_to_save_it" ) ); //$NON-NLS-1$ //$NON-NLS-2$
            if ( messageBox.open() != SWT.YES )
            {
                return;
            }
        }

        String savePath = null;

        if ( this.url == null )
        {
            FileDialog fd = new FileDialog( new Shell(), SWT.SAVE );
            fd.setText( Messages.getString( "Schema.Save_this_schema" ) + this.getName() ); //$NON-NLS-1$
            IEclipsePreferences prefs = new ConfigurationScope().getNode( Application.PLUGIN_ID );
            String defaultPath = prefs.get( SchemaPreferencePage.DEFAULT_DIRECTORY, System.getProperty( "user.home" ) ); //$NON-NLS-1$
            fd.setFilterPath( defaultPath );
            fd.setFileName( this.name + ".schema" ); //$NON-NLS-1$
            fd.setFilterExtensions( new String[]
                { "*.schema", "*.*" } ); //$NON-NLS-1$ //$NON-NLS-2$
            fd.setFilterNames( new String[]
                { Messages.getString( "Schema.Schema_files" ), Messages.getString( "Schema.All_files" ) } ); //$NON-NLS-1$ //$NON-NLS-2$
            savePath = fd.open();
            //we now have a specific location to save this schema in the future
            if ( savePath != null )
                this.url = localPathToURL( savePath );
        }
        else
            savePath = url.getPath();

        if ( savePath != null )
        {
            write( savePath );
            //when we have been written, we are synchronised with the filesystem
            this.saved();
            notifyChanged( LDAPModelEvent.Reason.SchemaSaved, null );
        }
    }


    /**
     * Save the schema on disk to a specific file
     * @throws Exception if error during writting of the file
     */
    public void saveas() throws Exception
    {
        if ( this.hasPendingModification() )
        {
            MessageBox messageBox = new MessageBox( PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
                SWT.YES | SWT.NO | SWT.ICON_QUESTION );
            messageBox
                .setMessage( Messages.getString( "Schema.The_schema" ) + this.getName() + Messages.getString( "Schema.Has_pending_modifications_in_editors_Do_you_want_to_apply_them" ) ); //$NON-NLS-1$ //$NON-NLS-2$
            if ( messageBox.open() == SWT.YES )
            {
                this.applyPendingModifications();
            }
        }

        FileDialog fd = new FileDialog( PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), SWT.SAVE );
        fd.setText( Messages.getString( "Schema.Save_this_schema" ) + this.getName() ); //$NON-NLS-1$
        IEclipsePreferences prefs = new ConfigurationScope().getNode( Application.PLUGIN_ID );
        String defaultPath = prefs.get( SchemaPreferencePage.DEFAULT_DIRECTORY, System.getProperty( "user.home" ) ); //$NON-NLS-1$
        fd.setFilterPath( defaultPath );
        fd.setFileName( this.name + ".schema" ); //$NON-NLS-1$
        fd.setFilterExtensions( new String[]
            { "*.schema", "*.*" } ); //$NON-NLS-1$ //$NON-NLS-2$
        fd.setFilterNames( new String[]
            { Messages.getString( "Schema.Schema_files" ), Messages.getString( "Schema.All_files" ) } ); //$NON-NLS-1$ //$NON-NLS-2$
        String savePath = fd.open();
        //check if cancel has been pressed
        if ( savePath != null )
        {
            URL newURL = localPathToURL( savePath );
            String newName = URLtoFileName( newURL );
            //if it's a bad url (no .schema, bad path) newName will be null
            if ( newName != null )
            {

                if ( SchemaPool.getInstance().getSchema( newName ) != null )
                {
                    MessageBox messageBox = new MessageBox( PlatformUI.getWorkbench().getActiveWorkbenchWindow()
                        .getShell(), SWT.OK | SWT.ICON_ERROR );
                    messageBox.setMessage( Messages
                        .getString( "Schema.A_schema_of_the_same_name_is_already_loaded_in_the_pool" ) ); //$NON-NLS-1$
                    messageBox.open();
                    return;
                }
                //if everything is ok, we update the current instance
                this.name = newName;
                this.url = newURL;

                write( savePath );
                //when we have been written, we are synchronised with the filesystem
                this.saved();
                notifyChanged( LDAPModelEvent.Reason.SchemaSaved, null );
            }
        }
    }


    private void write( String path ) throws Exception
    {
        if ( path != null && path != "" ) { //$NON-NLS-1$
            SchemaWriter writer = new SchemaWriter();
            writer.write( this, path );
        }
    }


    /******************************************
     *            Events emmiting             *
     ******************************************/

    public void addListener( SchemaListener listener )
    {
        if ( !listeners.contains( listener ) )
            listeners.add( listener );
    }


    public void removeListener( SchemaListener listener )
    {
        listeners.remove( listener );
    }


    private void notifyChanged( LDAPModelEvent.Reason reason, Object o )
    {
        for ( SchemaListener listener : listeners )
        {
            try
            {
                if ( o instanceof ObjectClass )
                    listener.schemaChanged( this, new LDAPModelEvent( reason, ( ObjectClass ) o ) );
                else if ( o instanceof AttributeType )
                    listener.schemaChanged( this, new LDAPModelEvent( reason, ( AttributeType ) o ) );
                else
                    listener.schemaChanged( this, new LDAPModelEvent( reason ) );
            }
            catch ( Exception e )
            {
                logger.debug( "error when notifying listener: " + listener ); //$NON-NLS-1$
            }
        }
    }


    /******************************************
     *     Schema Element Listener Impl       *
     ******************************************/

    public void schemaElementChanged( SchemaElement originatingSchemaElement, LDAPModelEvent e )
    {
        //if a binded element has been modified we consider that we have been modified
        this.modified();

        //then we notify our listeners that we have been modified
        for ( SchemaListener listener : listeners )
        {
            listener.schemaChanged( this, e );
        }
    }
}
TOP

Related Classes of org.apache.directory.ldapstudio.schemas.model.Schema

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.