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