1384N/A/*
1384N/A * CDDL HEADER START
1384N/A *
1384N/A * The contents of this file are subject to the terms of the
1384N/A * Common Development and Distribution License (the "License").
1384N/A * You may not use this file except in compliance with the License.
1384N/A *
1384N/A * See LICENSE.txt included in this distribution for the specific
1384N/A * language governing permissions and limitations under the License.
1384N/A *
1384N/A * When distributing Covered Code, include this CDDL HEADER in each
1384N/A * file and include the License file at LICENSE.txt.
1384N/A * If applicable, add the following below this CDDL HEADER, with the
1384N/A * fields enclosed by brackets "[]" replaced with your own identifying
1384N/A * information: Portions Copyright [yyyy] [name of copyright owner]
1384N/A *
1384N/A * CDDL HEADER END
1384N/A */
1384N/A
1384N/A/*
1384N/A * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
1384N/A * Portions Copyright 2012 Jens Elkner.
1384N/A */
1384N/Apackage org.opensolaris.opengrok.analysis;
1384N/A
1384N/Aimport java.io.BufferedOutputStream;
1384N/Aimport java.io.File;
1384N/Aimport java.io.FileNotFoundException;
1384N/Aimport java.io.FileOutputStream;
1384N/Aimport java.io.IOException;
1384N/Aimport java.io.OutputStream;
1384N/Aimport java.io.OutputStreamWriter;
1384N/Aimport java.io.RandomAccessFile;
1384N/Aimport java.io.Writer;
1384N/Aimport java.nio.channels.FileChannel;
1384N/Aimport java.util.zip.GZIPOutputStream;
1384N/A
1384N/Aimport org.opensolaris.opengrok.analysis.FileAnalyzer.Genre;
1384N/Aimport org.opensolaris.opengrok.util.IOUtils;
1384N/A
1384N/A/**
1384N/A * An UTF-8 OutputStreamWriter used to write Xref files.
1384N/A *
1384N/A * @author Jens Elkner
1384N/A * @version $Revision$
1384N/A */
1384N/Apublic class XrefWriter extends Writer {
1384N/A private long count;
1384N/A private RandomFileOutputStream fos;
1384N/A private Writer out;
1384N/A private long mark = -1;
1384N/A private XrefHeader header;
1384N/A
1384N/A /**
1384N/A * Creates an uncompressed xref output stream by creating/overwriting the
1384N/A * file with the given name.
1384N/A * @param name name of the file to create/overwrite.
1384N/A * @param genre the genre of the data to be written.
1384N/A * @throws IOException
1384N/A */
1384N/A public XrefWriter(String name, Genre genre) throws IOException {
1384N/A this(name, genre, true);
1384N/A }
1384N/A
1384N/A /**
1384N/A * Creates an [un]compressed xref output stream by creating/overwriting the
1384N/A * file with the given name.
1384N/A * @param name name of the file to create/overwrite
1384N/A * @param genre the genre of the data to be written.
1384N/A * @param compress if {@code true} written data gets compressed.
1384N/A * @throws IOException
1384N/A */
1384N/A public XrefWriter(String name, Genre genre, boolean compress) throws IOException {
1384N/A this(new File(name), genre, compress);
1384N/A }
1384N/A
1384N/A /**
1384N/A * Creates an uncompressed xref output stream by creating/overwriting the
1384N/A * given file.
1384N/A *
1384N/A * @param file file to write
1384N/A * @param genre the genre of the data to be written.
1384N/A * @throws IOException
1384N/A */
1384N/A public XrefWriter(File file, Genre genre) throws IOException {
1384N/A this(file, genre, false);
1384N/A }
1384N/A
1384N/A private class RandomFileOutputStream extends OutputStream {
1384N/A RandomAccessFile raf;
1384N/A
1384N/A public RandomFileOutputStream(File file) throws FileNotFoundException {
1384N/A if (file == null) {
1384N/A throw new IllegalArgumentException("null is not allowed");
1384N/A }
1384N/A raf = new RandomAccessFile(file, "rw");
1384N/A }
1384N/A
1384N/A @Override
1384N/A public void write(int b) throws IOException {
1384N/A raf.write(b);
1384N/A }
1384N/A
1384N/A @Override
1384N/A public void write(byte[] b, int off, int len) throws IOException {
1384N/A raf.write(b, off, len);
1384N/A }
1384N/A
1384N/A @Override
1384N/A public void close() {
1384N/A // prevent wrapper streams from closing it
1384N/A }
1384N/A }
1384N/A
1384N/A /**
1384N/A * Creates an [un]compressed xref output stream by creating/overwriting the
1384N/A * given file.
1384N/A *
1384N/A * @param file file to write
1384N/A * @param genre the genre of the data to be written.
1384N/A * @param compress if {@code true} written data gets compressed.
1384N/A * @throws IOException
1384N/A * @throws FileNotFoundException
1384N/A * @see FileOutputStream
1384N/A */
1384N/A public XrefWriter(File file, Genre genre, boolean compress) throws IOException {
1384N/A super(file);
1384N/A fos = new RandomFileOutputStream(file);
1384N/A header = new XrefHeader(genre, compress);
1384N/A header.write(fos);
1384N/A out = new OutputStreamWriter(compress
1384N/A ? new GZIPOutputStream(fos, 4096)
1384N/A : new BufferedOutputStream(fos, 16384), "UTF-8");
1384N/A }
1384N/A
1384N/A /**
1384N/A * Just wraps the given writer. {@link #close()} on this instance gets
1384N/A * ignored and thus no header will be written or updated.
1384N/A * @param out writer to wrap.
1384N/A */
1384N/A public XrefWriter(Writer out) {
1384N/A super(new Object());
1384N/A this.out = out;
1384N/A }
1384N/A
1384N/A /**
1384N/A * Get the number of bytes written to the output stream.
1384N/A * @return a number >= 0
1384N/A * @see FileChannel#position()
1384N/A */
1384N/A public long getCount() {
1384N/A return count;
1384N/A }
1384N/A
1384N/A /**
1384N/A * Get the file this instance is writing to.
1384N/A * @return {@code null} if this instance wraps another writer, the file
1384N/A * used to create this instance otherwise.
1384N/A */
1384N/A public File getFile() {
1384N/A return fos != null ? (File) this.lock : null;
1384N/A }
1384N/A
1384N/A /**
1384N/A * {@inheritDoc}
1384N/A */
1384N/A @Override
1384N/A public void write(char[] cbuf, int off, int len) throws IOException {
1384N/A out.write(cbuf, off, len);
1384N/A count += len;
1384N/A }
1384N/A
1384N/A /**
1384N/A * {@inheritDoc}
1384N/A */
1384N/A @Override
1384N/A public void flush() throws IOException {
1384N/A out.flush();
1384N/A }
1384N/A
1384N/A /**
1384N/A * {@inheritDoc}
1384N/A */
1384N/A @Override
1384N/A public void close() throws IOException {
1384N/A if (fos != null) {
1384N/A IOUtils.close(out); //flush and release wrapper stream resources
1384N/A fos.raf.seek(0);
1384N/A header.setSize(count);
1384N/A fos.raf.write(header.getBytes().array());
1384N/A IOUtils.close(fos.raf);
1384N/A }
1384N/A out = null;
1384N/A }
1384N/A
1384N/A /**
1384N/A * Remember the number of characters currently written to the underlying
1384N/A * output stream. NOTE: This method is unsynchronized and thus may return
1384N/A * an unexpected value if characters are written concurrently!
1384N/A * @return the value of {@link #getCount()}
1384N/A */
1384N/A public long mark() {
1384N/A mark = getCount();
1384N/A return mark;
1384N/A }
1384N/A
1384N/A /**
1384N/A * Get the number of characters written to the underlying
1384N/A * output stream at the last time when {@link #mark()} was called.
1384N/A * @return {@code -1} if {@code mark()} has never been called, otherwise
1384N/A * the value the last {@code mark()} returned.
1384N/A */
1384N/A public long getMark() {
1384N/A return mark;
1384N/A }
1384N/A
1384N/A /**
1384N/A * Set the number of lines written to this stream.
1384N/A * @param lines number of lines writen.
1384N/A */
1384N/A public void setLines(int lines) {
1384N/A if (header != null) {
1384N/A header.setLines(lines);
1384N/A }
1384N/A }
1384N/A}