001    package org.maltparser.core.config;
002    
003    import java.io.BufferedInputStream;
004    import java.io.BufferedOutputStream;
005    import java.io.BufferedReader;
006    import java.io.BufferedWriter;
007    import java.io.File;
008    import java.io.FileInputStream;
009    import java.io.FileNotFoundException;
010    import java.io.FileOutputStream;
011    import java.io.FileReader;
012    import java.io.FileWriter;
013    import java.io.IOException;
014    import java.io.InputStream;
015    import java.io.InputStreamReader;
016    import java.io.OutputStreamWriter;
017    import java.io.UnsupportedEncodingException;
018    import java.net.MalformedURLException;
019    import java.net.URL;
020    import java.nio.ByteBuffer;
021    import java.nio.CharBuffer;
022    import java.util.Date;
023    import java.util.Enumeration;
024    import java.util.HashMap;
025    import java.util.HashSet;
026    import java.util.SortedSet;
027    import java.util.TreeSet;
028    import java.util.jar.JarEntry;
029    import java.util.jar.JarFile;
030    import java.util.jar.JarInputStream;
031    import java.util.jar.JarOutputStream;
032    
033    import org.maltparser.core.config.version.Versioning;
034    import org.maltparser.core.exception.MaltChainedException;
035    import org.maltparser.core.helper.SystemInfo;
036    import org.maltparser.core.helper.SystemLogger;
037    import org.maltparser.core.helper.Util;
038    import org.maltparser.core.io.dataformat.DataFormatInstance;
039    import org.maltparser.core.io.dataformat.DataFormatManager;
040    import org.maltparser.core.options.OptionManager;
041    import org.maltparser.core.symbol.SymbolTableHandler;
042    import org.maltparser.core.symbol.trie.TrieSymbolTableHandler;
043    
044    
045    /**
046    * This class contains methods for handle the configuration directory.
047    *
048    * @author Johan Hall
049    */
050    public class ConfigurationDir  {
051            protected static final int BUFFER = 4096;
052            protected File configDirectory;
053            protected String name;
054            protected String type;
055            protected File workingDirectory;
056            protected URL url = null;
057            protected int containerIndex;
058            protected BufferedWriter infoFile = null;
059            protected String createdByMaltParserVersion;
060    
061            private SymbolTableHandler symbolTables;
062            private DataFormatManager dataFormatManager;
063            private HashMap<String,DataFormatInstance> dataFormatInstances;
064            
065            
066            /**
067             * Creates a configuration directory from a mco-file specified by an URL.
068             * 
069             * @param url   an URL to a mco-file
070             * @throws MaltChainedException
071             */
072            public ConfigurationDir(URL url) throws MaltChainedException {
073                    initWorkingDirectory();
074                    setUrl(url);
075                    initNameNTypeFromInfoFile(url);
076    //              initData();
077            }
078            
079            /**
080             * Creates a new configuration directory or a configuration directory from a mco-file
081             * 
082             * @param name  the name of the configuration
083             * @param type  the type of configuration
084             * @param containerIndex        the container index
085             * @throws MaltChainedException
086             */
087            public ConfigurationDir(String name, String type, int containerIndex) throws MaltChainedException {
088                    setContainerIndex(containerIndex);
089    
090                    initWorkingDirectory();
091                    if (name != null && name.length() > 0 && type != null && type.length() > 0) {
092                            setName(name);
093                            setType(type);
094                    } else {
095                            throw new ConfigurationException("The configuration name is not specified. ");
096                    }
097                    setConfigDirectory(new File(workingDirectory.getPath()+File.separator+getName()));
098            }
099            
100            public void initDataFormat() throws MaltChainedException {
101                    String inputFormatName = OptionManager.instance().getOptionValue(containerIndex, "input", "format").toString().trim();
102                    String outputFormatName = OptionManager.instance().getOptionValue(containerIndex, "output", "format").toString().trim();
103    //              SystemLogger.logger().info(inputFormatName + "\n");
104    //              SystemLogger.logger().info(outputFormatName + "\n");
105                    if (configDirectory != null && configDirectory.exists()) {
106                            if (outputFormatName.length() == 0 || inputFormatName.equals(outputFormatName)) {
107                                    URL inputFormatURL = Util.findURLinJars(inputFormatName);
108                                    if (inputFormatURL != null) {
109                                            outputFormatName = inputFormatName = this.copyToConfig(inputFormatURL);
110                                    } else {
111                                            outputFormatName = inputFormatName = this.copyToConfig(inputFormatName);
112                                    }
113                            } else {
114                                    URL inputFormatURL = Util.findURLinJars(inputFormatName);
115                                    if (inputFormatURL != null) {
116                                            inputFormatName = this.copyToConfig(inputFormatURL);
117                                    } else {
118                                            inputFormatName = this.copyToConfig(inputFormatName);
119                                    }
120                                    URL outputFormatURL = Util.findURLinJars(outputFormatName);
121                                    if (inputFormatURL != null) {
122                                            outputFormatName = this.copyToConfig(outputFormatURL);
123                                    } else {
124                                            outputFormatName = this.copyToConfig(outputFormatName);
125                                    }
126                            }
127                            OptionManager.instance().overloadOptionValue(containerIndex, "input", "format", inputFormatName);
128                    } else {
129                            if (outputFormatName.length() == 0) {
130                                    outputFormatName = inputFormatName;
131                            }
132                    }
133                    dataFormatInstances = new HashMap<String, DataFormatInstance>(3);
134    
135                    URL inURL = findURL(inputFormatName);
136                    URL outURL = findURL(outputFormatName);
137    //              SystemLogger.logger().info(inputFormatName + "\n");
138    //              SystemLogger.logger().info(outputFormatName + "\n");
139    //              SystemLogger.logger().info(inURL + "\n");
140    //              SystemLogger.logger().info(outURL + "\n");
141                    
142                    if (outURL != null) {
143                            try {
144                                    InputStream is = outURL.openStream();
145                            } catch (FileNotFoundException e) {
146                                    outURL = Util.findURL(outputFormatName);
147                            } catch (IOException e) {
148                                    outURL = Util.findURL(outputFormatName);
149                            }
150                    } else {
151                            outURL = Util.findURL(outputFormatName);
152                    }
153                    dataFormatManager = new DataFormatManager(inURL, outURL);
154                    symbolTables = new TrieSymbolTableHandler();
155            }
156            
157            private URL findURL(String specModelFileName) throws MaltChainedException {
158                    URL url = null;
159                    File specFile = this.getFile(specModelFileName);
160                    if (specFile.exists()) {
161                            try {
162                                    url = new URL("file:///"+specFile.getAbsolutePath());
163                            } catch (MalformedURLException e) {
164                                    throw new MaltChainedException("Malformed URL: "+specFile, e);
165                            }
166                    } else {
167                            url = this.getConfigFileEntryURL(specModelFileName);
168                    }
169                    return url;
170            }
171            
172            /**
173             * Creates an output stream writer, where the corresponding file will be included in the configuration directory
174             * 
175             * @param fileName      a file name
176             * @param charSet       a char set
177             * @return      an output stream writer for writing to a file within the configuration directory
178             * @throws MaltChainedException
179             */
180            public OutputStreamWriter getOutputStreamWriter(String fileName, String charSet) throws MaltChainedException {
181                    try {
182                            return new OutputStreamWriter(new FileOutputStream(configDirectory.getPath()+File.separator+fileName), charSet);
183                    } catch (FileNotFoundException e) {
184                            throw new ConfigurationException("The file '"+fileName+"' cannot be created. ", e);
185                    } catch (UnsupportedEncodingException e) {
186                            throw new ConfigurationException("The char set '"+charSet+"' is not supported. ", e);
187                    }
188            }
189            
190            /**
191             * Creates an output stream writer, where the corresponding file will be included in the 
192             * configuration directory. Uses UTF-8 for character encoding.
193             * 
194             * @param fileName      a file name
195             * @return an output stream writer for writing to a file within the configuration directory
196             * @throws MaltChainedException
197             */
198            public OutputStreamWriter getOutputStreamWriter(String fileName) throws MaltChainedException {
199                    try {
200                            return new OutputStreamWriter(new FileOutputStream(configDirectory.getPath()+File.separator+fileName, true), "UTF-8");
201                    } catch (FileNotFoundException e) {
202                            throw new ConfigurationException("The file '"+fileName+"' cannot be created. ", e);
203                    } catch (UnsupportedEncodingException e) {
204                            throw new ConfigurationException("The char set 'UTF-8' is not supported. ", e);
205                    }
206            }
207            /**
208             * This method acts the same as getOutputStreamWriter with the difference that the writer append in the file
209             * if it already exists instead of deleting the previous content before starting to write.
210             * 
211             * @param fileName      a file name
212             * @return an output stream writer for writing to a file within the configuration directory
213             * @throws MaltChainedException
214             */
215            public OutputStreamWriter getAppendOutputStreamWriter(String fileName) throws MaltChainedException {
216                    try {
217                            return new OutputStreamWriter(new FileOutputStream(configDirectory.getPath()+File.separator+fileName, true), "UTF-8");
218                    } catch (FileNotFoundException e) {
219                            throw new ConfigurationException("The file '"+fileName+"' cannot be created. ", e);
220                    } catch (UnsupportedEncodingException e) {
221                            throw new ConfigurationException("The char set 'UTF-8' is not supported. ", e);
222                    }
223            }
224            
225            /**
226             * Creates an input stream reader for reading a file within the configuration directory
227             * 
228             * @param fileName      a file name
229             * @param charSet       a char set
230             * @return an input stream reader for reading a file within the configuration directory
231             * @throws MaltChainedException
232             */
233            public InputStreamReader getInputStreamReader(String fileName, String charSet) throws MaltChainedException {
234                    try {
235                            return new InputStreamReader(new FileInputStream(configDirectory.getPath()+File.separator+fileName), charSet);
236                    } catch (FileNotFoundException e) {
237                            throw new ConfigurationException("The file '"+fileName+"' cannot be found. ", e);
238                    } catch (UnsupportedEncodingException e) {
239                            throw new ConfigurationException("The char set '"+charSet+"' is not supported. ", e);
240                    }
241            }
242            
243            /**
244             * Creates an input stream reader for reading a file within the configuration directory.
245             * Uses UTF-8 for character encoding.
246             * 
247             * @param fileName      a file name
248             * @return      an input stream reader for reading a file within the configuration directory
249             * @throws MaltChainedException
250             */
251            public InputStreamReader getInputStreamReader(String fileName) throws MaltChainedException {
252                    return getInputStreamReader(fileName, "UTF-8");
253            }
254            
255            public JarEntry getConfigFileEntry(String fileName) throws MaltChainedException {
256                    File mcoPath = new File(workingDirectory.getPath()+File.separator+getName()+".mco");
257                    try {
258                            JarFile mcoFile = new JarFile(mcoPath.getAbsolutePath());
259                            JarEntry entry = mcoFile.getJarEntry(getName()+File.separator+fileName);
260                            return entry;
261                    } catch (FileNotFoundException e) {
262                            throw new ConfigurationException("The file entry '"+fileName+"' in mco-file '"+mcoPath+"' cannot be found. ", e);
263                    } catch (IOException e) {
264                            throw new ConfigurationException("The file entry '"+fileName+"' in mco-file '"+mcoPath+"' cannot be found. ", e);
265                    }
266            }
267            
268            public InputStreamReader getInputStreamReaderFromConfigFileEntry(String fileName, String charSet) throws MaltChainedException {
269                    File mcoPath = new File(workingDirectory.getPath()+File.separator+getName()+".mco");
270                    try {
271                            JarFile mcoFile = new JarFile(mcoPath.getAbsolutePath());
272                            JarEntry entry = mcoFile.getJarEntry(getName()+File.separator+fileName);
273                            return new InputStreamReader(mcoFile.getInputStream(entry),  charSet);
274                    } catch (FileNotFoundException e) {
275                            throw new ConfigurationException("The file entry '"+fileName+"' in the mco file '"+mcoPath+"' cannot be found. ", e);
276                    } catch (UnsupportedEncodingException e) {
277                            throw new ConfigurationException("The char set '"+charSet+"' is not supported. ", e);
278                    } catch (IOException e) {
279                            throw new ConfigurationException("The file entry '"+fileName+"' in the mco file '"+mcoPath+"' cannot be loaded. ", e);
280                    }
281            }
282            
283            public InputStreamReader getInputStreamReaderFromConfigFile(String fileName) throws MaltChainedException {
284                    return getInputStreamReaderFromConfigFileEntry(fileName, "UTF-8");
285            }
286            
287            /**
288             * Returns a file handler object of a file within the configuration directory
289             * 
290             * @param fileName      a file name
291             * @return      a file handler object of a file within the configuration directory
292             * @throws MaltChainedException
293             */
294            public File getFile(String fileName) throws MaltChainedException {
295                    return new File(configDirectory.getPath()+File.separator+fileName);
296            }
297            
298            public URL getConfigFileEntryURL(String fileName) throws MaltChainedException {
299                    File mcoPath = new File(workingDirectory.getPath()+File.separator+getName()+".mco");
300                    try {
301                            if (!mcoPath.exists()) {
302                                    throw new ConfigurationException("Couldn't find mco-file '" +mcoPath.getAbsolutePath()+ "'");
303                            }
304                            new URL("file", null, mcoPath.getAbsolutePath());
305                            return new URL("jar:"+new URL("file", null, mcoPath.getAbsolutePath())+"!/"+getName()+File.separator+fileName + "\n");
306                    } catch (MalformedURLException e) {
307                            throw new ConfigurationException("Couldn't find the URL '" +"jar:"+mcoPath.getAbsolutePath()+"!/"+getName()+File.separator+fileName+ "'", e);
308                    }
309            }
310            
311        /**
312         * Copies a file into the configuration directory.
313         * 
314         * @param source    a path to file 
315         * @throws MaltChainedException
316         */
317        public String copyToConfig(File source) throws MaltChainedException {
318            byte[] readBuffer = new byte[BUFFER];
319            String destination = configDirectory.getPath()+File.separator+source.getName();
320            try {
321                    BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source));
322                    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destination), BUFFER);
323                
324                    int n = 0;
325                        while ((n = bis.read(readBuffer, 0, BUFFER)) != -1) {
326                            bos.write(readBuffer, 0, n);
327                        }
328                    bos.flush();
329                    bos.close();
330                    bis.close();
331                    } catch (FileNotFoundException e) {
332                            throw new ConfigurationException("The source file '"+source+"' cannot be found or the destination file '"+destination+"' cannot be created when coping the file. ", e);
333                    } catch (IOException e) {
334                            throw new ConfigurationException("The source file '"+source+"' cannot be copied to destination '"+destination+"'. ", e);
335                    }
336                    return source.getName();
337        }
338        
339    
340        public String copyToConfig(String fileUrl) throws MaltChainedException {
341            URL url = Util.findURL(fileUrl);
342            if (url == null) {
343                    throw new ConfigurationException("The file or URL '"+fileUrl+"' could not be found. ");
344            }
345            return copyToConfig(url);
346        }
347        
348        public String copyToConfig(URL url) throws MaltChainedException {
349            if (url == null) {
350                    throw new ConfigurationException("URL could not be found. ");
351            }
352            byte[] readBuffer = new byte[BUFFER];
353            String destFileName = url.getPath();
354                    int indexSlash = destFileName.lastIndexOf('/');
355                    if (indexSlash == -1) {
356                            indexSlash = destFileName.lastIndexOf('\\');
357                    }
358            
359                    if (indexSlash != -1) {
360                            destFileName = destFileName.substring(indexSlash+1);
361                    }
362            
363            String destination = configDirectory.getPath()+File.separator+destFileName;
364            try {
365                    BufferedInputStream bis = new BufferedInputStream(url.openStream());
366                    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destination), BUFFER);
367                
368                    int n = 0;
369                        while ((n = bis.read(readBuffer, 0, BUFFER)) != -1) {
370                            bos.write(readBuffer, 0, n);
371                        }
372                    bos.flush();
373                    bos.close();
374                    bis.close();
375                    } catch (FileNotFoundException e) {
376                            throw new ConfigurationException("The destination file '"+destination+"' cannot be created when coping the file. ", e);
377                    } catch (IOException e) {
378                            throw new ConfigurationException("The URL '"+url+"' cannot be copied to destination '"+destination+"'. ", e);
379                    }
380                    return destFileName;
381        }
382     
383        
384            /**
385             * Removes the configuration directory, if it exists and it contains a .info file. 
386             * 
387             * @throws MaltChainedException
388             */
389            public void deleteConfigDirectory() throws MaltChainedException {
390                    if (!configDirectory.exists()) {
391                            return;
392                    }
393                    File infoFile = new File(configDirectory.getPath()+File.separator+getName()+"_"+getType()+".info");
394                    if (infoFile.exists()) {
395                            deleteConfigDirectory(configDirectory);
396                    } else {
397                            throw new ConfigurationException("There exists a directory that is not a MaltParser configuration directory. ");
398                    }
399            }
400            
401            private void deleteConfigDirectory(File directory) throws MaltChainedException {
402                    if (directory.exists()) {
403                            File[] files = directory.listFiles();
404                            for (int i = 0; i < files.length; i++) {
405                                    if (files[i].isDirectory()) {
406                                            deleteConfigDirectory(files[i]);
407                                    } else {
408                                            files[i].delete();
409                                    }
410                            }
411                    } else {
412                            throw new ConfigurationException("The directory '"+directory.getPath()+ "' cannot be found. ");
413                    }
414                    directory.delete();
415            }
416            
417            /**
418             * Returns a file handler object for the configuration directory
419             * 
420             * @return a file handler object for the configuration directory
421             */
422            public File getConfigDirectory() {
423                    return configDirectory;
424            }
425    
426            protected void setConfigDirectory(File dir) {
427                    this.configDirectory = dir;
428            }
429    
430            /**
431             * Creates the configuration directory
432             * 
433             * @throws MaltChainedException
434             */
435            public void createConfigDirectory() throws MaltChainedException {
436                    checkConfigDirectory();
437                    configDirectory.mkdir();
438                    createInfoFile();
439            }
440            
441            protected void checkConfigDirectory()  throws MaltChainedException {
442                    if (configDirectory.exists() && !configDirectory.isDirectory()) {
443                            throw new ConfigurationException("The configuration directory name already exists and is not a directory. ");
444                    }
445                    
446                    if (configDirectory.exists()) {
447                            deleteConfigDirectory();
448                    } 
449            }
450            
451            protected void createInfoFile() throws MaltChainedException {
452                    infoFile = new BufferedWriter(getOutputStreamWriter(getName()+"_"+getType()+".info"));
453                    try {
454                            infoFile.write("CONFIGURATION\n");
455                            infoFile.write("Configuration name:   "+getName()+"\n");
456                            infoFile.write("Configuration type:   "+getType()+"\n");
457                            infoFile.write("Created:              "+new Date(System.currentTimeMillis())+"\n");
458                            
459                            infoFile.write("\nSYSTEM\n");
460                            infoFile.write("Operating system architecture: "+System.getProperty("os.arch")+"\n");
461                            infoFile.write("Operating system name:         "+System.getProperty("os.name")+"\n");
462                            infoFile.write("JRE vendor name:               "+System.getProperty("java.vendor")+"\n");
463                            infoFile.write("JRE version number:            "+System.getProperty("java.version")+"\n");
464                            
465                            infoFile.write("\nMALTPARSER\n");
466                            infoFile.write("Version:                       "+SystemInfo.getVersion()+"\n");
467                            infoFile.write("Build date:                    "+SystemInfo.getBuildDate()+"\n");
468                            HashSet<String> excludeGroups = new HashSet<String>();
469                            excludeGroups.add("system");
470                            infoFile.write("\nSETTINGS\n");
471                            infoFile.write(OptionManager.instance().toStringPrettyValues(containerIndex, excludeGroups));
472                            infoFile.flush();
473                    } catch (IOException e) {
474                            throw new ConfigurationException("Could not create the maltparser info file. ");
475                    }
476            }
477            
478            /**
479             * Returns a writer to the configuration information file
480             * 
481             * @return      a writer to the configuration information file
482             * @throws MaltChainedException
483             */
484            public BufferedWriter getInfoFileWriter() throws MaltChainedException {
485                    return infoFile;
486            }
487            
488            /**
489             * Creates the malt configuration file (.mco). This file is compressed.   
490             * 
491             * @throws MaltChainedException
492             */
493            public void createConfigFile() throws MaltChainedException {
494                    try {
495                            JarOutputStream jos = new JarOutputStream(new FileOutputStream(workingDirectory.getPath()+File.separator+getName()+".mco"));
496    //                      configLogger.info("Creates configuration file '"+workingDirectory.getPath()+File.separator+getName()+".mco' ...\n");
497                            createConfigFile(configDirectory.getPath(), jos);
498                            jos.close();
499                    } catch (FileNotFoundException e) {
500                            throw new ConfigurationException("The maltparser configurtation file '"+workingDirectory.getPath()+File.separator+getName()+".mco"+"' cannot be found. ", e);
501                    } catch (IOException e) {
502                            throw new ConfigurationException("The maltparser configurtation file '"+workingDirectory.getPath()+File.separator+getName()+".mco"+"' cannot be created. ", e);
503                    } 
504            }
505    
506            private void createConfigFile(String directory, JarOutputStream jos) throws MaltChainedException {
507            byte[] readBuffer = new byte[BUFFER];
508                    try {
509                            File zipDir = new File(directory);
510                            String[] dirList = zipDir.list();
511                            
512                            int bytesIn = 0;
513            
514                            for (int i = 0; i < dirList.length; i++) {
515                                    File f = new File(zipDir, dirList[i]);
516                                    if (f.isDirectory()) {
517                                            String filePath = f.getPath();
518                                            createConfigFile(filePath, jos);
519                                            continue;
520                                    }
521            
522                                    FileInputStream fis = new FileInputStream(f);
523                                    
524                                    
525                                    JarEntry entry = new JarEntry(f.getPath().substring(workingDirectory.getPath().length()+1));
526                                    jos.putNextEntry(entry);
527            
528                                    while ((bytesIn = fis.read(readBuffer)) != -1) {
529                                            jos.write(readBuffer, 0, bytesIn);
530                                    }
531            
532                                    fis.close();
533                            }
534                    } catch (FileNotFoundException e) {
535                            throw new ConfigurationException("The directory '"+directory+"' cannot be found. ", e);
536                    } catch (IOException e) {
537                            throw new ConfigurationException("The directory '"+directory+"' cannot be compressed into a mco file. ", e);
538                    } 
539            }
540            
541    
542            public void copyConfigFile(File in, File out, Versioning versioning) throws MaltChainedException {
543                    try {
544                            JarFile jar = new JarFile(in);
545                            JarOutputStream tempJar = new JarOutputStream(new FileOutputStream(out));
546                    byte[] buffer = new byte[BUFFER];
547                    int bytesRead;
548                    StringBuilder sb = new StringBuilder();
549    
550                    for (Enumeration<JarEntry> entries = jar.entries(); entries.hasMoreElements(); ) {
551                        JarEntry inEntry = (JarEntry) entries.nextElement();
552                        InputStream entryStream = jar.getInputStream(inEntry);
553                        JarEntry outEntry = versioning.getJarEntry(inEntry);
554                        
555                        if (!versioning.hasChanges(inEntry, outEntry)) {
556                                tempJar.putNextEntry(outEntry);
557                                while ((bytesRead = entryStream.read(buffer)) != -1) {
558                                    tempJar.write(buffer, 0, bytesRead);
559                                }
560                        } else {
561                            tempJar.putNextEntry(outEntry);
562                            BufferedReader br = new BufferedReader(new InputStreamReader(entryStream));
563                            String line = null;
564                            sb.setLength(0);
565                            while ((line = br.readLine()) != null) {
566                                    sb.append(line);
567                                    sb.append('\n');
568                            }
569                            String outString = versioning.modifyJarEntry(inEntry, outEntry, sb);
570                            tempJar.write(outString.getBytes());
571                        }
572                    }
573                    if (versioning.getFeatureModelXML() != null && versioning.getFeatureModelXML().startsWith("/appdata")) {
574                            int index = versioning.getFeatureModelXML().lastIndexOf('/');
575                    BufferedInputStream bis = new BufferedInputStream(Util.findURLinJars(versioning.getFeatureModelXML()).openStream());
576                    tempJar.putNextEntry(new JarEntry(versioning.getNewConfigName()+"/" +versioning.getFeatureModelXML().substring(index+1)));
577                    int n = 0;
578                        while ((n = bis.read(buffer, 0, BUFFER)) != -1) {
579                            tempJar.write(buffer, 0, n);
580                        }
581                    bis.close();
582                    }
583                    if (versioning.getInputFormatXML() != null && versioning.getInputFormatXML().startsWith("/appdata")) {
584                            int index = versioning.getInputFormatXML().lastIndexOf('/');
585                    BufferedInputStream bis = new BufferedInputStream(Util.findURLinJars(versioning.getInputFormatXML()).openStream());
586                    tempJar.putNextEntry(new JarEntry(versioning.getNewConfigName()+"/" +versioning.getInputFormatXML().substring(index+1)));
587                    int n = 0;
588                        while ((n = bis.read(buffer, 0, BUFFER)) != -1) {
589                            tempJar.write(buffer, 0, n);
590                        }
591                    bis.close();
592                    }
593                    tempJar.flush();
594                    tempJar.close();
595                    jar.close();
596                    } catch (IOException e) {
597                            throw new ConfigurationException("", e);
598                    }
599            }
600            
601        protected void initNameNTypeFromInfoFile(URL url) throws MaltChainedException {
602                    if (url == null) {
603                            throw new ConfigurationException("The URL cannot be found. ");
604                    }       
605                    try {
606                            JarEntry je;
607                            JarInputStream jis = new JarInputStream(url.openConnection().getInputStream());
608                            while ((je = jis.getNextJarEntry()) != null) {
609                                    String entryName = je.getName();
610                                    if (entryName.endsWith(".info")) {
611                                            int indexUnderScore = entryName.lastIndexOf('_');
612                                            int indexSeparator = entryName.lastIndexOf(File.separator);
613                                            if (indexSeparator == -1) {
614                                                    indexSeparator = entryName.lastIndexOf('/');
615                                            }
616                                            if (indexSeparator == -1) {
617                                                    indexSeparator = entryName.lastIndexOf('\\');
618                                            }
619                                            int indexDot = entryName.lastIndexOf('.');
620                                            if (indexUnderScore == -1 || indexDot == -1) {
621                                                    throw new ConfigurationException("Could not find the configuration name and type from the URL '"+url.toString()+"'. ");
622                                            }
623                                            setName(entryName.substring(indexSeparator+1, indexUnderScore));
624                                            setType(entryName.substring(indexUnderScore+1, indexDot));
625                                            setConfigDirectory(new File(workingDirectory.getPath()+File.separator+getName()));
626                                            jis.close();
627                                            return;
628                                    }
629                            }
630                            
631                    } catch (IOException e) {
632                            throw new ConfigurationException("Could not find the configuration name and type from the URL '"+url.toString()+"'. ", e);
633                    }
634        }
635        
636        /**
637         * Prints the content of the configuration information file to the system logger
638         * 
639         * @throws MaltChainedException
640         */
641        public void echoInfoFile() throws MaltChainedException {
642            checkConfigDirectory();
643            JarInputStream jis;
644            try {
645                    if (url == null) {
646                            jis = new JarInputStream(new FileInputStream(workingDirectory.getPath()+File.separator+getName()+".mco"));
647                    } else {
648                            jis = new JarInputStream(url.openConnection().getInputStream());
649                    }
650                            JarEntry je;
651    
652                            while ((je = jis.getNextJarEntry()) != null) {
653                            String entryName = je.getName();
654    
655                            if (entryName.endsWith(getName()+"_"+getType()+".info")) {
656                                    int c;
657                                        while ((c = jis.read()) != -1) {
658                                            SystemLogger.logger().info((char)c);
659                                        }   
660                            }
661                            }
662                    jis.close();
663            } catch (FileNotFoundException e) {
664                    throw new ConfigurationException("Could not print configuration information file. The configuration file '"+workingDirectory.getPath()+File.separator+getName()+".mco"+"' cannot be found. ", e);
665            } catch (IOException e) {
666                            throw new ConfigurationException("Could not print configuration information file. ", e);
667                    }
668    
669        }
670        
671        /**
672         * Unpacks the malt configuration file (.mco).
673         * 
674         * @throws MaltChainedException
675         */
676        public void unpackConfigFile() throws MaltChainedException {
677            checkConfigDirectory();
678            JarInputStream jis;
679            try {
680                    if (url == null) {
681                            jis = new JarInputStream(new FileInputStream(workingDirectory.getPath()+File.separator+getName()+".mco"));
682                    } else {
683                            jis = new JarInputStream(url.openConnection().getInputStream());
684                    }
685                    unpackConfigFile(jis);
686                    jis.close();
687            } catch (FileNotFoundException e) {
688                    throw new ConfigurationException("Could not unpack configuration. The configuration file '"+workingDirectory.getPath()+File.separator+getName()+".mco"+"' cannot be found. ", e);
689            } catch (IOException e) {
690                    if (configDirectory.exists()) {
691                            deleteConfigDirectory();
692                    }
693                            throw new ConfigurationException("Could not unpack configuration. ", e);
694                    }
695            initCreatedByMaltParserVersionFromInfoFile();
696        }
697    
698        protected void unpackConfigFile(JarInputStream jis) throws MaltChainedException {
699                    try {
700                            JarEntry je;
701                            byte[] readBuffer = new byte[BUFFER];
702                    SortedSet<String> directoryCache  = new TreeSet<String>();
703                            while ((je = jis.getNextJarEntry()) != null) {
704                            String entryName = je.getName();
705    
706                            if (entryName.startsWith("/")) {
707                                    entryName = entryName.substring(1);
708                            }
709                            if (entryName.endsWith(File.separator)) {
710                                return;
711                            }
712                            int index = -1;
713                            if (File.separator.equals("\\")) {
714                                    entryName = entryName.replace('/', '\\');
715                                    index = entryName.lastIndexOf("\\");
716                            } else if (File.separator.equals("/")) {
717                                    entryName = entryName.replace('\\', '/');
718                                    index = entryName.lastIndexOf("/");
719                            }
720                            if (index > 0) {
721                                String dirName = entryName.substring(0, index);
722                                if (!directoryCache.contains(dirName)) {
723                                    File directory = new File(workingDirectory.getPath()+File.separator+dirName);
724                                    if (!(directory.exists() && directory.isDirectory())) {
725                                        if (!directory.mkdirs()) {
726                                            throw new ConfigurationException("Unable to make directory '" + dirName +"'. ");
727                                        }
728                                        directoryCache.add(dirName);
729                                    }
730                                }
731                            }
732                           
733                            if (new File(workingDirectory.getPath()+File.separator+entryName).isDirectory() && new File(workingDirectory.getPath()+File.separator+entryName).exists()) {
734                                    continue;
735                            }
736                            BufferedOutputStream bos;
737                            try {
738                                    bos = new BufferedOutputStream(new FileOutputStream(workingDirectory.getPath()+File.separator+entryName), BUFFER);
739                            } catch (FileNotFoundException e) {
740                                            throw new ConfigurationException("Could not unpack configuration. The file '"+workingDirectory.getPath()+File.separator+entryName+"' cannot be unpacked. ", e);
741                            }
742                                    int n = 0;
743                                while ((n = jis.read(readBuffer, 0, BUFFER)) != -1) {
744                                    bos.write(readBuffer, 0, n);
745                                }
746                                bos.flush();
747                                bos.close();
748                            }
749                    } catch (IOException e) {
750                            throw new ConfigurationException("Could not unpack configuration. ", e);
751                    }
752        }
753                
754            /**
755             * Returns the name of the configuration directory
756             * 
757             * @return the name of the configuration directory
758             */
759            public String getName() {
760                    return name;
761            }
762    
763            protected void setName(String name) {
764                    this.name = name;
765            }
766    
767            /**
768             * Returns the type of the configuration directory
769             * 
770             * @return the type of the configuration directory
771             */
772            public String getType() {
773                    return type;
774            }
775    
776            protected void setType(String type) {
777                    this.type = type;
778            }
779            
780            /**
781             * Returns a file handler object for the working directory
782             * 
783             * @return a file handler object for the working directory
784             */
785            public File getWorkingDirectory() {
786                    return workingDirectory;
787            }
788    
789            /**
790             * Initialize the working directory
791             * 
792             * @throws MaltChainedException
793             */
794            public void initWorkingDirectory() throws MaltChainedException {
795                    try {
796                            initWorkingDirectory(OptionManager.instance().getOptionValue(0, "config", "workingdir").toString());
797                    } catch (NullPointerException e) {
798                            throw new ConfigurationException("The configuration cannot be found.", e);
799                    }
800            }
801    
802            /**
803             * Initialize the working directory according to the path. If the path is equals to "user.dir" or current directory, then the current directory
804             *  will be the working directory.
805             * 
806             * @param pathPrefixString      the path to the working directory
807             * @throws MaltChainedException
808             */
809            public void initWorkingDirectory(String pathPrefixString) throws MaltChainedException {
810                    if (pathPrefixString == null || pathPrefixString.equalsIgnoreCase("user.dir") || pathPrefixString.equalsIgnoreCase(".")) {
811                            workingDirectory = new File(System.getProperty("user.dir"));
812                    } else {
813                            workingDirectory = new File(pathPrefixString);
814                    }
815    
816                    if (workingDirectory == null || !workingDirectory.isDirectory()) {
817                            new ConfigurationException("The specified working directory '"+pathPrefixString+"' is not a directory. ");
818                    }
819            }
820            
821            /**
822             * Returns the URL to the malt configuration file (.mco) 
823             * 
824             * @return the URL to the malt configuration file (.mco)
825             */
826            public URL getUrl() {
827                    return url;
828            }
829    
830            protected void setUrl(URL url) {
831                    this.url = url;
832            }
833            
834            /**
835             * Returns the option container index
836             * 
837             * @return the option container index
838             */
839            public int getContainerIndex() {
840                    return containerIndex;
841            }
842    
843            /**
844             * Sets the option container index
845             * 
846             * @param containerIndex a option container index
847             */
848            public void setContainerIndex(int containerIndex) {
849                    this.containerIndex = containerIndex;
850            }
851    
852            /**
853             * Returns the version number of MaltParser which created the malt configuration file (.mco)
854             * 
855             * @return the version number of MaltParser which created the malt configuration file (.mco)
856             */
857            public String getCreatedByMaltParserVersion() {
858                    return createdByMaltParserVersion;
859            }
860    
861            /**
862             * Sets the version number of MaltParser which created the malt configuration file (.mco)
863             * 
864             * @param createdByMaltParserVersion a version number of MaltParser
865             */
866            public void setCreatedByMaltParserVersion(String createdByMaltParserVersion) {
867                    this.createdByMaltParserVersion = createdByMaltParserVersion;
868            }
869            
870            public void initCreatedByMaltParserVersionFromInfoFile() throws MaltChainedException {
871                    try {
872                            BufferedReader br = new BufferedReader(getInputStreamReaderFromConfigFileEntry(getName()+"_"+getType()+".info", "UTF-8"));
873                            String line = null;
874                            while ((line = br.readLine()) != null) {
875                                    if (line.startsWith("Version:                       ")) {
876                                            setCreatedByMaltParserVersion(line.substring(31));
877                                            break;
878                                    }
879                            }
880                            br.close();
881                    } catch (FileNotFoundException e) {
882                            throw new ConfigurationException("Could not retrieve the version number of the MaltParser configuration.", e);
883                    } catch (IOException e) {
884                            throw new ConfigurationException("Could not retrieve the version number of the MaltParser configuration.", e);
885                    }
886            }
887            
888            public void versioning() throws MaltChainedException {
889                    initCreatedByMaltParserVersionFromInfoFile();
890                    SystemLogger.logger().info("\nCurrent version      : " + SystemInfo.getVersion() + "\n");
891                    SystemLogger.logger().info("Parser model version : " + createdByMaltParserVersion + "\n");
892                    if (SystemInfo.getVersion() == null) {
893                            throw new ConfigurationException("Couln't determine the version of MaltParser");
894                    } else if (createdByMaltParserVersion == null) {
895                            throw new ConfigurationException("Couln't determine the version of the parser model");
896                    } else if (SystemInfo.getVersion().equals(createdByMaltParserVersion)) {
897                            SystemLogger.logger().info("The parser model "+getName()+".mco has already the same version as the current version of MaltParser. \n");
898                            return;
899                    }
900                    
901                    File mcoPath = new File(workingDirectory.getPath()+File.separator+getName()+".mco");
902                    File newMcoPath = new File(workingDirectory.getPath()+File.separator+getName()+"."+SystemInfo.getVersion().trim()+".mco");
903                    Versioning versioning = new Versioning(name, type, mcoPath, createdByMaltParserVersion);
904                    if (!versioning.support(createdByMaltParserVersion)) {
905                            SystemLogger.logger().warn("The parser model '"+ name+ ".mco' is created by MaltParser "+getCreatedByMaltParserVersion()+", which cannot be converted to a MaltParser "+SystemInfo.getVersion()+" parser model.\n");
906                            SystemLogger.logger().warn("Please retrain the parser model with MaltParser "+SystemInfo.getVersion() +" or download MaltParser "+getCreatedByMaltParserVersion()+" from http://maltparser.org/download.html\n");
907                            return;
908                    }
909                    SystemLogger.logger().info("Converts the parser model '"+ mcoPath.getName()+ "' into '"+newMcoPath.getName()+"'....\n");
910                    copyConfigFile(mcoPath, newMcoPath, versioning);
911            }
912            
913            protected void checkNConvertConfigVersion() throws MaltChainedException {
914                    if (createdByMaltParserVersion.startsWith("1.0")) {
915                            SystemLogger.logger().info("  Converts the MaltParser configuration ");
916                            SystemLogger.logger().info("1.0");
917                            SystemLogger.logger().info(" to ");
918                            SystemLogger.logger().info(SystemInfo.getVersion());
919                            SystemLogger.logger().info("\n");
920                            File[] configFiles = configDirectory.listFiles();
921                            for (int i = 0, n = configFiles.length; i < n; i++) {
922                                    if (configFiles[i].getName().endsWith(".mod")) {
923                                            configFiles[i].renameTo(new File(configDirectory.getPath()+File.separator+"odm0."+configFiles[i].getName()));
924                                    }
925                                    if (configFiles[i].getName().endsWith(getName()+".dsm")) {
926                                            configFiles[i].renameTo(new File(configDirectory.getPath()+File.separator+"odm0.dsm"));
927                                    }
928                                    if (configFiles[i].getName().equals("savedoptions.sop")) {
929                                            configFiles[i].renameTo(new File(configDirectory.getPath()+File.separator+"savedoptions.sop.old"));
930                                    }
931                                    if (configFiles[i].getName().equals("symboltables.sym")) {
932                                            configFiles[i].renameTo(new File(configDirectory.getPath()+File.separator+"symboltables.sym.old"));
933                                    }
934                            }
935                            try {
936                                    BufferedReader br = new BufferedReader(new FileReader(configDirectory.getPath()+File.separator+"savedoptions.sop.old"));
937                                    BufferedWriter bw = new BufferedWriter(new FileWriter(configDirectory.getPath()+File.separator+"savedoptions.sop"));
938                                    String line;
939                                    while ((line = br.readLine()) != null) {
940                                            if (line.startsWith("0\tguide\tprediction_strategy")) {
941                                                    bw.write("0\tguide\tdecision_settings\tT.TRANS+A.DEPREL\n");
942                                            } else {
943                                                    bw.write(line);
944                                                    bw.write('\n');
945                                            }
946                                    }
947                                    br.close();
948                                    bw.flush();
949                                    bw.close();
950                                    new File(configDirectory.getPath()+File.separator+"savedoptions.sop.old").delete();
951                            } catch (FileNotFoundException e) {
952                                    throw new ConfigurationException("Could convert savedoptions.sop version 1.0.4 to version 1.1. ", e);
953                            }  catch (IOException e) {
954                                    throw new ConfigurationException("Could convert savedoptions.sop version 1.0.4 to version 1.1. ", e);
955                            }               
956                            try {
957                                    BufferedReader br = new BufferedReader(new FileReader(configDirectory.getPath()+File.separator+"symboltables.sym.old"));
958                                    BufferedWriter bw = new BufferedWriter(new FileWriter(configDirectory.getPath()+File.separator+"symboltables.sym"));
959                                    String line;
960                                    while ((line = br.readLine()) != null) {
961                                            if (line.startsWith("AllCombinedClassTable")) {
962                                                    bw.write("T.TRANS+A.DEPREL\n");
963                                            } else {
964                                                    bw.write(line);
965                                                    bw.write('\n');
966                                            }
967                                    }
968                                    br.close();
969                                    bw.flush();
970                                    bw.close();
971                                    new File(configDirectory.getPath()+File.separator+"symboltables.sym.old").delete();
972                            } catch (FileNotFoundException e) {
973                                    throw new ConfigurationException("Could convert symboltables.sym version 1.0.4 to version 1.1. ", e);
974                            }  catch (IOException e) {
975                                    throw new ConfigurationException("Could convert symboltables.sym version 1.0.4 to version 1.1. ", e);
976                            }
977                    }
978                    if (!createdByMaltParserVersion.startsWith("1.3")) {
979                            SystemLogger.logger().info("  Converts the MaltParser configuration ");
980                            SystemLogger.logger().info(createdByMaltParserVersion);
981                            SystemLogger.logger().info(" to ");
982                            SystemLogger.logger().info(SystemInfo.getVersion());
983                            SystemLogger.logger().info("\n");
984                            
985    
986                            new File(configDirectory.getPath()+File.separator+"savedoptions.sop").renameTo(new File(configDirectory.getPath()+File.separator+"savedoptions.sop.old"));
987                            try {
988                                    BufferedReader br = new BufferedReader(new FileReader(configDirectory.getPath()+File.separator+"savedoptions.sop.old"));
989                                    BufferedWriter bw = new BufferedWriter(new FileWriter(configDirectory.getPath()+File.separator+"savedoptions.sop"));
990                                    String line;
991                                    while ((line = br.readLine()) != null) {
992                                            int index = line.indexOf('\t');
993                                            int container = 0;
994                                            if (index > -1) {
995                                                    container = Integer.parseInt(line.substring(0,index));
996                                            }
997                                            
998                                            if (line.startsWith(container+"\tnivre\tpost_processing")) {
999                                            } else if (line.startsWith(container+"\tmalt0.4\tbehavior")) {
1000                                                    if (line.endsWith("true")) {
1001                                                            SystemLogger.logger().info("MaltParser 1.3 doesn't support MaltParser 0.4 emulation.");
1002                                                            br.close();
1003                                                            bw.flush();
1004                                                            bw.close();
1005                                                            deleteConfigDirectory();
1006                                                            System.exit(0);
1007                                                    }
1008                                            } else if (line.startsWith(container+"\tsinglemalt\tparsing_algorithm")) {
1009                                                    bw.write(container);
1010                                                    bw.write("\tsinglemalt\tparsing_algorithm\t");
1011                                                    if (line.endsWith("NivreStandard")) {
1012                                                            bw.write("class org.maltparser.parser.algorithm.nivre.NivreArcStandardFactory");        
1013                                                    } else if (line.endsWith("NivreEager")) {
1014                                                            bw.write("class org.maltparser.parser.algorithm.nivre.NivreArcEagerFactory");
1015                                                    } else if (line.endsWith("CovingtonNonProjective")) {
1016                                                            bw.write("class org.maltparser.parser.algorithm.covington.CovingtonNonProjFactory");
1017                                                    } else if (line.endsWith("CovingtonProjective")) {
1018                                                            bw.write("class org.maltparser.parser.algorithm.covington.CovingtonProjFactory");
1019                                                    }
1020                                                    bw.write('\n');
1021                                            } else {
1022                                                    bw.write(line);
1023                                                    bw.write('\n');
1024                                            }
1025                                    }
1026                                    br.close();
1027                                    bw.flush();
1028                                    bw.close();
1029                                    new File(configDirectory.getPath()+File.separator+"savedoptions.sop.old").delete();
1030                            } catch (FileNotFoundException e) {
1031                                    throw new ConfigurationException("Could convert savedoptions.sop version 1.0.4 to version 1.1. ", e);
1032                            }  catch (IOException e) {
1033                                    throw new ConfigurationException("Could convert savedoptions.sop version 1.0.4 to version 1.1. ", e);
1034                            }
1035                    }
1036            }
1037            
1038            /**
1039             * Terminates the configuration directory
1040             * 
1041             * @throws MaltChainedException
1042             */
1043            public void terminate() throws MaltChainedException {
1044                    if (infoFile != null) {
1045                            try {
1046                                    infoFile.flush();
1047                                    infoFile.close();
1048                            } catch (IOException e) {
1049                                    throw new ConfigurationException("Could not close configuration information file. ", e);
1050                            }
1051                    }
1052                    symbolTables = null;
1053    //              configuration = null;
1054            }
1055            
1056            /* (non-Javadoc)
1057             * @see java.lang.Object#finalize()
1058             */
1059            protected void finalize() throws Throwable {
1060                    try {
1061                            if (infoFile != null) {
1062                                    infoFile.flush();
1063                                    infoFile.close();
1064                            }
1065                    } finally {
1066                            super.finalize();
1067                    }
1068            }
1069            
1070            public SymbolTableHandler getSymbolTables() {
1071                    return symbolTables;
1072            }
1073    
1074            public void setSymbolTables(SymbolTableHandler symbolTables) {
1075                    this.symbolTables = symbolTables;
1076            }
1077    
1078            public DataFormatManager getDataFormatManager() {
1079                    return dataFormatManager;
1080            }
1081    
1082            public void setDataFormatManager(DataFormatManager dataFormatManager) {
1083                    this.dataFormatManager = dataFormatManager;
1084            }
1085            
1086            public HashMap<String, DataFormatInstance> getDataFormatInstances() {
1087                    return dataFormatInstances;
1088            }
1089    }