All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.powerlibraries.io.builder.InBuilder Maven / Gradle / Ivy

package com.github.powerlibraries.io.builder;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.stream.Stream;
import java.util.zip.DeflaterInputStream;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.ZipInputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import com.github.powerlibraries.io.builder.sources.Source;
import com.github.powerlibraries.io.helper.CompressorRegistry;

/**
 * This builder is used to create an input chain.
 * 
 * @author Manuel Hegner
 *
 */
@SuppressWarnings("unchecked")
public class InBuilder extends CharsetHolder{
	private Source source;
	private boolean decompress=false;
	private Base64.Decoder base64Decoder=null;

	public InBuilder(Source source) {
		this.source=source;
	}
	
	/**
	 * This method will tell the builder to decompress the bytes. The returned reader or stream will contain an 
	 * appropriate decompressor. If the defined source specifies a name with a file ending, the builder will try to 
	 * use the appropriate decompressor for the file extension. If there is no extension or it is unknown this
	 * simply adds a {@link DeflaterOutputStream} to the chain.
	 * 
	 * If you want to add file extensions to the automatic selection see {@link CompressorRegistry#registerWrapper}.
	 * @return this builder
	 */
	public InBuilder decompress() {
		decompress=true;
		return this;
	}
	
	/**
	 * This method will add a {@link Base64.Decoder} to this chain.
	 * @return this builder
	 */
	public InBuilder decodeBase64() {
		base64Decoder=Base64.getDecoder();
		return this;
	}
	
	/**
	 * This method will add a {@link Base64.Decoder} to this chain.
	 * @param decoder the specific decoder that should be used.
	 * @return this builder
	 */
	public InBuilder decodeBase64(Base64.Decoder decoder) {
		base64Decoder=decoder;
		return this;
	}
	
	/**
	 * This method creates a simple {@link InputStream} from this builder with all the chosen options.
	 * @return an {@link InputStream}
	 * @throws IOException if any element of the chain throws an {@link IOException}
	 */
	public InputStream asStream() throws IOException {
		return createInputStream();
	}
	
	/**
	 * This method creates a {@link BufferedReader} from this builder with all the chosen options. It uses the default 
	 * {@link Charset} for that.
	 * @return a {@link BufferedReader}
	 * @throws IOException if any element of the chain throws an {@link IOException}
	 */
	public BufferedReader asReader() throws IOException {
		return new BufferedReader(new InputStreamReader(createInputStream(), getCharset()));
	}
	
	/**
	 * This method creates an {@link ObjectInputStream} from this builder with all the chosen options.
	 * @return a {@link ObjectInputStream}
	 * @throws IOException if any element of the chain throws an {@link IOException}
	 */
	public ObjectInputStream asObjects() throws IOException {
		return new ObjectInputStream(new BufferedInputStream(createInputStream()));
	}
	
	/**
	 * This method creates an {@link DataInputStream} from this builder with all the chosen options.
	 * @return a {@link DataInputStream}
	 * @throws IOException if any element of the chain throws an {@link IOException}
	 */
	public DataInputStream asData() throws IOException {
		return new DataInputStream(new BufferedInputStream(createInputStream()));
	}
	
	/**
	 * This method creates an {@link ZipInputStream} from this builder with all the chosen options.
	 * @return a {@link ZipInputStream}
	 * @throws IOException if any element of the chain throws an {@link IOException}
	 */
	public ZipInputStream asZip() throws IOException {
		return new ZipInputStream(new BufferedInputStream(createInputStream()));
	}
	
	/**
	 * This method reads an XML document from the selected source into a {@link Document}.
	 * It uses a default {@link DocumentBuilderFactory} and {@link DocumentBuilder}.
	 * @return the parsed Document
	 * @throws IOException if any element of the chain throws an {@link IOException}
	 * @throws SAXException if any parse errors occur
	 */
	public Document readXML() throws IOException, SAXException {
		try {
			DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			return readXML(builder);
		} catch (ParserConfigurationException e) {
			throw new RuntimeException("DocumentBuilder creation failed", e);
		}
	}
	/**
	 * This method reads an XML document from the selected source into a {@link Document}.
	 * @param documentBuilder a fresh {@link DocumentBuilder} that is used as parser
	 * @return the parsed Document
	 * @throws IOException if any element of the chain throws an {@link IOException}
	 * @throws SAXException if any parse errors occur
	 */
	public Document readXML(DocumentBuilder documentBuilder) throws IOException, SAXException {
		try(BufferedInputStream in=new BufferedInputStream(this.asStream())) {
			return documentBuilder.parse(in);
		}
	}
	
	/**
	 * This method reads the complete input in a String. Lines are seperated with a single '\n'.
	 * @return a String containing the whole content of the file
	 * @throws IOException if any element of the chain throws an {@link IOException}
	 */
	public String readAll() throws IOException {
		try(BufferedReader in=this.asReader()) {
			StringBuilder sb=new StringBuilder();
			String l;
			while((l=in.readLine())!=null) {
				sb.append(l).append('\n');
			}
			if(sb.length()==0)
				return "";
			else
				return sb.substring(0, sb.length()-1);
		}
	}
	
	/**
	 * This method simply reads the first object from the input using an 
	 * {@link ObjectInputStream} and returns it. 
	 * @param  the expected type of the read object
	 * @return the read Object
	 * @throws ClassNotFoundException if the class of a serialized object cannot be found.
	 * @throws IOException if any element of the chain throws an {@link IOException}
	 */
	public  T readObject() throws ClassNotFoundException, IOException {
		try(ObjectInputStream in=this.asObjects()) {
			return (T)in.readObject();
		}
	}
	
	/**
	 * This method reads as many objects from the input using an 
	 * {@link ObjectInputStream} as it can and returns them. Be aware that
	 * this method will throw an exception if there are other information in the stream
	 * than objects (e.g. primitives). 
	 * @param  the expected supertype of the read objects
	 * @return a list of read objects
	 * @throws ClassNotFoundException if the class of a serialized object cannot be found.
	 * @throws IOException if any element of the chain throws an {@link IOException}
	 */
	public  List readObjects() throws ClassNotFoundException, IOException {
		try(ObjectInputStream in=this.asObjects()) {
			ArrayList objects=new ArrayList<>();
			while(in.available()>0)
				objects.add((T)in.readObject());
			return objects;
		}
	}
	
	/**
	 * This method reads the complete input in a {@link List} of Strings. Each element of the list 
	 * represents one line of the source document.
	 * @return a list of lines containing the whole content of the file
	 * @throws IOException if any element of the chain throws an {@link IOException}
	 */
	public List readLines() throws IOException {
		try(BufferedReader in=this.asReader()) {
			ArrayList list=new ArrayList<>();
			String l;
			while((l=in.readLine())!=null)
				list.add(l);
			return list;
		}
	}
	
	/**
	 * Copies the content of this {@link InputStream} to the given {@link OutputStream}.
	 * This does not close the {@link OutputStream}.
	 * @param out the {@link OutputStream} to copy to
	 * @throws IOException if any element of the chain throws an {@link IOException}
	 */
	public void copyTo(OutputStream out) throws IOException {
		try(InputStream in=createInputStream()) {
			byte[] buffer = new byte[8192];
			int len = 0;
			while ((len=in.read(buffer)) != -1)
				out.write(buffer, 0, len);
		}
	}
	
	/**
	 * Copies the content of this {@link BufferedReader} to the given {@link Writer}.
	 * This does not close the {@link Writer}.
	 * @param out the {@link Writer} to copy to
	 * @throws IOException if any element of the chain throws an {@link IOException}
	 */
	public void copyTo(Writer out) throws IOException {
		try(BufferedReader in=this.asReader()) {
			char[] buffer = new char[4096];
			int len = 0;
			while ((len=in.read(buffer)) != -1)
				out.write(buffer, 0, len);
		}
	}
	
	/**
	 * This method reads the complete input in a {@link Stream} of Strings. Each element of the stream 
	 * represents one line of the source document. The stream is lazily populated. Be aware that the created
	 * reader is only closed if the created stream is closed.
	 * @return a {@link Stream} containing the lines of this input
	 * @throws IOException if any element of the chain throws an {@link IOException}
	 */
	@SuppressWarnings("resource")
	public Stream streamLines() throws IOException {
		BufferedReader in=this.asReader();
		return in.lines().onClose(() ->  {
			try {
				in.close();
			} catch (Exception e) {
				throw new RuntimeException(e);
			}	
		});
	}

	@SuppressWarnings("resource")
	private InputStream createInputStream() throws IOException {
		InputStream stream=source.openStream();
		if(decompress) {
			if(source.hasName() && CompressorRegistry.getInstance().canWrapInput(source.getName()))
				stream=CompressorRegistry.getInstance().wrap(source.getName(), stream);
			else
				stream=new DeflaterInputStream(stream);
		}
		if(base64Decoder!=null)
			stream=base64Decoder.wrap(stream);
		return stream;
	}
	
	/**
	 * @return the source used by this InBuilder
	 */
	public Source getSource() {
		return source;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy