001 /*
002 *
003 * $Revision: 14750 $ $Date: 2009-02-17 16:36:38 +0100 (Tue, 17 Feb 2009) $
004 *
005 * This file is part of *** M y C o R e ***
006 * See http://www.mycore.de/ for details.
007 *
008 * This program is free software; you can use it, redistribute it
009 * and / or modify it under the terms of the GNU General Public License
010 * (GPL) as published by the Free Software Foundation; either version 2
011 * of the License or (at your option) any later version.
012 *
013 * This program is distributed in the hope that it will be useful, but
014 * WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program, in a file called gpl.txt or license.txt.
020 * If not, write to the Free Software Foundation Inc.,
021 * 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA
022 */
023
024 package org.mycore.common;
025
026 import static org.mycore.common.MCRConstants.DATE_FORMAT;
027 import static org.mycore.common.MCRConstants.DEFAULT_ENCODING;
028
029 import java.io.BufferedOutputStream;
030 import java.io.ByteArrayOutputStream;
031 import java.io.File;
032 import java.io.FileOutputStream;
033 import java.io.IOException;
034 import java.io.InputStream;
035 import java.io.OutputStream;
036 import java.io.Reader;
037 import java.io.Writer;
038 import java.text.DateFormat;
039 import java.text.ParseException;
040 import java.text.SimpleDateFormat;
041 import java.util.ArrayList;
042 import java.util.Calendar;
043 import java.util.GregorianCalendar;
044 import java.util.HashSet;
045 import java.util.Iterator;
046 import java.util.Locale;
047 import java.util.Properties;
048
049 import javax.xml.parsers.SAXParser;
050 import javax.xml.parsers.SAXParserFactory;
051
052 import org.apache.log4j.Logger;
053 import org.jdom.Document;
054 import org.jdom.Element;
055 import org.jdom.output.Format;
056 import org.jdom.output.XMLOutputter;
057 import org.xml.sax.Attributes;
058 import org.xml.sax.InputSource;
059 import org.xml.sax.helpers.DefaultHandler;
060
061 /**
062 * This class represent a general set of external methods to support the
063 * programming API.
064 *
065 * @author Jens Kupferschmidt
066 * @author Frank L\u00fctzenkirchen
067 * @author Thomas Scheffler (yagee)
068 *
069 * @version $Revision: 14750 $ $Date: 2009-02-17 16:36:38 +0100 (Tue, 17 Feb 2009) $
070 */
071 public class MCRUtils {
072 // The file slash
073 private static String SLASH = System.getProperty("file.separator");;
074
075 public final static char COMMAND_OR = 'O';
076
077 public final static char COMMAND_AND = 'A';
078
079 public final static char COMMAND_XOR = 'X';
080
081 // public constant data
082 private static final Logger LOGGER = Logger.getLogger(MCRUtils.class);
083
084 // Language lists
085 private static ArrayList<String> langlist = new ArrayList<String>();
086 private static ArrayList<String> countrylist = new ArrayList<String>();
087
088 /**
089 * Load two static arrays for fast search of ISO-639/ISO-3166 strings.
090 */
091 static {
092 StringBuffer sb;
093 // add id as workaround
094 langlist.add("id");
095 countrylist.add("ID");
096 // add codes from locale
097 for ( Locale l : Locale.getAvailableLocales()) {
098 sb = new StringBuffer(l.getLanguage());
099 langlist.add(sb.toString());
100 sb.append('-').append(l.getCountry());
101 countrylist.add(sb.toString());
102 }
103 }
104
105 /**
106 * This method check the language string base on RFC 1766 to the supported
107 * languages in mycore.
108 *
109 * @param lang
110 * the language string
111 * @return true if the language was supported, otherwise false
112 */
113 public static final boolean isSupportedLang(String lang) {
114 if ((lang == null) || ((lang = lang.trim()).length() == 0)) {
115 return false;
116 }
117 if (lang.startsWith("x-")) { return true; }
118 if (langlist.contains(lang)) { return true; }
119 if (countrylist.contains(lang)) { return true; }
120 return false;
121 }
122
123 /**
124 * The methode convert the input date string to the ISO output string. If
125 * the input can't convert, the output is null.
126 *
127 * @param indate
128 * the date input
129 * @return the ISO output or null
130 */
131 public static final String covertDateToISO(String indate) {
132 if ((indate == null) || ((indate = indate.trim()).length() == 0)) {
133 return null;
134 }
135
136 GregorianCalendar calendar = new GregorianCalendar();
137 boolean test = false;
138 SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
139 formatter.setLenient(false);
140
141 try {
142 calendar.setTime(formatter.parse(indate));
143 test = true;
144 } catch (ParseException e) {
145 }
146
147 if (!test) {
148 for (int i = 0; i < DATE_FORMAT.length; i++) {
149 DateFormat df = DATE_FORMAT[i];
150 df.setLenient(false);
151
152 try {
153 calendar.setTime(df.parse(indate));
154 test = true;
155 } catch (ParseException e) {
156 }
157
158 if (test) {
159 break;
160 }
161 }
162 }
163
164 if (!test) {
165 return null;
166 }
167
168 formatter.setCalendar(calendar);
169
170 return formatter.format(calendar.getTime());
171 }
172
173 /**
174 * The methode convert the input date string to the GregorianCalendar. If
175 * the input can't convert, the output is null.
176 *
177 * @param indate
178 * the date input
179 * @return the GregorianCalendar or null
180 */
181 public static final GregorianCalendar covertDateToGregorianCalendar(String indate) {
182 if ((indate == null) || ((indate = indate.trim()).length() == 0)) {
183 return null;
184 }
185
186 boolean era = true;
187 int start = 0;
188
189 if (indate.substring(0, 2).equals("AD")) {
190 era = true;
191 start = 2;
192 }
193
194 if (indate.substring(0, 2).equals("BC")) {
195 era = false;
196 start = 2;
197 }
198
199 GregorianCalendar calendar = new GregorianCalendar();
200 boolean test = false;
201 SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
202
203 try {
204 calendar.setTime(formatter.parse(indate.substring(start, indate.length())));
205
206 if (!era) {
207 calendar.set(Calendar.ERA, GregorianCalendar.BC);
208 }
209
210 test = true;
211 } catch (ParseException e) {
212 }
213
214 if (!test) {
215 for (int i = 0; i < DATE_FORMAT.length; i++) {
216 DateFormat df = DATE_FORMAT[i];
217
218 try {
219 calendar.setTime(df.parse(indate.substring(start, indate.length())));
220
221 if (!era) {
222 calendar.set(Calendar.ERA, GregorianCalendar.BC);
223 }
224
225 test = true;
226 } catch (ParseException e) {
227 }
228
229 if (test) {
230 break;
231 }
232 }
233 }
234
235 if (!test) {
236 return null;
237 }
238
239 return calendar;
240 }
241
242 /**
243 * This methode replace any characters to XML entity references.
244 * <p>
245 * <ul>
246 * <li>< to &lt;
247 * <li>> to &gt;
248 * <li>& to &amp;
249 * <li>" to &quot;
250 * <li>' to &apos;
251 * </ul>
252 *
253 * @param in
254 * a string
255 * @return the converted string.
256 */
257 public static final String stringToXML(String in) {
258 if (in == null) {
259 return "";
260 }
261
262 StringBuffer sb = new StringBuffer(2048);
263
264 for (int i = 0; i < in.length(); i++) {
265 if (in.charAt(i) == '<') {
266 sb.append("<");
267
268 continue;
269 }
270
271 if (in.charAt(i) == '>') {
272 sb.append(">");
273
274 continue;
275 }
276
277 if (in.charAt(i) == '&') {
278 sb.append("&");
279
280 continue;
281 }
282
283 if (in.charAt(i) == '\"') {
284 sb.append(""");
285
286 continue;
287 }
288
289 if (in.charAt(i) == '\'') {
290 sb.append("'");
291
292 continue;
293 }
294
295 sb.append(in.charAt(i));
296 }
297
298 return sb.toString();
299 }
300
301 /**
302 * This method convert a JDOM tree to a byte array.
303 *
304 * @param jdom
305 * the JDOM tree
306 * @return a byte array of the JDOM tree
307 */
308 public static final byte[] getByteArray(org.jdom.Document jdom) throws MCRPersistenceException {
309 MCRConfiguration conf = MCRConfiguration.instance();
310 String mcr_encoding = conf.getString("MCR.Metadata.DefaultEncoding", DEFAULT_ENCODING);
311 ByteArrayOutputStream outb = new ByteArrayOutputStream();
312
313 try {
314 XMLOutputter outp = new XMLOutputter(Format.getRawFormat().setEncoding(mcr_encoding));
315 outp.output(jdom, outb);
316 } catch (Exception e) {
317 throw new MCRPersistenceException("Can't produce byte array.");
318 }
319
320 return outb.toByteArray();
321 }
322
323 /**
324 * Converts an Array of Objects to an Array of Strings using the toString()
325 * method.
326 *
327 * @param objects
328 * Array of Objects to be converted
329 * @return Array of Strings representing Objects
330 */
331 public static final String[] getStringArray(Object[] objects) {
332 String[] returns = new String[objects.length];
333
334 for (int i = 0; i < objects.length; i++)
335 returns[i] = objects[i].toString();
336
337 return returns;
338 }
339
340 /**
341 * Converts an Array of Objects to an Array of Strings using the toString()
342 * method.
343 *
344 * @param objects
345 * Array of Objects to be converted
346 * @param maxitems
347 * The maximum of items to convert
348 * @return Array of Strings representing Objects
349 */
350 public static final String[] getStringArray(Object[] objects, int maxitems) {
351 String[] returns = new String[maxitems];
352
353 for (int i = 0; i < maxitems; i++)
354 returns[i] = objects[i].toString();
355
356 return returns;
357 }
358
359 /**
360 * Copies all content read from the given input stream to the given output
361 * stream. Note that this method will NOT close the streams when finished
362 * copying.
363 *
364 * @param source
365 * the InputStream to read the bytes from
366 * @param target
367 * out the OutputStream to write the bytes to, may be null
368 * @return true if Inputstream copied successfully to OutputStream
369 */
370 public static boolean copyStream(InputStream source, OutputStream target) {
371 if (source == null) {
372 throw new MCRException("InputStream source is null.");
373 }
374
375 try {
376 // R E A D / W R I T E by chunks
377 int chunkSize = 63 * 1024;
378
379 // code will work even when chunkSize = 0 or chunks = 0;
380 // Even for small files, we allocate a big buffer, since we
381 // don't know the size ahead of time.
382 byte[] ba = new byte[chunkSize];
383
384 // keep reading till hit eof
385 while (true) {
386 int bytesRead = readBlocking(source, ba, 0, chunkSize);
387
388 if (LOGGER.isDebugEnabled()) {
389 LOGGER.debug(MCRUtils.class.getName() + ".copyStream(): " + bytesRead + " bytes read");
390 }
391
392 if (bytesRead > 0) {
393 if (target != null) {
394 target.write(ba, 0 /* offset in ba */, bytesRead /*
395 * bytes
396 * to
397 * write
398 */);
399 }
400 } else {
401 break; // hit eof
402 }
403 } // end while
404
405 // C L O S E, done by caller if wanted.
406 } catch (IOException e) {
407 LOGGER.debug("IOException caught while copying streams:");
408 LOGGER.debug(e.getClass().getName() + ": " + e.getMessage());
409 LOGGER.debug(e);
410 return false;
411 }
412
413 // all was ok
414 return true;
415 } // end copy
416
417 /**
418 * Copies all content read from the given input stream to the given output
419 * stream. Note that this method will NOT close the streams when finished
420 * copying.
421 *
422 * @param source
423 * the InputStream to read the bytes from
424 * @param target
425 * out the OutputStream to write the bytes to, may be null
426 * @return true if Inputstream copied successfully to OutputStream
427 */
428 public static boolean copyReader(Reader source, Writer target) {
429 if (source == null) {
430 throw new MCRException("Reader source is null.");
431 }
432
433 try {
434 // R E A D / W R I T E by chunks
435 int chunkSize = 63 * 1024;
436
437 // code will work even when chunkSize = 0 or chunks = 0;
438 // Even for small files, we allocate a big buffer, since we
439 // don't know the size ahead of time.
440 char[] ca = new char[chunkSize];
441
442 // keep reading till hit eof
443 while (true) {
444 int charsRead = readBlocking(source, ca, 0, chunkSize);
445
446 if (LOGGER.isDebugEnabled()) {
447 LOGGER.debug(MCRUtils.class.getName() + ".copyReader(): " + charsRead + " characters read");
448 }
449
450 if (charsRead > 0) {
451 if (target != null) {
452 target.write(ca, 0 /* offset in ba */, charsRead /*
453 * bytes
454 * to
455 * write
456 */);
457 }
458 } else {
459 break; // hit eof
460 }
461 } // end while
462
463 // C L O S E, done by caller if wanted.
464 } catch (IOException e) {
465 return false;
466 }
467
468 // all was ok
469 return true;
470 } // end copy
471
472 /**
473 * merges to HashSets of MyCoreIDs after specific rules
474 *
475 * @see #COMMAND_OR
476 * @see #COMMAND_AND
477 * @see #COMMAND_XOR
478 * @param set1
479 * 1st HashSet to be merged
480 * @param set2
481 * 2nd HashSet to be merged
482 * @param operation
483 * available COMMAND_XYZ
484 * @return merged HashSet
485 */
486 public static final <T> HashSet<T> mergeHashSets(HashSet<? extends T> set1, HashSet<? extends T> set2, char operation) {
487 HashSet<T> merged = new HashSet<T>();
488 T id;
489
490 switch (operation) {
491 case COMMAND_OR:
492 merged.addAll(set1);
493 merged.addAll(set2);
494
495 break;
496
497 case COMMAND_AND:
498
499 for (Iterator<? extends T> it = set1.iterator(); it.hasNext();) {
500 id = it.next();
501
502 if (set2.contains(id)) {
503 merged.add(id);
504 }
505 }
506
507 break;
508
509 case COMMAND_XOR:
510
511 for (Iterator<? extends T> it = set1.iterator(); it.hasNext();) {
512 id = it.next();
513
514 if (!set2.contains(id)) {
515 merged.add(id);
516 }
517 }
518
519 for (Iterator<? extends T> it = set2.iterator(); it.hasNext();) {
520 id = it.next();
521
522 if (!set1.contains(id) && !merged.contains(id)) {
523 merged.add(id);
524 }
525 }
526
527 break;
528
529 default:
530 throw new IllegalArgumentException("operation not permited: " + operation);
531 }
532
533 return merged;
534 }
535
536 /**
537 * The method cut an ArrayList for a maximum of items.
538 *
539 * @param arrayin The incoming ArrayList
540 * @param maxitem The maximum number of items
541 * @return the cutted ArrayList
542 */
543 public static final <T> ArrayList<T> cutArrayList(ArrayList<? extends T> arrayin, int maxitems) {
544 if (arrayin == null) {
545 throw new MCRException("Input ArrayList is null.");
546 }
547
548 if (maxitems < 1) {
549 LOGGER.warn("The maximum items are lower then 1.");
550 }
551
552 ArrayList<T> arrayout = new ArrayList<T>();
553 int i = 0;
554
555 for (Iterator<? extends T> it = arrayin.iterator(); it.hasNext() && (i < maxitems); i++) {
556 arrayout.add(it.next());
557 }
558 return arrayout;
559 }
560
561 /**
562 * Reads exactly <code>len</code> bytes from the input stream into the
563 * byte array. This method reads repeatedly from the underlying stream until
564 * all the bytes are read. InputStream.read is often documented to block
565 * like this, but in actuality it does not always do so, and returns early
566 * with just a few bytes. readBlockiyng blocks until all the bytes are read,
567 * the end of the stream is detected, or an exception is thrown. You will
568 * always get as many bytes as you asked for unless you get an eof or other
569 * exception. Unlike readFully, you find out how many bytes you did get.
570 *
571 * @param b
572 * the buffer into which the data is read.
573 * @param off
574 * the start offset of the data.
575 * @param len
576 * the number of bytes to read.
577 * @return number of bytes actually read.
578 * @exception IOException
579 * if an I/O error occurs.
580 *
581 */
582 public static final int readBlocking(InputStream in, byte[] b, int off, int len) throws IOException {
583 int totalBytesRead = 0;
584
585 while (totalBytesRead < len) {
586 int bytesRead = in.read(b, off + totalBytesRead, len - totalBytesRead);
587
588 if (bytesRead < 0) {
589 break;
590 }
591
592 totalBytesRead += bytesRead;
593 }
594
595 return totalBytesRead;
596 } // end readBlocking
597
598 /**
599 * Reads exactly <code>len</code> bytes from the input stream into the
600 * byte array. This method reads repeatedly from the underlying stream until
601 * all the bytes are read. Reader.read is often documented to block like
602 * this, but in actuality it does not always do so, and returns early with
603 * just a few bytes. readBlockiyng blocks until all the bytes are read, the
604 * end of the stream is detected, or an exception is thrown. You will always
605 * get as many bytes as you asked for unless you get an eof or other
606 * exception. Unlike readFully, you find out how many bytes you did get.
607 *
608 * @param c
609 * the buffer into which the data is read.
610 * @param off
611 * the start offset of the data.
612 * @param len
613 * the number of bytes to read.
614 * @return number of bytes actually read.
615 * @exception IOException
616 * if an I/O error occurs.
617 *
618 */
619 public static final int readBlocking(Reader in, char[] c, int off, int len) throws IOException {
620 int totalCharsRead = 0;
621
622 while (totalCharsRead < len) {
623 int charsRead = in.read(c, off + totalCharsRead, len - totalCharsRead);
624
625 if (charsRead < 0) {
626 break;
627 }
628
629 totalCharsRead += charsRead;
630 }
631
632 return totalCharsRead;
633 } // end readBlocking
634
635 /**
636 * <p>
637 * Returns String in with newStr substituted for find String.
638 *
639 * @param in
640 * String to edit
641 * @param find
642 * string to match
643 * @param newStr
644 * string to substitude for find
645 */
646 public static String replaceString(String in, String find, String newStr) {
647 char[] working = in.toCharArray();
648 StringBuffer sb = new StringBuffer();
649
650 int startindex = in.indexOf(find);
651
652 if (startindex < 0) {
653 return in;
654 }
655
656 int currindex = 0;
657
658 while (startindex > -1) {
659 for (int i = currindex; i < startindex; i++) {
660 sb.append(working[i]);
661 } // for
662
663 currindex = startindex;
664 sb.append(newStr);
665 currindex += find.length();
666 startindex = in.indexOf(find, currindex);
667 } // while
668
669 for (int i = currindex; i < working.length; i++) {
670 sb.append(working[i]);
671 } // for
672
673 return sb.toString();
674 }
675
676 /**
677 * The method wrap the org.jdom.Element in a org.jdom.Document and write it
678 * to a file.
679 *
680 * @param elm
681 * the JDOM Document
682 * @param xml
683 * the File instance
684 */
685 public static final void writeElementToFile(Element elm, File xml) {
686 writeJDOMToFile((new Document()).addContent(elm), xml);
687 }
688
689 /**
690 * The method write a given JDOM Document to a file.
691 *
692 * @param jdom
693 * the JDOM Document
694 * @param xml
695 * the File instance
696 */
697 public static final void writeJDOMToFile(Document jdom, File xml) {
698 try {
699 XMLOutputter xout = new XMLOutputter(Format.getPrettyFormat());
700 BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(xml));
701 xout.output(jdom, out);
702 out.close();
703 } catch (IOException ioe) {
704 if (LOGGER.isDebugEnabled()) {
705 ioe.printStackTrace();
706 } else {
707 LOGGER.error("Can't write org.jdom.Document to file "+xml.getName()+".");
708 }
709 }
710 }
711
712 /**
713 * The method wrap the org.jdom.Element in a org.jdom.Document and write it
714 * to Sysout.
715 *
716 * @param elm
717 * the JDOM Document
718 */
719 public static final void writeElementToSysout(Element elm) {
720 writeJDOMToSysout((new Document()).addContent(elm));
721 }
722
723 /**
724 * The method write a given JDOM Document to the system output.
725 *
726 * @param jdom
727 * the JDOM Document
728 */
729 public static final void writeJDOMToSysout(Document jdom) {
730 try {
731 XMLOutputter xout = new XMLOutputter(Format.getPrettyFormat());
732 BufferedOutputStream out = new BufferedOutputStream(System.out);
733 xout.output(jdom, out);
734 out.flush();
735 } catch (IOException ioe) {
736 if (LOGGER.isDebugEnabled()) {
737 ioe.printStackTrace();
738 } else {
739 LOGGER.error("Can't write org.jdom.Document to Sysout.");
740 }
741 }
742 }
743
744 /**
745 * The method return a list of all file names under the given directory and
746 * subdirectories of itself.
747 *
748 * @param basedir
749 * the File instance of the basic directory
750 * @return an ArrayList with file names as pathes
751 */
752 public static ArrayList<String> getAllFileNames(File basedir) {
753 ArrayList<String> out = new ArrayList<String>();
754 File[] stage = basedir.listFiles();
755
756 for (int i = 0; i < stage.length; i++) {
757 if (stage[i].isFile()) {
758 out.add(stage[i].getName());
759 }
760
761 if (stage[i].isDirectory()) {
762 out.addAll(getAllFileNames(stage[i], stage[i].getName() + SLASH));
763 }
764 }
765
766 return out;
767 }
768
769 /**
770 * The method return a list of all file names under the given directory and
771 * subdirectories of itself.
772 *
773 * @param basedir
774 * the File instance of the basic directory
775 * @param path
776 * the part of directory path
777 * @return an ArrayList with file names as pathes
778 */
779 public static ArrayList<String> getAllFileNames(File basedir, String path) {
780 ArrayList<String> out = new ArrayList<String>();
781 File[] stage = basedir.listFiles();
782
783 for (int i = 0; i < stage.length; i++) {
784 if (stage[i].isFile()) {
785 out.add(path + stage[i].getName());
786 }
787
788 if (stage[i].isDirectory()) {
789 out.addAll(getAllFileNames(stage[i], path + stage[i].getName() + SLASH));
790 }
791 }
792
793 return out;
794 }
795
796 /**
797 * The method return a list of all directory names under the given directory
798 * and subdirectories of itself.
799 *
800 * @param basedir
801 * the File instance of the basic directory
802 * @return an ArrayList with directory names as pathes
803 */
804 public static ArrayList<String> getAllDirectoryNames(File basedir) {
805 ArrayList<String> out = new ArrayList<String>();
806 File[] stage = basedir.listFiles();
807
808 for (int i = 0; i < stage.length; i++) {
809 if (stage[i].isDirectory()) {
810 out.add(stage[i].getName());
811 out.addAll(getAllDirectoryNames(stage[i], stage[i].getName() + SLASH));
812 }
813 }
814
815 return out;
816 }
817
818 /**
819 * The method return a list of all directory names under the given directory
820 * and subdirectories of itself.
821 *
822 * @param basedir
823 * the File instance of the basic directory
824 * @param path
825 * the part of directory path
826 * @return an ArrayList with directory names as pathes
827 */
828 public static ArrayList<String> getAllDirectoryNames(File basedir, String path) {
829 ArrayList<String> out = new ArrayList<String>();
830 File[] stage = basedir.listFiles();
831
832 for (int i = 0; i < stage.length; i++) {
833 if (stage[i].isDirectory()) {
834 out.add(path + stage[i].getName());
835 out.addAll(getAllDirectoryNames(stage[i], path + stage[i].getName() + SLASH));
836 }
837 }
838
839 return out;
840 }
841
842 public static String arrayToString(Object[] objArray, String seperator) {
843 StringBuffer buf = new StringBuffer();
844
845 for (int i = 0; i < objArray.length; i++) {
846 buf.append(objArray[i]).append(seperator);
847 }
848
849 if (objArray.length > 0) {
850 buf.setLength(buf.length() - seperator.length());
851 }
852
853 return buf.toString();
854 }
855
856 public static String parseDocumentType(InputStream in) {
857 SAXParser parser = null;
858
859 try {
860 parser = SAXParserFactory.newInstance().newSAXParser();
861 } catch (Exception ex) {
862 String msg = "Could not build a SAX Parser for processing XML input";
863 throw new MCRConfigurationException(msg, ex);
864 }
865
866 final Properties detected = new Properties();
867 final String forcedInterrupt = "mcr.forced.interrupt";
868
869 DefaultHandler handler = new DefaultHandler() {
870 public void startElement(String uri, String localName, String qName, Attributes attributes) {
871 LOGGER.debug("MCRLayoutService detected root element = " + qName);
872 detected.setProperty("docType", qName);
873 throw new MCRException(forcedInterrupt);
874 }
875
876 // We would need SAX 2.0 to be able to do this, for later use:
877 public void startDTD(String name, String publicId, String systemId) {
878 if (LOGGER.isDebugEnabled()) {
879 LOGGER.debug(new StringBuffer(1024).append("MCRUtils detected DOCTYPE declaration = ").append(name).append(" publicId = ").append(publicId).append(" systemId = ").append(systemId).toString());
880 }
881 detected.setProperty("docType", name);
882 throw new MCRException(forcedInterrupt);
883 }
884 };
885
886 try {
887 parser.parse(new InputSource(in), handler);
888 } catch (Exception ex) {
889 if (!forcedInterrupt.equals(ex.getMessage())) {
890 String msg = "Error while detecting XML document type from input source";
891 throw new MCRException(msg, ex);
892 }
893 }
894
895 return detected.getProperty("docType");
896 }
897
898 }