/*
* 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) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
*/
package org.opensolaris.opengrok.web;
import java.io.EOFException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.opensolaris.opengrok.util.IOUtils;
/**
* An Extremely Fast Tagged Attribute Read-only File Reader
* Created on October 12, 2005
*
* @author Chandan
*/
public class EftarFileReader {
private final RandomAccessFile f;
private boolean isOpen;
private static final Logger log = Logger.getLogger(EftarFileReader.class.getName());
class FNode {
public long offset;
public long hash;
public int childOffset;
public int numChildren;
public int tagOffset;
public FNode() throws IOException {
offset = f.getFilePointer();
//System.err.println("offset " + offset);
try {
hash = f.readLong();
childOffset = f.readUnsignedShort();
numChildren = f.readUnsignedShort();
tagOffset = f.readUnsignedShort();
} catch (EOFException e) {
numChildren = 0;
tagOffset = 0;
}
//System.err.println("offset " + offset + " num children " + numChildren + " tagOff " + tagOffset);
}
public FNode(long hash, long offset, int childOffset, int num, int tagOffset) {
this.hash = hash;
this.offset = offset;
this.childOffset = childOffset;
this.numChildren = num;
this.tagOffset = tagOffset;
}
public FNode get(long hash) throws IOException {
if (childOffset == 0 || numChildren == 0) {
return null;
}
return binarySearch(offset + childOffset, numChildren, hash);
}
private FNode binarySearch(long start, int len, long hash) throws IOException {
//System.err.printf(" Searching [%x] in %5d elem from %x\n", hash, len, start);
int b = 0;
int e = len;
while (b <= e) {
int m = (b + e) / 2;
f.seek(start + m * EftarFile.RECORD_LENGTH);
long mhash = f.readLong();
if (hash > mhash) {
b = m + 1;
} else if (hash < mhash) {
e = m - 1;
} else {
return new FNode(mhash, f.getFilePointer() - 8l, f.readUnsignedShort(), f.readUnsignedShort(), f.readUnsignedShort());
}
}
return null;
}
public String getTag() throws IOException {
if (tagOffset == 0) {
return null;
}
f.seek(offset + tagOffset);
byte[] tagString;
if (childOffset == 0) {
tagString = new byte[numChildren];
} else {
tagString = new byte[childOffset - tagOffset];
}
int len = f.read(tagString);
if (len == -1) {
throw new EOFException();
}
return new String(tagString, 0, len);
}
@Override
public String toString() {
String tagString = null;
try {
tagString = getTag();
} catch (EOFException e) { // NOPMD
// ignore
} catch (IOException e) {
log.log(Level.WARNING, "Got excption while getting the tag: ", e);
}
return "H[" + hash + "] num = " + numChildren + " tag = " + tagString;
}
}
public EftarFileReader(String file) throws FileNotFoundException {
this(new File(file));
}
public EftarFileReader(File file) throws FileNotFoundException {
f = new RandomAccessFile(file, "r");
isOpen = true;
}
public FNode getNode(String path) throws IOException {
StringTokenizer toks = new StringTokenizer(path, "/");
f.seek(0);
FNode n = new FNode();
if (File.separator.equals(path) || path.length() == 0) {
return n;
}
FNode next = null;
while (toks.hasMoreTokens() && ((next = n.get(EftarFile.myHash(toks.nextToken()))) != null)) {
n = next;
}
if (!toks.hasMoreElements()) {
return next;
}
return null;
}
public String getChildTag(FNode fn, String name) throws IOException {
if (fn != null && fn.childOffset != 0 && fn.numChildren != 0) {
FNode ch = fn.binarySearch(fn.offset + fn.childOffset, fn.numChildren, EftarFile.myHash(name));
if (ch != null) {
return ch.getTag();
}
}
return null;
}
public String get(String path) throws IOException {
StringTokenizer toks = new StringTokenizer(path, "/");
f.seek(0);
FNode n = new FNode();
FNode next;
long tagOffset = 0;
int tagLength = 0;
while (toks.hasMoreTokens()) {
String tok = toks.nextToken();
if (tok == null || tok.length() == 0) {
continue;
}
next = n.get(EftarFile.myHash(tok));
if (next == null) {
break;
}
if (next.tagOffset != 0) {
tagOffset = next.offset + next.tagOffset;
if (next.childOffset == 0) {
tagLength = next.numChildren;
} else {
tagLength = next.childOffset - next.tagOffset;
}
}
n = next;
}
if (tagOffset != 0) {
f.seek(tagOffset);
byte[] desc = new byte[tagLength];
int len = f.read(desc);
if (len == -1) {
throw new EOFException();
}
return new String(desc, 0, len);
}
return "";
}
/**
* Check, whether this instance has been already closed.
* @return {@code true} if closed.
*/
public boolean isClosed() {
return !isOpen;
}
public void close() {
if (isOpen) {
IOUtils.close(f);
isOpen = false;
}
}
}