/**
* Copyright (c) 2014 Takari, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package io.takari.maven.plugins.compile.javac;
/*******************************************************************************
* Copyright (c) 2011 Sonatype, Inc. All rights reserved. This program and the accompanying
* materials are made available under the terms of the Eclipse Public License v1.0 which accompanies
* this distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
// duplicates io.takari.incrementalbuild.spi.IncrementalFileOutputStream
// this is necessary to provide IncrementalFileOutputStream for forked compiler executions
// ideally, this class should be moved to a helper library also used by incrementalbuild,
// but is not currently supported by mojo unit test harness, and likely will never be
// (in order to add helper library to classpath of forked compiler execution during unit tests,
// the test harness will need to resolve the plugin dependency artifacts somehow, something that
// can only be done in the "outer" build and then passed into the test. doable, but a lot of work)
class IncrementalFileOutputStream extends OutputStream {
public static final int BUF_SIZE = 1024 * 16;
private final RandomAccessFile raf;
private final byte[] buffer;
private boolean modified;
public IncrementalFileOutputStream(File file) throws IOException {
if (file == null) {
throw new IllegalArgumentException("output file not specified");
}
File parent = file.getParentFile();
if (!parent.isDirectory() && !parent.mkdirs()) {
throw new IOException("Could not create directory " + parent);
}
modified = !file.exists();
if (file.exists() && !file.canWrite()) {
file.setWritable(true);
}
raf = new RandomAccessFile(file, "rw");
buffer = new byte[BUF_SIZE];
}
@Override
public void close() throws IOException {
long pos = raf.getFilePointer();
if (pos < raf.length()) {
modified = true;
raf.setLength(pos);
}
raf.close();
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
if (modified) {
raf.write(b, off, len);
} else {
for (int n = len; n > 0;) {
int read = raf.read(buffer, 0, Math.min(buffer.length, n));
if (read < 0 || !arrayEquals(b, off + len - n, buffer, 0, read)) {
modified = true;
if (read > 0) {
raf.seek(raf.getFilePointer() - read);
}
raf.write(b, off + len - n, n);
break;
} else {
n -= read;
}
}
}
}
private boolean arrayEquals(byte[] a1, int off1, byte[] a2, int off2, int len) {
for (int i = 0; i < len; i++) {
if (a1[off1 + i] != a2[off2 + i]) {
return false;
}
}
return true;
}
@Override
public void write(int b) throws IOException {
if (modified) {
raf.write(b);
} else {
int i = raf.read();
if (i < 0 || i != (b & 0xFF)) {
modified = true;
if (i >= 0) {
raf.seek(raf.getFilePointer() - 1);
}
raf.write(b);
}
}
}
}