001    /*
002     * $Revision: 15011 $ 
003     * $Date: 2009-03-25 11:30:44 +0100 (Wed, 25 Mar 2009) $
004     *
005     * This file is part of ***  M y C o R e  ***
006     * See http://www.mycore.de/ for details.
007     *
008     * This program is free software; you can use it, redistribute it
009     * and / or modify it under the terms of the GNU General Public License
010     * (GPL) as published by the Free Software Foundation; either version 2
011     * of the License or (at your option) any later version.
012     *
013     * This program is distributed in the hope that it will be useful, but
014     * WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016     * GNU General Public License for more details.
017     *
018     * You should have received a copy of the GNU General Public License
019     * along with this program, in a file called gpl.txt or license.txt.
020     * If not, write to the Free Software Foundation Inc.,
021     * 59 Temple Place - Suite 330, Boston, MA  02111-1307 USA
022     */
023    
024    package org.mycore.datamodel.ifs2;
025    
026    import java.io.ByteArrayInputStream;
027    import java.io.ByteArrayOutputStream;
028    import java.io.File;
029    import java.io.FileInputStream;
030    import java.io.FileOutputStream;
031    import java.io.IOException;
032    import java.io.InputStream;
033    import java.io.OutputStream;
034    import java.io.UnsupportedEncodingException;
035    import java.net.URL;
036    
037    import org.apache.commons.vfs.FileObject;
038    import org.apache.commons.vfs.FileSystemException;
039    import org.apache.commons.vfs.VFS;
040    import org.jdom.Document;
041    import org.jdom.JDOMException;
042    import org.jdom.input.SAXBuilder;
043    import org.jdom.output.Format;
044    import org.jdom.output.XMLOutputter;
045    import org.mycore.common.MCRUsageException;
046    import org.mycore.common.MCRUtils;
047    
048    /**
049     * Used to read/write content from any source to any target. Sources and targets
050     * can be strings, local files, Apache VFS file objects, XML documents, byte[]
051     * arrays and streams. MCRContent can only be consumed once, otherwise the
052     * getters throw MCRUsageException. Use makeCopies() to avoid this. The
053     * underlying input stream is closed after consumption, excepted you use
054     * getInputStream(), of course.
055     * 
056     * @author Frank Lützenkirchen
057     */
058    public class MCRContent {
059    
060        /**
061         * The content itself
062         */
063        protected InputStream in;
064    
065        /**
066         * If true, this content already was used and cannot be used again
067         */
068        protected boolean consumed = false;
069    
070        /**
071         * If true, we are absolutely sure that source is XML
072         */
073        protected boolean isXML = false;
074    
075        /**
076         * Creates content from a String, using UTF-8 encoding
077         * 
078         * @param text
079         *            the content
080         */
081        public static MCRContent readFrom(String text) throws IOException, UnsupportedEncodingException {
082            return readFrom(text, "UTF-8");
083        }
084    
085        /**
086         * Creates content from a String, using the given encoding
087         * 
088         * @param text
089         *            the content
090         * @param encoding
091         *            the encoding to be used to write bytes
092         */
093        public static MCRContent readFrom(String text, String encoding) throws IOException, UnsupportedEncodingException {
094            return readFrom(text.getBytes(encoding));
095        }
096    
097        /**
098         * Creates content from a local file
099         * 
100         * @param file
101         *            the local file to read
102         */
103        public static MCRContent readFrom(File file) throws IOException {
104            return readFrom(new FileInputStream(file));
105        }
106    
107        /**
108         * Creates content from Apache VFS file object
109         * 
110         * @param fo
111         *            the file object to read content from
112         */
113        public static MCRContent readFrom(FileObject fo) throws FileSystemException {
114            return readFrom(fo.getContent().getInputStream());
115        }
116    
117        /**
118         * Creates content from byte[] arrray
119         * 
120         * @param bytes
121         *            the content's bytes
122         */
123        public static MCRContent readFrom(byte[] bytes) throws IOException {
124            return readFrom(new ByteArrayInputStream(bytes));
125        }
126    
127        /**
128         * Creates content from XML document. Content will be written
129         * pretty-formatted, using UTF-8 encoding and line indentation.
130         * 
131         * @param xml
132         *            the XML document to read in as content
133         */
134        public static MCRContent readFrom(Document xml) throws Exception {
135            ByteArrayOutputStream out = new ByteArrayOutputStream();
136            XMLOutputter xout = new XMLOutputter();
137            xout.setFormat(Format.getPrettyFormat().setEncoding("UTF-8").setIndent("  "));
138            xout.output(xml, out);
139            out.close();
140            MCRContent content = readFrom(out.toByteArray());
141            content.isXML = true;
142            return content;
143        }
144    
145        /**
146         * Creates new content from input stream
147         * 
148         * @param in
149         *            the input stream to read content from
150         */
151        public static MCRContent readFrom(InputStream in) {
152            return new MCRContent(in);
153        }
154    
155        private MCRContent(InputStream in) {
156            this.in = in;
157        }
158    
159        /**
160         * Creates new content reading from the given URL.
161         * 
162         * @param url
163         *            the url to read content from
164         */
165        public static MCRContent readFrom(URL url) throws FileSystemException {
166            return readFrom(VFS.getManager().resolveFile(url.toExternalForm()));
167        }
168    
169        /**
170         * Ensures that content is XML
171         */
172        public MCRContent ensureXML() throws Exception {
173            if (isXML)
174                return this;
175            else
176                return MCRContent.readFrom(asXML());
177        }
178    
179        /**
180         * Returns content as input stream. Be sure to close this stream properly!
181         * 
182         * @return input stream to read content from
183         */
184        public InputStream getInputStream() {
185            checkConsumed();
186            return in;
187        }
188    
189        /**
190         * Returns content as content input stream, which provides MD5
191         * functionality. Be sure to close this stream properly!
192         * 
193         * @return the content input stream
194         */
195        public MCRContentInputStream getContentInputStream() {
196            if (!(in instanceof MCRContentInputStream))
197                in = new MCRContentInputStream(in);
198            return (MCRContentInputStream) in;
199        }
200    
201        /**
202         * Sends content to the given OutputStream
203         * 
204         * @param out
205         *            the OutputStream to write the content to
206         */
207        public void sendTo(OutputStream out) throws IOException {
208            checkConsumed();
209            MCRUtils.copyStream(in, out);
210            in.close();
211        }
212    
213        /**
214         * Sends content to the given local file
215         * 
216         * @param target
217         *            the file to write the content to
218         */
219        public void sendTo(File target) throws IOException {
220            OutputStream out = new FileOutputStream(target);
221            sendTo(out);
222            out.close();
223        }
224    
225        /**
226         * Sends the content to the given file object
227         * 
228         * @param target
229         *            the file to write the content to
230         */
231        public void sendTo(FileObject target) throws IOException, FileSystemException {
232            OutputStream out = target.getContent().getOutputStream();
233            sendTo(out);
234            out.close();
235        }
236    
237        /**
238         * Parses content, assuming it is XML, and returns the parsed document.
239         * 
240         * @return the XML document parsed from content
241         */
242        public Document asXML() throws JDOMException, IOException {
243            checkConsumed();
244            Document xml = new SAXBuilder().build(in);
245            in.close();
246            return xml;
247        }
248    
249        /**
250         * Returns the raw content
251         * 
252         * @return the content
253         */
254        public byte[] asByteArray() throws IOException {
255            ByteArrayOutputStream baos = new ByteArrayOutputStream();
256            sendTo(baos);
257            baos.close();
258            return baos.toByteArray();
259        }
260    
261        /**
262         * Returns the content as String, assuming the provided encoding
263         * 
264         * @param encoding
265         *            the encoding to use to build the characters
266         * @return content as String
267         */
268        public String asString(String encoding) throws IOException, UnsupportedEncodingException {
269            return new String(asByteArray(), encoding);
270        }
271    
272        /**
273         * Returns content as String, assuming UTF-8 encoding
274         * 
275         * @return content as String
276         */
277        public String asString() throws IOException, UnsupportedEncodingException {
278            return asString("UTF-8");
279        }
280    
281        /**
282         * Ensures that this content is not already consumed, because it can only be
283         * used once.
284         */
285        protected void checkConsumed() {
286            if (consumed)
287                throw new MCRUsageException("MCRContent is already consumed, can only be used once");
288            else
289                consumed = true;
290        }
291    
292        /**
293         * Makes copies of the content, consuming this content
294         * 
295         * @param numCopies
296         *            the number of copies to make
297         * @return copies of the content
298         */
299        public MCRContent[] makeCopies(int numCopies) throws IOException {
300            MCRContent[] copies = new MCRContent[numCopies];
301            byte[] bytes = asByteArray();
302            for (int i = 0; i < numCopies; i++)
303                copies[i] = MCRContent.readFrom(bytes);
304            return copies;
305        }
306    }