/*
* Copyright 2010 Bizosys Technologies Limited
*
* Licensed to the Bizosys Technologies Limited (Bizosys) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The Bizosys licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.bizosys.hsearch.filter;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.List;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.filter.Filter;
/**
* This is a document specific filter gets applied
* in matching terms. Meta section and Access Control are the
* two vital filtering steps after matching keywords.
* @author karan
*
*/
public class AMFilterMerged implements Filter {
public static final char META_HEADER_0 = 'm';
private static final byte[] META_HEADER_B = new byte[]{META_HEADER_0};
public static final char META_DETAIL_0 = 'n';
private static final byte[] META_DETAIL_B = new byte[]{META_DETAIL_0};
public static final char ACL_HEADER_0 = 'a';
public static final char ACL_DETAIL_0 = 'b';
private int[] docSerials = null;
/**
* The implementation class
*/
AMFilterCommon amfc = null;
/**
* Bytes
*/
byte[] bytes = null;
/**
* Default constructor
*
*/
public AMFilterMerged(){
}
/**
* Initialized
* @param amfc
*/
public AMFilterMerged(AMFilterCommon amfc){
this.amfc = amfc;
}
public void setDocSerials(int[] docSerials){
this.docSerials = docSerials;
}
/**
* Get filtering class
* @return The Filter logic object
*/
public AMFilterCommon getFma(){
return this.amfc;
}
/**
* Set filtering class
* @param amfc
*/
public void setFma(AMFilterCommon amfc) {
this.amfc = amfc;
}
/**
* Not necessary
*/
public boolean filterAllRemaining() {
return false;
}
/**
* True to drop this key/value
*/
public ReturnCode filterKeyValue(KeyValue kv) {
return ReturnCode.INCLUDE;
}
/**
* Not necessary
*/
public boolean filterRow() {
return false;
}
/**
* Last chance to drop entire row based on the sequence of filterValue()
* calls. Eg: filter a row if it doesn't contain a specified column
*/
public void filterRow(List<KeyValue> kvL) {
if ( null == docSerials ) return;
MergedBlocks.Block metaBlocks = new MergedBlocks.Block();
MergedBlocks.Block aclBlocks = new MergedBlocks.Block();
KeyValue prestine = getExistingBlocks(kvL, metaBlocks, aclBlocks);
if ( null == prestine) return;
int totalDocs = docSerials.length;
List<AMMarker> markings = FilterObjectFactory.getInstance().getAMMarkers();
mark(metaBlocks, aclBlocks, totalDocs, markings);
kvL.clear();
int dataLenMeta = 0;
for (AMMarker marker : markings) {
dataLenMeta = dataLenMeta + (marker.metaEnd - marker.metaStart);
}
byte[] metaHeader = new byte[markings.size() * 2];
byte[] metaData = new byte[dataLenMeta];
int metaDataPos = 0, len = 0, metaHeaderPos = 0;
FilterObjectFactory fof = FilterObjectFactory.getInstance();
for (AMMarker marker : markings) {
metaHeader[metaHeaderPos++] = (byte)(marker.serial >> 8 & 0xff);
metaHeader[metaHeaderPos++] = (byte)(marker.serial & 0xff);
len = (marker.metaEnd - marker.metaStart);
System.arraycopy(metaBlocks.data, marker.metaStart, metaData, metaDataPos, len);
metaDataPos = metaDataPos + len;
fof.putOneAMMarker(marker);
}
fof.putAMMarkers(markings);
byte[] r = prestine.getRow();
byte[] f = prestine.getFamily();
kvL.add(new KeyValue(r,f,META_HEADER_B,metaHeader));
kvL.add(new KeyValue(r,f,META_DETAIL_B,metaData));
}
private void mark(MergedBlocks.Block metaBlocks, MergedBlocks.Block aclBlocks,
int totalDocs, List<AMMarker> markings) {
int aclStartPos, aclEndPos, metaStartPos, metaEndPos, docSerial;
FilterObjectFactory fof = FilterObjectFactory.getInstance();
for ( int i=0; i< totalDocs; i++) {
docSerial = docSerials[i];
aclStartPos=0; aclEndPos=0 ; metaStartPos=0 ; metaEndPos=0;
if ( null != aclBlocks.header) {
aclStartPos = MergedBlocks.readHeader(aclBlocks.header, docSerial);
if ( -1 != aclStartPos) {
aclEndPos = this.amfc.allowAccess(aclBlocks.data, aclStartPos);
if ( -1 == aclEndPos ) continue;
}
}
if ( null != metaBlocks.header) {
metaStartPos = MergedBlocks.readHeader(metaBlocks.header, docSerial);
if ( -1 == metaStartPos) continue;
metaEndPos = this.amfc.allowMeta(metaBlocks.data, metaStartPos);
if ( -1 == metaEndPos) continue;
}
//ACL as well as META is OK
AMMarker aMarker = fof.getOneAMMarker();
aMarker.set(docSerial,aclStartPos,aclEndPos,metaStartPos,metaEndPos);
markings.add(aMarker);
}
}
private KeyValue getExistingBlocks(List<KeyValue> kvL, MergedBlocks.Block metaBlocks, MergedBlocks.Block aclBlocks) {
KeyValue prestine = null;
for (KeyValue kv : kvL) {
if ( null == prestine) {
prestine = new KeyValue(kv.getRow(),kv.getFamily(), kv.getQualifier());
}
switch ( kv.getQualifier()[0] ) {
case META_HEADER_0:
metaBlocks.header = kv.getValue();
break;
case META_DETAIL_0:
metaBlocks.data = kv.getValue();
break;
case ACL_HEADER_0:
aclBlocks.header = kv.getValue();
break;
case ACL_DETAIL_0:
aclBlocks.data = kv.getValue();
break;
default:
System.err.println("Unknown Column :" + new String(kv.getQualifier()));
break;
}
}
return prestine;
}
/**
* True to drop this row, if false, we will also call
*/
public boolean filterRowKey(byte[] rowKey, int offset, int length) {
return false;
}
public KeyValue getNextKeyHint(KeyValue arg0) {
return null;
}
public boolean hasFilterRow() {
return true;
}
public void reset() {
}
@Override
public void readFields(DataInput in) throws IOException {
int totalDocs = in.readInt();
docSerials = new int[totalDocs];
for ( int i=0; i<totalDocs; i++ ) {
docSerials[i] = in.readInt();
}
if ( null == this.amfc ) this.amfc = new AMFilterCommon();
this.amfc.readHeader(in);
}
@Override
public void write(DataOutput out) throws IOException {
int totalDocs = ( null == this.docSerials) ? 0 : docSerials.length;
out.writeInt(totalDocs);
if ( totalDocs > 0) {
for (int serial : docSerials) {
out.writeInt(serial);
}
}
if ( null == this.amfc ) this.amfc = new AMFilterCommon();
amfc.writeHeader(out);
}
}