/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * See LICENSE.txt included in this distribution for the specific * language governing permissions and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at LICENSE.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. * Portions Copyright 2012 Jens Elkner. */ package org.opensolaris.opengrok.analysis; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Writer; import java.util.logging.Level; import java.util.logging.Logger; import java.util.zip.GZIPInputStream; import org.opensolaris.opengrok.analysis.FileAnalyzer.Genre; import org.opensolaris.opengrok.util.IOUtils; /** * A {@link FilterInputStream} for Opengrok crossfiles. * It uses a {@link BufferedInputStream} or {@link GZIPInputStream} as * underlying stream to provide the required functionality. So there is usually * no need to wrap it into another BufferedInputStream etc. . * * @see BufferedInputStream * @see GZIPInputStream * * @author Jens Elkner * @version $Revision$ */ public class XrefInputStream extends FilterInputStream { private static final Logger logger = Logger.getLogger(XrefInputStream.class.getName()); private XrefHeader header; private File file; private boolean uncompress; private boolean canClose; /** * Create a new InputStream from the given crossfile. The header gets read * automatically and thus internal cursor points to the start of the data * section of the crossfile, when this method returns. * * @param file file to read. * @param uncompress if {@code true} crossfile data gets uncompressed on * the fly if they are compressed. * @throws IOException on read error or if the given file is not a crossfile */ public XrefInputStream(File file, boolean uncompress) throws IOException { super(null); FileInputStream fis = new FileInputStream(file); this.file = file; try { header = new XrefHeader(fis); this.uncompress = uncompress && header.isCompressed(); in = this.uncompress ? new GZIPInputStream(fis, 4096) : new BufferedInputStream(fis, header.isCompressed() ? 4096 : 16384); } catch (IOException e) { if (in != null) { IOUtils.close(in); } else { IOUtils.close(fis); } throw new IOException(e.getMessage() + "(" + file + ")"); } canClose = true; } /** * Get the header of the crossfile beeing read. * @return the crossfile header */ public XrefHeader getHeader() { return header; } /** * Convinience method to check, whether the wrapped crossfile contains * compressed data. * @return {@code true} if data section is compressed. * @see XrefHeader#isCompressed() * @see #getHeader() */ public boolean isCompressed() { return header.isCompressed(); } /** * Convinience method to get the Genre of data provided by this stream. * @return the data's genre. * @see XrefHeader#getGenre() * @see #getHeader() */ public Genre getGenre() { return header.getGenre(); } /** * Get the file from which this stream has been created. * @return the origin of this stream */ public File getFile() { return file; } /** * Dump all data not yet read to the given output stream. * @param out where to dump remaining data. * @throws IOException */ public void dump(OutputStream out) throws IOException { if (out == null) { throw new IllegalArgumentException("null output stream not allowed"); } byte[] buf = new byte[!uncompress && (in instanceof GZIPInputStream) ? 4096 : 16384]; int len = 0; while ((len = in.read(buf)) >= 0) { out.write(buf, 0, len); } } /** * Dump all data not yet read to the given writer. The constructor for this * instance should have been called with on-the-fly uncompression enabled. * If not and the data of the stream are compressed, it is tried to switch * over to on-the-fly uncompression, but obviously this will fail with an * IOException, if any data have been read from this stream already. * * @param out where to write data. * @throws IOException on read error * @see XrefInputStream#XrefInputStream(File, boolean) */ public void dump(Writer out) throws IOException { if (out == null) { throw new IllegalArgumentException("null output stream not allowed"); } if (header.isCompressed() && !uncompress) { logger.warning("switching over to on-the-fly uncompression for " + file + " (wasn't specified in constructor)"); GZIPInputStream gin = new GZIPInputStream(in, 4096); in = gin; } InputStreamReader isr = new InputStreamReader(in, "UTF-8"); char[] buf = new char[4096]; // underlying buffers are suffient int len = 0; canClose = false; // avoid that isr.close() closes in as well try { while ((len = isr.read(buf)) >= 0) { out.write(buf, 0, len); } } finally { IOUtils.close(isr); canClose = true; } } /** * Convinience method to dump a crossfile to the given writer.. * * @param file crossfile to dump. * @param out dump destination. * @param script if {@code true} the javascript snippet * {@code O.lines=$num; O.createLinenums();} gets emitted to the writer * first. * @return {@code true} on success. */ public static boolean dump(File file, Writer out, boolean script) { if (out == null) { return false; } XrefInputStream in = null; try { in = new XrefInputStream(file, true); if (script) { out.write(""); } in.dump(out); return true; } catch (Exception e) { logger.warning(e.getLocalizedMessage()); logger.log(Level.FINE, "dump " + file, e); } finally { IOUtils.close(in); } return false; } /** * {@inheritDoc} */ @Override public void close() throws IOException { if (!canClose) { return; } in.close(); } /** * Dump an opengrok cross file. * @param args opengrok crossfile name */ public static void main(String[] args) { if (args.length == 0) { System.out.println("Usage: java ... " + logger.getName() + " xrefFile"); return; } XrefInputStream in = null; try { in = new XrefInputStream(new File(args[0]), true); System.out.println(in.getHeader().toString()); System.out.println(); in.dump(System.out); } catch (IOException e) { System.out.println(e.getLocalizedMessage()); } finally { IOUtils.close(in); } } }