PageRenderTime 122ms CodeModel.GetById 64ms app.highlight 50ms RepoModel.GetById 1ms app.codeStats 0ms

/bundles/plugins-trunk/XML/xml/SchemaMappingManager.java

#
Java | 706 lines | 500 code | 83 blank | 123 comment | 56 complexity | f36f691c2bfe157878a60fbb193e1fc5 MD5 | raw file
  1/*
  2 * SchemaMappingManager.java - select a schema for validation
  3 * :tabSize=4:indentSize=4:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 2009, Eric Le Lay
  7 *
  8 * The XML plugin is licensed under the GNU General Public License, with
  9 * the following exception:
 10 *
 11 * "Permission is granted to link this code with software released under
 12 * the Apache license version 1.1, for example used by the Xerces XML
 13 * parser package."
 14 */
 15package xml;
 16
 17//{{{ Imports
 18import java.io.File;
 19import java.io.IOException;
 20import java.net.URI;
 21import java.net.MalformedURLException;
 22import java.net.URISyntaxException;
 23
 24import javax.swing.JOptionPane;
 25import javax.swing.JCheckBox;
 26import javax.swing.JTextField;
 27import javax.swing.JPanel;
 28import javax.swing.JLabel;
 29import javax.swing.JButton;
 30import javax.swing.BorderFactory;
 31import java.awt.Color;
 32
 33import java.awt.event.ActionEvent;
 34import java.awt.event.KeyEvent;
 35import java.awt.event.ActionListener;
 36
 37import java.util.Map;
 38import java.util.HashMap;
 39import java.util.Arrays;
 40import java.util.regex.Pattern;
 41
 42import org.gjt.sp.jedit.View;
 43import org.gjt.sp.jedit.Buffer;
 44import org.gjt.sp.jedit.jEdit;
 45import org.gjt.sp.jedit.browser.VFSBrowser;
 46import org.gjt.sp.jedit.MiscUtilities;
 47import org.gjt.sp.jedit.GUIUtilities;
 48import org.gjt.sp.util.Log;
 49import org.gjt.sp.jedit.gui.EnhancedDialog;
 50import org.gjt.sp.jedit.gui.HistoryTextField;
 51import org.xml.sax.ErrorHandler;
 52import org.xml.sax.SAXException;
 53import org.xml.sax.SAXParseException;
 54
 55import common.gui.OkCancelButtons;
 56import ise.java.awt.KappaLayout;
 57
 58import xml.parser.SchemaMapping;
 59import static xml.PathUtilities.*;
 60//}}}
 61
 62public final class SchemaMappingManager
 63{
 64	public static final String SCHEMA_MAPPING_PROP = "xml.schema-mapping";
 65	public static final String BUFFER_ENABLE_SCHEMA_MAPPING_PROP = "xml.enable-schema-mapping";
 66	public static final String ENABLE_SCHEMA_MAPPING_PROP = "buffer."+BUFFER_ENABLE_SCHEMA_MAPPING_PROP;
 67	public static final String BUFFER_SCHEMA_PROP = "xml.validation.schema";
 68	public static final String BUFFER_AUTO_SCHEMA_PROP = "xml.validation.schema-auto";
 69	
 70	private static final String BUILT_IN_SCHEMA = "xml/dtds/schemas.xml";
 71	
 72	// {{{ singleton constructor
 73	private SchemaMappingManager(){}
 74	// }}}
 75	
 76	//{{{ promptSchemaForBuffer() method
 77	/**
 78	 * - let the user choose a schema file from the VFSBrowser
 79	 */
 80	public static void promptSchemaForBuffer(View view, Buffer buffer)
 81	{
 82		String oldSchema = buffer.getStringProperty(BUFFER_SCHEMA_PROP);
 83
 84		String specificSchema = null;
 85		SchemaMapping lMapping = null;
 86		SchemaMapping gMapping = null;
 87		
 88		boolean schemasEnabled = isSchemaMappingEnabled(buffer);
 89		
 90		// local schema-mapping
 91		if(schemasEnabled){
 92			specificSchema = MiscUtilities.constructPath(
 93				MiscUtilities.getParentOfPath(
 94					buffer.getPath()),SchemaMapping.SCHEMAS_FILE);
 95		
 96			lMapping = getLocalSchemaMapping(buffer, new LoggingErrorHandler(jEdit.getProperty("xml.local-schema")));
 97			gMapping = getGlobalSchemaMapping(new LoggingErrorHandler(jEdit.getProperty("xml.global-schema")));
 98		
 99			// always prefer the explicit schema
100			String oldSchemaAuto = buffer.getStringProperty(BUFFER_AUTO_SCHEMA_PROP);
101			if(oldSchema == null)oldSchema = oldSchemaAuto;
102		}
103		
104		
105		ChooseSchemaDialog choose = new ChooseSchemaDialog(view);
106		
107        if(choose.choose(buffer.getPath(),urlToPath(oldSchema), true))
108        {
109			String bufferURL = pathToURL(buffer.getPath());
110			URI schemaURL = choose.schemaURL;
111
112        	if(schemasEnabled)
113        	{
114				// no schemas.xml in the buffer's directory : will create one
115				if(lMapping == null)
116				{
117					try
118					{
119						lMapping = new SchemaMapping(new URI(pathToURL(specificSchema)));
120						if(gMapping != null)lMapping.ensureIncluded(gMapping);
121					}
122					catch(URISyntaxException ue)
123					{
124						Log.log(Log.ERROR,SchemaMappingManager.class,ue);
125					}
126				}
127				
128				
129				SchemaMapping.URIResourceRule newRule = new SchemaMapping.URIResourceRule(null,bufferURL,schemaURL.toString(), false);
130				
131				lMapping.updateMapping(newRule);
132				
133				try
134				{
135					lMapping.toDocument(lMapping.getBaseURI().toString());
136				}
137				catch(IOException ioe)
138				{
139					// if saving fails, try to output it in a buffer
140					JOptionPane.showMessageDialog(
141						view,
142						jEdit.getProperty("xml-error-to-document.message",new Object[]{ioe.getClass(),ioe.getMessage()}),
143						jEdit.getProperty("xml-error-to-document.title"),
144						JOptionPane.ERROR_MESSAGE
145						);
146					Buffer b = jEdit.newFile(view,lMapping.getBaseURI().toString());
147					b.insert(0,lMapping.toString());
148					view.getEditPane().setBuffer(buffer);
149				}
150			}
151			else
152			{
153				buffer.setProperty(BUFFER_SCHEMA_PROP,schemaURL);
154			}
155			
156			// finally, use the new schema mapping
157			view.getInputHandler().setRepeatCount(1);
158			view.getInputHandler().invokeAction("sidekick-parse");
159		}
160	}
161	//}}}
162
163	// {{{ ChooseSchemaDialog class
164	static class ChooseSchemaDialog extends EnhancedDialog
165	{
166		// {{{ private instance variables
167		private JCheckBox relative_cb;
168		private JTextField path;
169		private JTextField buffer_path;
170		private JTextField relative_path;
171		private boolean valid;
172		// }}}
173		
174		/** URI of the chosen schema */
175		URI schemaURL = null;
176		/** is schemaURL relative to the buffer */
177		boolean relative = false;
178		
179		/** did the user click OK */
180		boolean confirmed = false;
181		
182		
183		ChooseSchemaDialog(final View view)
184		{
185			super(view, jEdit.getProperty("xml.choose-schema.title"),true);
186			JPanel panel = new JPanel( new KappaLayout() );
187			panel.setBorder( BorderFactory.createEmptyBorder( 6, 6, 6, 6 ) );
188			
189			
190			ActionListener relativize = new ActionListener(){
191				public void actionPerformed(ActionEvent e)
192				{
193					relativize();
194				}
195			};
196			
197			// reminder : buffer path
198			JLabel buffer_label = new JLabel( jEdit.getProperty("xml.choose-schema.buffer-path") );
199			buffer_path = new JTextField(40);
200			buffer_path.setEditable(false);
201			buffer_path.setName("buffer_path");
202			
203			// choose a path
204			JLabel choose_label = new JLabel( jEdit.getProperty("xml.choose-schema.message") );
205			path = new HistoryTextField("xml.choose-schema",true);
206			path.setName("path");
207			path.setText("");
208			path.setColumns(40);
209			path.addActionListener(relativize);
210			
211			JButton browse = new JButton( jEdit.getProperty("xml.choose-schema.browse") );
212			browse.setMnemonic(KeyEvent.VK_B);
213			browse.setName("browse");
214			browse.addActionListener( new ActionListener()
215				{
216					public void actionPerformed( ActionEvent ae )
217					{
218						String[] paths = GUIUtilities.showVFSFileDialog( view, path.getText(), VFSBrowser.OPEN_DIALOG, false );
219						if ( paths != null && paths.length > 0 ) {
220							path.setText( paths[0] );
221							relativize();
222						}
223					}
224				});
225			
226			// set a relative path
227			relative_cb = new JCheckBox(jEdit.getProperty("xml.choose-schema.relative"));
228			relative_cb.addActionListener(relativize);
229			relative_cb.setName("relative");
230			
231			relative_path = new JTextField(40);
232			relative_path.setEditable(false);
233			relative_path.setName("relative_path");
234			
235			//LTTTTTTT
236			//S
237			//LLLLLLLL
238			//TTTTTTTB
239			//S
240			//S
241			//S
242			//CCCCCC
243			//TTTTTTT
244			//S
245			// OK CANCEL
246			
247			JPanel okCancel = new OkCancelButtons(this);
248			
249			// add the components to the option panel
250			panel.add( "0, 0, 1, 1, W, w, 3", buffer_label );
251			panel.add( "1, 0, 6, 1, W, w, 3", buffer_path );
252			panel.add( "0, 1, 1, 1, 0,  , 0", KappaLayout.createVerticalStrut( 11, true ) );
253			panel.add( "0, 2, 8, 1, W, w, 3", choose_label );
254			panel.add( "0, 3, 7, 1, W, w, 3", path );
255			panel.add( "7, 3, 1, 1, W, w, 3", browse );
256			panel.add( "0, 4, 1, 1, 0,  , 0", KappaLayout.createVerticalStrut( 33, true ) );
257			panel.add( "0, 5, 6, 1, 0, w, 0", relative_cb );
258			panel.add( "0, 6, 5, 1, W, w, 3", relative_path );
259			panel.add( "0, 7, 1, 1, 0,  , 0", KappaLayout.createVerticalStrut( 11, true ) );
260			panel.add( "0, 8, 8, 1, 0, w, 0", okCancel );
261			
262			setContentPane(panel);
263			pack();
264		}
265		
266		/**
267		 * for unit tests, mainly
268		 */
269		void init(String buffer, String oldSchema, boolean relativeEnabled)
270		{
271			buffer_path.setText(buffer);
272			if(oldSchema == null)
273			{
274				path.setText(MiscUtilities.getParentOfPath(buffer));
275			}
276			else
277			{
278				path.setText(oldSchema);
279			}
280			relative_cb.setSelected(false);
281			relative_cb.setEnabled(relativeEnabled);
282			relativize();
283		}
284		
285		/**
286		 * shows the dialog and returns true if the user confirmed
287		 * @param	buffer		path to the xml document needing a schema
288		 * @param	oldSchema	path to the schema currently assigned or null
289		 * @param	relativeEnabled	is a relative path enabled
290		 */
291		public boolean choose(String buffer, String oldSchema, boolean relativeEnabled)
292		{
293			confirmed = false;
294			init(buffer,oldSchema, relativeEnabled);
295			setVisible(true);
296			return confirmed;
297		}
298		
299		/**
300		 * refuse to close the dialog if there is an error
301		 */
302		public void ok()
303		{
304			relativize();
305			if(valid)
306			{
307				confirmed = true;
308				setVisible(false);
309			}
310		}
311		
312		public void cancel() 
313		{
314			confirmed = false;
315			setVisible(false);
316		}
317		
318		/**
319		* transform the contents of path into a relative URL, if relative_cb is checked.
320		* TODO: handle a relative URL entered by the user
321		* TODO: relativize jar:file:...!etc URLs
322		*/
323		private void relativize()
324		{
325			relative = relative_cb.isSelected();
326        	try
327        	{
328        		schemaURL = new URI(pathToURL(path.getText()));
329				if(relative)
330				{
331					String bufferURL = pathToURL(MiscUtilities.getParentOfPath(buffer_path.getText()));
332					schemaURL = new URI(bufferURL).relativize(schemaURL);
333				}
334        		path.setForeground(Color.BLACK);
335        		valid = true;
336        		relative_path.setText(schemaURL.toString());
337        	}
338        	catch(URISyntaxException ue)
339        	{
340        		Log.log(Log.ERROR,SchemaMappingManager.class,ue);
341        		path.setForeground(Color.RED);
342        		valid = false;
343        		relative_path.setText(ue.getMessage());
344        	}
345		}
346	}
347	// }}}
348	
349	//{{{ promptTypeIdForBuffer() method
350	/**
351	 * - gather all type Ids
352	 * - let the user choose one
353	 * - update the SchemaMapping
354	 */
355	public static void promptTypeIdForBuffer(View view, Buffer buffer)
356	{
357		// local schema-mapping
358		String specificSchema = MiscUtilities.constructPath(
359			MiscUtilities.getParentOfPath(
360				buffer.getPath()),SchemaMapping.SCHEMAS_FILE);
361		
362		URI specificSchemaURI = null;
363		
364		try
365		{
366			specificSchemaURI = new URI(pathToURL(specificSchema));
367		}
368		catch(URISyntaxException mfue)
369		{
370			// TODO: react to the error 
371			Log.log(Log.ERROR,SchemaMappingManager.class,mfue);
372		}
373		
374		SchemaMapping lMapping = getLocalSchemaMapping(buffer, new LoggingErrorHandler(jEdit.getProperty("xml.local-schema")));
375		SchemaMapping gMapping = getGlobalSchemaMapping(new LoggingErrorHandler(jEdit.getProperty("xml.global-schema")));
376		SchemaMapping bMapping = getBuiltInSchemaMapping(new LoggingErrorHandler(jEdit.getProperty("xml.builtin-schema")));
377		
378		Map<String,SchemaMapping> allTypeIds = new HashMap<String,SchemaMapping>();
379		
380		String oldTypeId = null;
381		
382		for(SchemaMapping m: Arrays.asList(lMapping, gMapping, bMapping))
383		{
384			if(m != null)
385			{
386				for(SchemaMapping.TypeIdMapping tid : m.getTypeIds())
387				{
388					allTypeIds.put(tid.getId(),m);
389				}
390				
391				if(oldTypeId == null)
392				{
393					oldTypeId = m.getTypeIdForDocument(buffer.getPath());
394				}
395			}
396		}
397		
398		String[] tids = allTypeIds.keySet().toArray(new String[]{});
399		if(tids.length == 0)
400		{
401			view.getToolkit().beep();
402			view.getStatus().setMessage("xml.no-type-id.message");
403			return;
404		}
405		
406		String tid = (String)JOptionPane.showInputDialog(
407			view,
408			jEdit.getProperty("xml.choose-type-id.message"),
409			jEdit.getProperty("xml.choose-type-id.title"),
410            JOptionPane.OK_CANCEL_OPTION,
411            null,
412            tids,
413            oldTypeId == null ? tids[0] : oldTypeId
414            );
415        
416        if(tid!=null)
417        {
418        	SchemaMapping tidMapping = allTypeIds.get(tid);
419			
420        	if(isSchemaMappingEnabled(buffer))
421        	{
422				// no schemas.xml in the buffer's directory : will create one
423				if(lMapping == null)
424				{
425					lMapping = new SchemaMapping(specificSchemaURI);
426					if(gMapping != null)lMapping.ensureIncluded(gMapping);
427				}
428				
429				if(lMapping != tidMapping)
430				{
431					lMapping.ensureIncluded(tidMapping);
432				}
433				
434				String bufferURL = pathToURL(buffer.getPath());
435				SchemaMapping.URIResourceRule newRule = new SchemaMapping.URIResourceRule(null,bufferURL,tid, true);
436				
437				lMapping.updateMapping(newRule);
438				
439				try
440				{
441					lMapping.toDocument(lMapping.getBaseURI().toString());
442				}
443				catch(IOException ioe)
444				{
445					// if saving fails, try to output it in a buffer
446					JOptionPane.showMessageDialog(
447						view,
448						jEdit.getProperty("xml-error-to-document.message",new Object[]{ioe.getClass(),ioe.getMessage()}),
449						jEdit.getProperty("xml-error-to-document.title"),
450						JOptionPane.ERROR_MESSAGE
451						);
452					Buffer b = jEdit.newFile(view,lMapping.getBaseURI().toString());
453					b.insert(0,lMapping.toString());
454					view.getEditPane().setBuffer(buffer);
455				}
456				
457			}
458			else
459			{
460				SchemaMapping.Result res = tidMapping.resolveTypeId(tid);
461				if(res == null)
462				{
463				}
464				else
465				{
466					String schemaURL = res.baseURI.resolve(res.target).toString();
467					buffer.setStringProperty(BUFFER_SCHEMA_PROP,schemaURL);
468				}
469			}
470				
471			// finally, use the new schema mapping
472			view.getInputHandler().setRepeatCount(1);
473			view.getInputHandler().invokeAction("sidekick-parse");
474        }
475	}
476	//}}}
477	
478	//{{{ getGlobalSchemaMapping() method
479	/**
480	 * @param errorHandler	notified of errors loading the global schemas.xml
481	 * @return	schema mapping rules in JEDIT_SETTINGS/plugins/xml.XMLPLugin/schemas.xml
482	 */
483	public static SchemaMapping getGlobalSchemaMapping(ErrorHandler errorHandler)
484	{
485		String schemaURL = jEdit.getProperty(SCHEMA_MAPPING_PROP);
486		SchemaMapping mapping = null;
487		// if schemaURL is null, it means that there is no settings directory
488		if(schemaURL != null)
489		{
490			Log.log(Log.DEBUG,SchemaMappingManager.class,"global mapping="+schemaURL);
491			try {
492				mapping = SchemaMapping.fromDocument(schemaURL,errorHandler);
493			} catch (IOException e) {
494				mapping = null;
495				Log.log(Log.ERROR,SchemaMappingManager.class,
496						"Error loading global schema mapping: '"+schemaURL+"'",e);
497			} catch (SAXException e) {
498				mapping = null;
499			}
500		}
501		return mapping;
502	}
503	//}}}
504	
505	//{{{ getBuiltInSchemaMapping() method
506	/**
507	 * @param errorHandler	notified of errors/warning loading builtin schema mapping (not likely)
508	 * @return	mappings shipped with this plugin : jeditresource:XML.jar!/xml/dtds/schemas.xml
509	 */
510	public static SchemaMapping getBuiltInSchemaMapping(ErrorHandler errorHandler)
511	{
512		java.net.URL schemaURL = SchemaMappingManager.class.getClassLoader().getResource(BUILT_IN_SCHEMA);
513		
514		if(schemaURL == null)
515		{
516			throw new AssertionError("built-in schemas.xml cant be found !");
517		}
518		else
519		{
520			try {
521				return SchemaMapping.fromDocument(schemaURL.toString(),errorHandler);
522			} catch (IOException e) {
523				Log.log(Log.ERROR,SchemaMappingManager.class,
524						"Error loading local schema mapping : '"+schemaURL+"'",e);
525				return null;
526			} catch (SAXException e) {
527				return null;
528			}
529		}
530	}
531	//}}}
532	
533	//{{{ getLocalSchemaMapping() method
534	/**
535	* @return schemas.xml next to buffer or null if it doesn't exist
536	*/
537	public static SchemaMapping getLocalSchemaMapping(Buffer buffer, ErrorHandler errorHandler)
538	{
539		SchemaMapping mapping;
540		
541		// local schema-mapping
542		String specificSchema = MiscUtilities.constructPath(
543			MiscUtilities.getParentOfPath(
544				buffer.getPath()),SchemaMapping.SCHEMAS_FILE);
545		
546		// VFS-compatible
547		try
548		{
549			URI uriSpecificSchema = new URI(PathUtilities.pathToURL(specificSchema));
550			if(SchemaMapping.resourceExists(uriSpecificSchema))
551			{
552					String schemaURL = uriSpecificSchema.toString();
553					mapping = SchemaMapping.fromDocument(schemaURL, errorHandler);
554			}
555			else
556			{
557				Log.log(Log.DEBUG, SchemaMappingManager.class,
558					"no schemas.xml in "+specificSchema);
559				mapping = null;
560			}
561		}
562		catch(URISyntaxException ue)
563		{
564			// may happen if specificSchema is relative, but it should not be the case with buffer.getPath()
565			Log.log(Log.ERROR,SchemaMappingManager.class,
566				"Error converting path for fromDocument : '"+specificSchema+"'",ue);
567			mapping = null;
568		} catch (IOException e) {
569			mapping = null;
570			Log.log(Log.ERROR,SchemaMappingManager.class,
571					"Error loading local schema mapping : '"+specificSchema+"'",e);
572		} catch (SAXException e) {
573			// logging has already been taken care of
574			mapping = null;
575		}
576		return mapping;
577	}
578	//}}}
579	
580	//{{{ getSchemaMappingForBuffer() method
581	/**
582	* @return local SchemaMapping or global SchemaMapping or built-in SchemaMapping
583	*/
584	public static SchemaMapping getSchemaMappingForBuffer(Buffer buffer, ErrorHandler errorHandler)
585	{
586		SchemaMapping mapping;
587		
588		// local schema-mapping ; be sure not to continue on validation error by encapsulating the errorHandler in a LoggingErrorHandler
589		mapping  = getLocalSchemaMapping(buffer, new LoggingErrorHandler(jEdit.getProperty(jEdit.getProperty("xml.local-schema")), errorHandler));
590		
591		if(mapping == null)
592		{
593			mapping = getGlobalSchemaMapping(new LoggingErrorHandler(jEdit.getProperty(jEdit.getProperty("xml.global-schema")), errorHandler));
594		}
595		
596		if(mapping == null)
597		{
598			Log.log(Log.DEBUG, SchemaMappingManager.class,
599				"no settings => using built-in schema mapping file");
600			mapping = getBuiltInSchemaMapping(new LoggingErrorHandler(jEdit.getProperty(jEdit.getProperty("xml.builtin-schema")), errorHandler));
601		}
602		return mapping;
603	}
604	//}}}
605	
606	//{{{ initGlobalSchemaMapping() method
607	/**
608	 * Finds (and creates if needed) the global schema mapping (schemas.xml)
609	 * file in the settings directory, for reading or overwriting it.
610	 */
611	public static void initGlobalSchemaMapping(View view){
612		jEdit.unsetProperty(SCHEMA_MAPPING_PROP);
613		
614		File home = org.gjt.sp.jedit.EditPlugin.getPluginHome(xml.XmlPlugin.class);
615		if(home == null)
616		{
617			// -nosettings
618			return;
619		}
620		else if(!home.exists())
621		{
622			Log.log(Log.DEBUG,SchemaMappingManager.class, "creating settings directory");
623			try
624			{
625				boolean created = home.mkdirs();
626				if(!created)
627				{
628					GUIUtilities.error( view, "unable to create settings directory: "+home,null);
629					return;
630				}
631			}
632			catch(SecurityException se)
633			{
634				GUIUtilities.error( view, "unable to create settings directory (security exception): "+home,null);
635				return;
636			}
637		}
638		File schemas = new File(home,SchemaMapping.SCHEMAS_FILE);
639		// create an empty mapping file in settings directory
640		// it points to the global schemaMapping, to get all
641		// the builtin rules
642		if(!schemas.exists()){
643			SchemaMapping builtinMapping = getBuiltInSchemaMapping(new LoggingErrorHandler(jEdit.getProperty("xml.builtin-schema")));
644			SchemaMapping tmp = new SchemaMapping();
645			tmp.ensureIncluded(builtinMapping);
646			try{
647				tmp.toDocument(schemas.toURI().toURL().toString());
648			}catch(IOException ioe){
649				Log.log(Log.ERROR,SchemaMappingManager.class,"Unable to save default RelaxNG mappings",ioe);
650				return;
651			}
652		}
653		try
654		{
655			jEdit.setProperty(SCHEMA_MAPPING_PROP,schemas.toURI().toURL().toString());
656		}
657		catch(java.net.MalformedURLException mfe)
658		{
659			Log.log(Log.ERROR,SchemaMappingManager.class,mfe);
660		}
661	}
662	//}}}
663
664	//{{{ isSchemaMappingEnabled() method
665	public static boolean isSchemaMappingEnabled(Buffer b){
666		return b.getBooleanProperty(BUFFER_ENABLE_SCHEMA_MAPPING_PROP);
667	}
668	//}}}
669	
670	//{{{ LoggingErrorHandler class
671	/** logs errors and warnings to Activity Log and rethrow exception on error and fatalError to prevent
672	 *  invalid documents to result in invalid SchemaMappings.
673	 */
674	public static class LoggingErrorHandler implements ErrorHandler{
675		private String resourceName;
676		private ErrorHandler errorHandler;
677
678		public LoggingErrorHandler(String resourceName){
679		}
680		
681		public LoggingErrorHandler(String property, ErrorHandler errorHandler) {
682			this.resourceName = resourceName;
683			this.errorHandler = errorHandler;
684		}
685
686		@Override
687		public void error(SAXParseException exception) throws SAXException {
688			Log.log(Log.WARNING, SchemaMappingManager.class, jEdit.getProperty("xml.SchemaMappingManager.error",new Object[]{resourceName,exception.getSystemId(),exception.getLineNumber(),exception.getLocalizedMessage()}));
689			if(errorHandler != null)errorHandler.error(exception);
690			throw exception;
691		}
692		
693		@Override
694		public void fatalError(SAXParseException exception) throws SAXException {
695			Log.log(Log.WARNING, SchemaMappingManager.class, jEdit.getProperty("xml.SchemaMappingManager.fatal-error",new Object[]{resourceName,exception.getSystemId(),exception.getLineNumber(),exception.getLocalizedMessage()}));
696			if(errorHandler != null)errorHandler.fatalError(exception);
697			throw exception;
698		}
699		
700		@Override
701		public void warning(SAXParseException exception) throws SAXException {
702			Log.log(Log.NOTICE, SchemaMappingManager.class, jEdit.getProperty("xml.SchemaMappingManager.warning",new Object[]{resourceName,exception.getSystemId(),exception.getLineNumber(),exception.getLocalizedMessage()}));
703			if(errorHandler != null)errorHandler.warning(exception);
704		}
705	}
706}