KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > directory > ldapstudio > schemas > model > Schema


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  *
19  */

20
21 package org.apache.directory.ldapstudio.schemas.model;
22
23
24 import java.io.File JavaDoc;
25 import java.io.FileNotFoundException JavaDoc;
26 import java.io.IOException JavaDoc;
27 import java.net.MalformedURLException JavaDoc;
28 import java.net.URL JavaDoc;
29 import java.text.ParseException JavaDoc;
30 import java.util.ArrayList JavaDoc;
31 import java.util.HashSet JavaDoc;
32 import java.util.Hashtable JavaDoc;
33 import java.util.Set JavaDoc;
34
35 import org.apache.directory.ldapstudio.schemas.Activator;
36 import org.apache.directory.ldapstudio.schemas.Messages;
37 import org.apache.directory.ldapstudio.schemas.PluginConstants;
38 import org.apache.directory.ldapstudio.schemas.io.SchemaParser;
39 import org.apache.directory.ldapstudio.schemas.io.SchemaWriter;
40 import org.apache.directory.server.core.tools.schema.AttributeTypeLiteral;
41 import org.apache.directory.server.core.tools.schema.ObjectClassLiteral;
42 import org.apache.log4j.Logger;
43 import org.eclipse.swt.SWT;
44 import org.eclipse.swt.widgets.FileDialog;
45 import org.eclipse.swt.widgets.MessageBox;
46 import org.eclipse.swt.widgets.Shell;
47 import org.eclipse.ui.PlatformUI;
48
49
50 public class Schema implements SchemaElementListener
51 {
52     private static Logger logger = Logger.getLogger( Schema.class );
53
54     private String JavaDoc name;
55     private URL JavaDoc url;
56     //the schema elements stored in highly optimised and strongly typed
57
//hash tables (stored by name).
58
private Hashtable JavaDoc<String JavaDoc, ObjectClass> objectClassTable;
59     private Hashtable JavaDoc<String JavaDoc, AttributeType> attributeTypeTable;
60     private ArrayList JavaDoc<SchemaListener> listeners;
61     //we change this to true if the schema has been modified since last save
62
private boolean hasBeenModified = false;
63
64     /**
65      * coreSchema = a schema that can't be deleted, it's a core schema of the ApacheDS server
66      * userSchema = any other schema
67      */

68     public enum SchemaType
69     {
70         userSchema, coreSchema
71     }
72
73     /**
74      * The type of this schema
75      */

76     public SchemaType type;
77
78
79     /******************************************
80      * Utility *
81      ******************************************/

82
83     /**
84      * Gets the filename WITHOUT EXTENSION of the schema designated by this url
85      * @param url url pointing to a .schema file
86      * @return the filename or null if bad url
87      */

88     public static String JavaDoc URLtoFileName( URL JavaDoc url )
89     {
90         try
91         {
92             //It's time for sun to provide a standard method to access a file's name !
93
String JavaDoc separator = "/"; //$NON-NLS-1$
94
//if it's a ressource located in a bundle, we use slashes
95
if ( url.getProtocol().equals( "bundleresource" ) ) //$NON-NLS-1$
96
separator = "/"; //$NON-NLS-1$
97
else
98             {
99                 //if not, let's find this platform specific separator !
100
separator = File.separator;
101
102                 //no wait ! If it's a backslash, the regex motor will explode. We
103
//have to put not 2, not 3 but 4 backslashes !! That's right !
104
if ( separator.equals( "\\" ) ) { //$NON-NLS-1$
105
separator = "\\\\"; //$NON-NLS-1$
106
}
107             }
108
109             String JavaDoc path = url.getPath();
110             String JavaDoc[] splFileName = path.split( separator );
111             String JavaDoc fileNoPath = splFileName[splFileName.length - 1];
112
113             if ( fileNoPath.endsWith( ".schema" ) ) //$NON-NLS-1$
114
{
115                 String JavaDoc[] fileName = fileNoPath.split( "\\." ); //$NON-NLS-1$
116
return fileName[0];
117             }
118         }
119         catch ( Exception JavaDoc e )
120         {
121             logger.debug( "error when converting " + url + " to filename" ); //$NON-NLS-1$ //$NON-NLS-2$
122
}
123         return null;
124     }
125
126
127     /**
128      * Converts a local path in a URL object
129      * @param path the local path
130      * @return the url corresponding to the path
131      * @throws SchemaCreationException if error with the given path
132      */

133     public static URL JavaDoc localPathToURL( String JavaDoc path ) throws SchemaCreationException
134     {
135         URL JavaDoc tempURL = null;
136         try
137         {
138             tempURL = new URL JavaDoc( "file", "localhost", -1, path ); //$NON-NLS-1$ //$NON-NLS-2$
139
}
140         catch ( MalformedURLException JavaDoc e )
141         {
142             throw new SchemaCreationException( "malformed path:" + path, e ); //$NON-NLS-1$
143
}
144         return tempURL;
145
146     }
147
148
149     /**
150      * Tests if a given file is a schema file, it does not read the content of the schema
151      * but rather tries to determine the extension of the file
152      * @param url the input file
153      * @return returns true if its a .schema file, if not it returns false
154      */

155     private static boolean isASchemaFile( URL JavaDoc url )
156     {
157         return URLtoFileName( url ) != null;
158     }
159
160
161     /******************************************
162      * Constructors *
163      ******************************************/

164
165     /**
166      * Creates a new user schema of the specified name
167      * @param name the name of the schema
168      */

169     public Schema( String JavaDoc name )
170     {
171         this( name, null, SchemaType.userSchema );
172     }
173
174
175     /**
176      * Creates a new schema of the specified name and type
177      * @param type the type of the schema
178      * @param name the name of the schema
179      */

180     public Schema( SchemaType type, String JavaDoc name )
181     {
182         this( name, null, type );
183     }
184
185
186     /**
187      * Creates a new schema object loaded from a .schema file
188      * @param path the path to the .schema file
189      * @param type the type of the schema
190      * @throws SchemaCreationException if error during parsing of the .schema file
191      */

192     public Schema( String JavaDoc path, SchemaType type ) throws SchemaCreationException
193     {
194         this( localPathToURL( path ), type );
195     }
196
197
198     /**
199      * Creates a new schema object loaded from a .schema file
200      * @param url the URL to the .schema file
201      * @param type the type of the schema
202      * @throws SchemaCreationException if error during parsing of the .schema file
203      */

204     public Schema( URL JavaDoc url, SchemaType type ) throws SchemaCreationException
205     {
206         this( URLtoFileName( url ), url, type );
207
208         //we only load .schema files
209
if ( !isASchemaFile( url ) )
210             throw new SchemaCreationException( "not a .schema file: " + url, null ); //$NON-NLS-1$
211

212         //launch parsing of the .schema file right now
213
try
214         {
215             read();
216         }
217         catch ( IOException JavaDoc e )
218         {
219             throw new SchemaCreationException( "error opening " + url.toString(), e ); //$NON-NLS-1$
220
}
221         catch ( ParseException JavaDoc e )
222         {
223             throw new SchemaCreationException( "error during parsing of " + url.toString(), e ); //$NON-NLS-1$
224
}
225     }
226
227
228     /**
229      * General constructor (called by all the other constructors, ensure correct
230      * initialization of all the instance variables)
231      * @param name the name of the schema
232      * @param url the url of the schema
233      * @param type the type of the schema
234      */

235     private Schema( String JavaDoc name, URL JavaDoc url, SchemaType type )
236     {
237         this.name = name;
238         this.url = url;
239         this.type = type;
240
241         objectClassTable = new Hashtable JavaDoc<String JavaDoc, ObjectClass>();
242         attributeTypeTable = new Hashtable JavaDoc<String JavaDoc, AttributeType>();
243         listeners = new ArrayList JavaDoc<SchemaListener>();
244
245         //we created a new schema, so it has to be saved later on
246
this.modified();
247     }
248
249
250     /******************************************
251      * Accessors *
252      ******************************************/

253
254     /**
255      * @return the name of the schema
256      */

257     public String JavaDoc getName()
258     {
259         return name;
260     }
261
262
263     /**
264      * @return the url of the schema if it has been specified
265      */

266     public URL JavaDoc getURL()
267     {
268         return url;
269     }
270
271
272     /**
273      * Accessor to the objectClasses defined by this schema
274      * @return as an (name, objectClass) hashtable
275      */

276     public Hashtable JavaDoc<String JavaDoc, ObjectClass> getObjectClassesAsHashTable()
277     {
278         return objectClassTable;
279     }
280
281
282     /**
283      * Accessor to the attributeTypes defined by this schema
284      * @return as an (name, attributeType) hashtable
285      */

286     public Hashtable JavaDoc<String JavaDoc, AttributeType> getAttributeTypesAsHashTable()
287     {
288         return attributeTypeTable;
289     }
290
291
292     /**
293      * Accessor to the objectClasses defined by this schema
294      * @return as an array
295      */

296     public ObjectClass[] getObjectClassesAsArray()
297     {
298         Set JavaDoc<ObjectClass> set = new HashSet JavaDoc<ObjectClass>();
299         set.addAll( objectClassTable.values() );
300         return set.toArray( new ObjectClass[0] );
301     }
302
303
304     /**
305      * Accessor to the attributeTypes defined by this schema
306      * @return as an array
307      */

308     public AttributeType[] getAttributeTypesAsArray()
309     {
310         Set JavaDoc<AttributeType> set = new HashSet JavaDoc<AttributeType>();
311         set.addAll( attributeTypeTable.values() );
312         return set.toArray( new AttributeType[0] );
313     }
314
315
316     /******************************************
317      * Logic *
318      ******************************************/

319
320     /**
321      * Checks if the schema has been modified since last save
322      * @return true if modified
323      */

324     public boolean hasBeenModified()
325     {
326         return hasBeenModified;
327     }
328
329
330     /**
331      * Checks if the schema has pending modifications in editors that have not
332      * been already applied
333      * @return true if the schema has pending modifications
334      */

335     public boolean hasPendingModification()
336     {
337         ObjectClass[] OCs = getObjectClassesAsArray();
338         for ( ObjectClass objectClass : OCs )
339         {
340             if ( objectClass.hasPendingModifications() )
341             {
342                 return true;
343             }
344         }
345
346         AttributeType[] ATs = getAttributeTypesAsArray();
347         for ( AttributeType attributeType : ATs )
348         {
349             if ( attributeType.hasPendingModifications() )
350             {
351                 return true;
352             }
353         }
354         return false;
355     }
356
357
358     /**
359      * Apply the pending modifications to the model (this instance)
360      */

361     public void applyPendingModifications()
362     {
363         ObjectClass[] OCs = getObjectClassesAsArray();
364         for ( ObjectClass objectClass : OCs )
365         {
366             if ( objectClass.hasPendingModifications() )
367             {
368                 objectClass.applyPendingModifications();
369             }
370         }
371
372         AttributeType[] ATs = getAttributeTypesAsArray();
373         for ( AttributeType attributeType : ATs )
374         {
375             if ( attributeType.hasPendingModifications() )
376             {
377                 attributeType.applyPendingModifications();
378             }
379         }
380     }
381
382
383     /**
384      * Close the editors associated to this schema WITHOUT applying the
385      * modifications
386      */

387     public void closeAssociatedEditors()
388     {
389         ObjectClass[] OCs = getObjectClassesAsArray();
390         for ( ObjectClass objectClass : OCs )
391         {
392             objectClass.closeAssociatedEditor();
393         }
394
395         AttributeType[] ATs = getAttributeTypesAsArray();
396         for ( AttributeType attributeType : ATs )
397         {
398             attributeType.closeAssociatedEditor();
399         }
400     }
401
402
403     /**
404      * Use this method to indicate that the schema has been modified.
405      * Surgeons warning: internal use only
406      */

407     private void modified()
408     {
409         this.hasBeenModified = true;
410     }
411
412
413     /**
414      * Use this method to indicate that the schema has been saved.
415      * Surgeons warning: internal use only
416      */

417     private void saved()
418     {
419         this.hasBeenModified = false;
420     }
421
422
423     /**
424      * Adds the specified objectClass to the schema (overwriting the previous associations)
425      * @param oc the objectClass
426      */

427     public void addObjectClass( ObjectClass oc )
428     {
429         for ( String JavaDoc alias : oc.getNames() )
430             objectClassTable.put( alias, oc );
431         oc.addListener( this );
432         this.modified();
433         notifyChanged( LDAPModelEvent.Reason.OCAdded, null, oc );
434     }
435
436
437     /**
438      * Adds the specified attributeType to the schema (overwriting the previous associations)
439      * @param at the attributeType
440      */

441     public void addAttributeType( AttributeType at )
442     {
443         for ( String JavaDoc alias : at.getNames() )
444             attributeTypeTable.put( alias, at );
445         at.addListener( this );
446         this.modified();
447         notifyChanged( LDAPModelEvent.Reason.ATAdded, null, at );
448     }
449
450
451     /**
452      * Removes the specified objectClass from the schema
453      * @param oc the objectClass
454      */

455     public void removeObjectClass( ObjectClass oc )
456     {
457         for ( String JavaDoc alias : oc.getNames() )
458             objectClassTable.remove( alias );
459         oc.removeListener( this );
460         this.modified();
461         notifyChanged( LDAPModelEvent.Reason.OCRemoved, oc, null );
462     }
463
464
465     /**
466      * Removes the specified attributeType from the schema
467      * @param at the attributeType
468      */

469     public void removeAttributeType( AttributeType at )
470     {
471         for ( String JavaDoc alias : at.getNames() )
472             attributeTypeTable.remove( alias );
473         at.removeListener( this );
474         this.modified();
475         notifyChanged( LDAPModelEvent.Reason.ATRemoved, at, null );
476     }
477
478
479     /******************************************
480      * I/O *
481      ******************************************/

482
483     /**
484      * Read the schema from the already specified .schema file
485      * @throws ParseException if error during parsing of the .schema file
486      * @throws IOException if error opening the .schema file
487      *
488      */

489     public void read() throws IOException JavaDoc, ParseException JavaDoc
490     {
491         SchemaParser parser = null;
492         parser = SchemaParser.parserFromURL( url );
493
494         if ( parser == null )
495             throw new FileNotFoundException JavaDoc( "Schema model object: no path or url specified !" ); //$NON-NLS-1$
496

497         parser.parse();
498
499         ObjectClassLiteral[] objectClasses = parser.getObjectClasses();
500         AttributeTypeLiteral[] attributeTypes = parser.getAttributeTypes();
501
502         for ( AttributeTypeLiteral literal : attributeTypes )
503         {
504             AttributeType AT = new AttributeType( literal, this );
505             AT.addListener( this );
506             for ( String JavaDoc alias : literal.getNames() )
507                 attributeTypeTable.put( alias, AT );
508         }
509
510         for ( ObjectClassLiteral literal : objectClasses )
511         {
512             ObjectClass OC = new ObjectClass( literal, this );
513             OC.addListener( this );
514             for ( String JavaDoc alias : literal.getNames() )
515                 objectClassTable.put( alias, OC );
516         }
517
518         //this schema has been loaded from a file, so we can consider we are
519
//synchronised with the filesystem
520
this.saved();
521     }
522
523
524     /**
525      * Save the schema on disk, do not ask for confirmation
526      * @throws Exception if error during writting of the file
527      */

528     public void save() throws Exception JavaDoc
529     {
530         save( false );
531     }
532
533
534     /**
535      * Save the schema on disk
536      * @param askForConfirmation if true, will ask form confirmation before saving
537      * @throws Exception if error during writting of the file
538      */

539     public void save( boolean askForConfirmation ) throws Exception JavaDoc
540     {
541         if ( this.type == SchemaType.coreSchema )
542             return;
543
544         if ( this.hasPendingModification() )
545         {
546             MessageBox messageBox = new MessageBox( PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
547                 SWT.YES | SWT.NO | SWT.ICON_QUESTION );
548             messageBox
549                 .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$
550
if ( messageBox.open() == SWT.YES )
551             {
552                 this.applyPendingModifications();
553             }
554             else
555             {
556                 this.closeAssociatedEditors();
557             }
558         }
559
560         if ( !this.hasBeenModified() )
561             return;
562
563         if ( askForConfirmation )
564         {
565             MessageBox messageBox = new MessageBox( new Shell(), SWT.YES | SWT.NO | SWT.ICON_QUESTION );
566             messageBox
567                 .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$
568
if ( messageBox.open() != SWT.YES )
569             {
570                 return;
571             }
572         }
573
574         String JavaDoc savePath = null;
575
576         if ( this.url == null )
577         {
578             FileDialog fd = new FileDialog( new Shell(), SWT.SAVE );
579             fd.setText( Messages.getString( "Schema.Save_this_schema" ) + this.getName() ); //$NON-NLS-1$
580
fd.setFilterPath( Activator.getDefault().getPreferenceStore().getString(
581                 PluginConstants.PREFS_SAVE_FILE_DIALOG ) );
582             fd.setFileName( this.name + ".schema" ); //$NON-NLS-1$
583
fd.setFilterExtensions( new String JavaDoc[]
584                 { "*.schema", "*.*" } ); //$NON-NLS-1$ //$NON-NLS-2$
585
fd.setFilterNames( new String JavaDoc[]
586                 { Messages.getString( "Schema.Schema_files" ), Messages.getString( "Schema.All_files" ) } ); //$NON-NLS-1$ //$NON-NLS-2$
587
savePath = fd.open();
588             //we now have a specific location to save this schema in the future
589
if ( savePath != null )
590                 this.url = localPathToURL( savePath );
591         }
592         else
593             savePath = url.getPath();
594
595         if ( savePath != null )
596         {
597             write( savePath );
598             //when we have been written, we are synchronised with the filesystem
599
this.saved();
600             notifyChanged( LDAPModelEvent.Reason.SchemaSaved, this, null );
601             Activator.getDefault().getPreferenceStore().putValue( PluginConstants.PREFS_SAVE_FILE_DIALOG,
602                 new File JavaDoc( savePath ).getParent() );
603         }
604     }
605
606
607     /**
608      * Save the schema on disk to a specific file
609      * @throws Exception if error during writting of the file
610      */

611     public void saveas() throws Exception JavaDoc
612     {
613         if ( this.hasPendingModification() )
614         {
615             MessageBox messageBox = new MessageBox( PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
616                 SWT.YES | SWT.NO | SWT.ICON_QUESTION );
617             messageBox
618                 .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$
619
if ( messageBox.open() == SWT.YES )
620             {
621                 this.applyPendingModifications();
622             }
623         }
624
625         FileDialog fd = new FileDialog( PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), SWT.SAVE );
626         fd.setText( Messages.getString( "Schema.Save_this_schema" ) + this.getName() ); //$NON-NLS-1$
627
fd.setFilterPath( Activator.getDefault().getPreferenceStore()
628             .getString( PluginConstants.PREFS_SAVE_FILE_DIALOG ) );
629         fd.setFileName( this.name + ".schema" ); //$NON-NLS-1$
630
fd.setFilterExtensions( new String JavaDoc[]
631             { "*.schema", "*.*" } ); //$NON-NLS-1$ //$NON-NLS-2$
632
fd.setFilterNames( new String JavaDoc[]
633             { Messages.getString( "Schema.Schema_files" ), Messages.getString( "Schema.All_files" ) } ); //$NON-NLS-1$ //$NON-NLS-2$
634
String JavaDoc savePath = fd.open();
635         //check if cancel has been pressed
636
if ( savePath != null )
637         {
638             URL JavaDoc newURL = localPathToURL( savePath );
639             String JavaDoc newName = URLtoFileName( newURL );
640             //if it's a bad url (no .schema, bad path) newName will be null
641
if ( newName != null )
642             {
643                 if ( SchemaPool.getInstance().getSchema( newName ) != null )
644                 {
645                     MessageBox messageBox = new MessageBox( PlatformUI.getWorkbench().getActiveWorkbenchWindow()
646                         .getShell(), SWT.OK | SWT.ICON_ERROR );
647                     messageBox.setMessage( Messages
648                         .getString( "Schema.A_schema_of_the_same_name_is_already_loaded_in_the_pool" ) ); //$NON-NLS-1$
649
messageBox.open();
650                     return;
651                 }
652                 //if everything is ok, we update the current instance
653
this.name = newName;
654                 this.url = newURL;
655
656                 write( savePath );
657                 //when we have been written, we are synchronised with the filesystem
658
this.saved();
659                 notifyChanged( LDAPModelEvent.Reason.SchemaSaved, this, null );
660                 Activator.getDefault().getPreferenceStore().putValue( PluginConstants.PREFS_SAVE_FILE_DIALOG,
661                     new File JavaDoc( newName ).getParent() );
662             }
663         }
664     }
665
666
667     private void write( String JavaDoc path ) throws Exception JavaDoc
668     {
669         if ( path != null && path != "" ) { //$NON-NLS-1$
670
SchemaWriter writer = new SchemaWriter();
671             writer.write( this, path );
672         }
673     }
674
675
676     /******************************************
677      * Events emmiting *
678      ******************************************/

679
680     public void addListener( SchemaListener listener )
681     {
682         if ( !listeners.contains( listener ) )
683             listeners.add( listener );
684     }
685
686
687     public void removeListener( SchemaListener listener )
688     {
689         listeners.remove( listener );
690     }
691
692
693     private void notifyChanged( LDAPModelEvent.Reason reason, Object JavaDoc oldValue, Object JavaDoc newValue )
694     {
695         for ( SchemaListener listener : listeners )
696         {
697             try
698             {
699                 if ( ( oldValue instanceof ObjectClass ) || ( newValue instanceof ObjectClass ) )
700                 {
701                     listener.schemaChanged( this, new LDAPModelEvent( reason, ( ObjectClass ) oldValue,
702                         ( ObjectClass ) newValue ) );
703                 }
704                 else if ( ( oldValue instanceof AttributeType ) || ( newValue instanceof AttributeType ) )
705                 {
706                     listener.schemaChanged( this, new LDAPModelEvent( reason, ( AttributeType ) oldValue,
707                         ( AttributeType ) newValue ) );
708                 }
709                 else
710                 {
711                     listener.schemaChanged( this, new LDAPModelEvent( reason ) );
712                 }
713             }
714             catch ( Exception JavaDoc e )
715             {
716                 logger.debug( "error when notifying listener: " + listener ); //$NON-NLS-1$
717
}
718         }
719     }
720
721
722     /******************************************
723      * Schema Element Listener Impl *
724      ******************************************/

725
726     public void schemaElementChanged( SchemaElement originatingSchemaElement, LDAPModelEvent e )
727     {
728         //if a binded element has been modified we consider that we have been modified
729
this.modified();
730
731         //then we notify our listeners that we have been modified
732
for ( SchemaListener listener : listeners )
733         {
734             listener.schemaChanged( this, e );
735         }
736     }
737 }
738
Popular Tags