package org.xcmis.restatom.abdera; import org.apache.commons.codec.binary.Base64InputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; class ContentOutputStream extends FilterOutputStream { private static final int MAX_BUFFER_SIZE = 204800; private int outBufLength = 0; private boolean isFoundStart; private boolean isXmlWrapped; private boolean isFoundEnd; private boolean isFirstWrite = true; private boolean overflow; private boolean isPrevCycleFull; private ByteArrayOutputStream byteTempBufStream; private File file; private OutputStream outFile; private boolean isClosed; /** * * Uses for Abdera. Will be written base64 encoded data. * * To write data like: * <cmisra:base64 xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/">aGVsbG8=</cmisra:base64> * In this example the 'aGVsbG8=' is a 'hello' word. * * @throws FileNotFoundException */ public ContentOutputStream() { super(null); out = new ByteArrayOutputStream(MAX_BUFFER_SIZE); byteTempBufStream = new ByteArrayOutputStream(); } @Override public void write(byte[] b, int off, int len) throws IOException { // if finished skip the next blocks if (isFoundEnd) return; // check whether xml wrapped on the first write if (isFirstWrite) { // run once isFirstWrite = false; // will be checked on the first iteration only isXmlWrapped = b[0] == 60; // is the first == '<' } if (!isXmlWrapped) { writeInternal(b, off, len); } else { // SKIP THE XML // finding START element if (!isFoundStart) { // look for the first '>' int indexOfStart = -1; for (int i = 0; i < len; i++) { if (b[i] == 62) { indexOfStart = i; break; } } if (indexOfStart != -1) { isFoundStart = true; off = indexOfStart + 1; len = len - off; } } // finding END element if (isFoundStart) { boolean isNotFullBlock = len < b.length; if (isNotFullBlock) { if (isPrevCycleFull) { // if previous block was full writeInternal(byteTempBufStream.toByteArray(), 0, -1); byteTempBufStream.reset(); } // set to false for skip byteTempBufStream in close() isPrevCycleFull = false; writeEnd(b, off, len); } else { // it is full block // middle or full last // maybe will be end if (isPrevCycleFull) { writeInternal(byteTempBufStream.toByteArray(), 0, -1); } isPrevCycleFull = true; byteTempBufStream.reset(); byteTempBufStream.write(b, off, len); } } } } private void writeEnd(byte[] b, int off, int len) throws IOException { // the first or the last or the one // let's check the end // get the end string no more 30 length int offForEnd = len < 30 ? off : off + len - 30; int lenForEnd = len < 30 ? len : 30; // look for the last '<' for (int indexOfEnd = offForEnd; indexOfEnd < offForEnd + lenForEnd; indexOfEnd++) { if (b[indexOfEnd] == 60) { isFoundEnd = true; len = indexOfEnd - off; } } writeInternal(b, off, len); } private void writeInternal(byte[] b, int off, int len) throws IOException { // if write previous full block, then count the 'len' from array if (len == -1) len = b.length; if (!overflow) { // count the bytes length, whether overflow outBufLength += len; overflow = outBufLength > MAX_BUFFER_SIZE; } if (!overflow) { // small data, use bytes out.write(b, off, len); } else { // large data, use file if (outFile == null) { // create a file file = File.createTempFile("cmisatom-base64-", null); outFile = new FileOutputStream(file); // write from BUF if (outBufLength != len) { // 'out' has bytes // overflow doesn't happen in the FIRST iteration outFile.write(((ByteArrayOutputStream)out).toByteArray()); } } // write data in file outFile.write(b, off, len); } } @Override public void close() throws IOException { if (!isClosed) { isClosed = true; // to check BUF and write if is if (isPrevCycleFull) { // last block was full writeEnd(byteTempBufStream.toByteArray(), 0, byteTempBufStream.toByteArray().length); } if (overflow) { // was written to file, let's close it outFile.close(); } // 'out' in the super will be closed super.close(); } } public InputStream getInputStream() throws IOException { InputStream input = null; if (overflow) { // return file try { input = new ContentFileInputStream(file); } catch (FileNotFoundException e) { return null; } } else { // return byte array ByteArrayInputStream bytesInput = new ByteArrayInputStream(((ByteArrayOutputStream)out).toByteArray()); input = new Base64InputStream(bytesInput); } return input; } }