001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *        http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 */
019
020package org.apache.isis.core.commons.xml;
021
022import java.io.File;
023import java.io.FileInputStream;
024import java.io.FileNotFoundException;
025import java.io.FileOutputStream;
026import java.io.FilenameFilter;
027import java.io.IOException;
028import java.io.InputStreamReader;
029import java.io.OutputStreamWriter;
030
031import org.xml.sax.ContentHandler;
032import org.xml.sax.InputSource;
033import org.xml.sax.SAXException;
034import org.xml.sax.SAXParseException;
035import org.xml.sax.XMLReader;
036import org.xml.sax.helpers.XMLReaderFactory;
037
038import org.apache.isis.core.commons.exceptions.IsisException;
039import org.apache.isis.core.commons.util.ToString;
040
041public class XmlFile {
042    private static final String[] ESCAPE_STRING = { "&", "<", ">", """, "'" };
043    private static final String[] SPECIAL_CHARACTERS = { "&", "<", ">", "\"", "'" };
044
045    public static String getValueWithSpecialsEscaped(final String s) {
046        String result = s;
047        for (int i = 0; i < SPECIAL_CHARACTERS.length; i++) {
048            final String special = SPECIAL_CHARACTERS[i];
049            int pos = -1;
050            while (true) {
051                pos = result.indexOf(special, pos + 1);
052                if (pos < 0) {
053                    break;
054                }
055                result = result.substring(0, pos) + ESCAPE_STRING[i] + result.substring(pos + special.length());
056            }
057        }
058        return result;
059    }
060
061    private final String charset;
062    private final File directory;
063
064    public XmlFile(final String charset, final String directory) {
065        this.directory = new File(directory);
066        createDirectoryIfRequired();
067        this.charset = charset;
068    }
069
070    private void createDirectoryIfRequired() {
071        if (this.directory.exists()) {
072            return;
073        }
074        this.directory.mkdirs();
075    }
076
077    private void createDirectoryIfRequired(File file) {
078        if(file.exists()) {
079            return;
080        }
081        file.getParentFile().mkdirs();
082    }
083
084    public File getDirectory() {
085        return directory;
086    }
087
088    private File file(final String fileName) {
089        return new File(directory, fileName + ".xml");
090    }
091
092    public void writeXml(final String name, final ContentWriter writer) {
093        OutputStreamWriter pw;
094
095        try {
096            final File file = file(name);
097            createDirectoryIfRequired(file);
098            pw = new OutputStreamWriter(new FileOutputStream(file), charset);
099            pw.write("<?xml version=\"1.0\" encoding=\"" + charset + "\" ?>\n");
100            pw.write("\n");
101            writer.write(pw);
102            pw.write("\n");
103            pw.close();
104        } catch (final IOException e) {
105            throw new IsisException("Problems writing data files", e);
106        }
107    }
108
109
110    public boolean parse(final ContentHandler handler, final String fileName) {
111        XMLReader parser;
112
113        try {
114            parser = XMLReaderFactory.createXMLReader();
115        } catch (final SAXException e) {
116            try {
117                parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
118            } catch (final SAXException e2) {
119                try {
120                    parser = XMLReaderFactory.createXMLReader("org.apache.crimson.parser.XMLReaderImpl");
121                } catch (final SAXException failed) {
122                    throw new IsisException("Couldn't locate a SAX parser");
123                }
124            }
125        }
126
127        final File file = file(fileName);
128        try {
129            parser.setContentHandler(handler);
130            final FileInputStream fis = new FileInputStream(file);
131            final InputStreamReader isr = new InputStreamReader(fis, charset);
132            final InputSource is = new InputSource(isr);
133            parser.parse(is);
134
135            return true;
136        } catch (final FileNotFoundException e) {
137            return false;
138        } catch (final IOException e) {
139            throw new IsisException("Error reading XML file", e);
140        } catch (final SAXParseException e) {
141            throw new IsisException("Error while parsing: " + e.getMessage() + " in " + file + ")", e);
142        } catch (final SAXException e) {
143            throw new IsisException("Error in file " + file + " ", e);
144        }
145    }
146
147    public void delete(final String fileName) {
148        file(fileName).delete();
149    }
150
151    /**
152     * The XML store is deemed to be initialised if the directory used to store
153     * the data has no xml files in it.
154     */
155    public boolean isFixturesInstalled() {
156        final String[] list = directory.list(new FilenameFilter() {
157            @Override
158            public boolean accept(final File dir, final String name) {
159                return name.toLowerCase().endsWith(".xml");
160            }
161        });
162        return list.length > 0;
163    }
164    
165    @Override
166    public String toString() {
167        return new ToString(this).append("directory", this.directory).toString();
168    }
169    
170}