/*******************************************************************************
* ALMA - Atacama Large Millimeter Array
* Copyright (c) COSYLAB - Control System Laboratory, 2011
* (in the framework of the ALMA collaboration).
* All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*******************************************************************************/
/*
* Created on Jan 14, 2005
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*/
package com.cosylab.logging.engine;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.DOMBuilder;
import org.jdom.adapters.JAXPDOMAdapter;
import com.cosylab.logging.engine.Filter.Constraint;
import com.cosylab.logging.engine.log.ILogEntry;
import com.cosylab.logging.engine.log.LogField;
/**
* @author acaproni
*
* This class stores all the filters defined by the user.
* It supports all the functionalities needed by the filters like
* load and save, the check if a log is compatible with the
* filters and so on
*
* FiltersList is a Vector. It uses another vector to
* store the indexes of the active filters
*/
public class FiltersVector extends Vector<Filter> {
// The vector of the active filters
// It contains the indexes (int) of the active filters
private Vector<Integer> activeFilters= new Vector<Integer>();
/**
* Constructor
*/
public FiltersVector() {
super();
}
/**
* Add a filter to the vector.
* If it is active, its index is added to the vector of the
* active filters
*
* @param f The filter to add
* @param active true if the filter is active
*/
public void addFilter(Filter f, boolean active) {
if (f==null) {
throw new IllegalArgumentException("Invalid null filter");
}
add(f);
if (active) activeFilters.add(new Integer(size()-1));
}
/**
* Set the filters in the vector deleting any other
* filter. The activeFilters vector is also updated
*
* @param filters The array of filters to add
* @param active The array of active filters
*/
public void setFilters(Filter[] f, boolean[] active) {
if (f.length != active.length) {
throw new IllegalArgumentException("The size of filters and active differ");
}
clear();
activeFilters.clear();
for (int i = 0; i < f.length; i++) {
add(f[i]);
if (active[i]) {
activeFilters.add(new Integer(i));
}
}
}
/**
* Set the filters in this vector to be the same of the
* passed vector
*
* @param flts The vector of filters
*/
public void setFilters(FiltersVector flts) {
if (flts==null) {
throw new IllegalArgumentException("Invalid null filters vector");
}
clear();
activeFilters.clear();
// Get the indexes of the active filters
for (Filter f: flts) {
if (f==null) {
throw new IllegalStateException("A filter in the vector is null");
}
add(f);
}
int[] activesIdx=flts.getAppliedFiltersIndexes();
if (activesIdx==null) {
return;
}
for (int t=0; t<activesIdx.length; t++) {
activeFilters.add(activesIdx[t]);
}
}
/**
* Return the indexes of the active filters
*
* @return The array of the indexes of the active filters
*/
public int[] getAppliedFiltersIndexes() {
if (activeFilters.size()==0) {
// No active filters
return null;
}
int[] temp = new int[activeFilters.size()];
for (int t=0; t<activeFilters.size(); t++) {
temp[t]=(activeFilters.get(t)).intValue();
}
return temp;
}
/**
* Check if there are active filters
*
* @return true if there are active filters
*/
public boolean hasActiveFilters() {
return activeFilters.size()>0;
}
/**
* Return true if the filter is active
*
* @param n The index of the filters
* @return true if the filter is active
*/
public boolean isActive(int n) {
return activeFilters.contains(new Integer(n));
}
/**
* Apply the (active) filters to a log
*
* @param log The log to check
* @return true if the log pass all the active filters check
*/
public boolean applyFilters(ILogEntry log) {
boolean testPassed=activeFilters.size()>=0;
// Check the log against all the active Filters (if any)
for (int t=0; t<activeFilters.size() && testPassed; t++) {
testPassed=(get(activeFilters.get(t).intValue())).applyTo(log, false);
}
return testPassed;
}
/**
* Returns the filter(s) applied as a string.
*
* @return The string with the applied filters
*/
public String getFilterString() {
if (activeFilters.size() == 0) {
return "Not filtered";
}
StringBuffer returnValue = new StringBuffer();
for (int t = 0; t<activeFilters.size(); t++) {
int pos = activeFilters.get(t).intValue();
if (t>0) returnValue.append(", ");
returnValue.append(((Filter)elementAt(pos)).field.getName());
}
return "Filtered by: "+returnValue.toString();
}
/**
* Delete all the filters (and the active vector)
*/
public void clear() {
super.clear();
activeFilters.clear();
}
/**
* Load filters
* In case of errors an exception is thrown
*
* @param f The xml file to parse (java.io.File)
* @param eraseFilters If true existing filters will be deleted before loading
* @param fileName Is the name of the file (it is usually null and the name is
* read from the parameter f. However when this method is called
* recursively we need to pass the name because the file we're reading
* is a temporary file (generated by converting the original file)
* @throws <code>Exception</code> In case of error loading the filters
*/
public void loadFilters(File f, boolean eraseFilters, String fileName) throws Exception {
Document doc;
FileInputStream fStream;
fStream = new FileInputStream(f);
JAXPDOMAdapter adapter = new JAXPDOMAdapter();
DOMBuilder builder = new DOMBuilder();
doc = builder.build(adapter.getDocument(fStream,false));
Element root = doc.getRootElement();
if (root.getName().compareToIgnoreCase("FILTER_LIST")!=0 &&
root.getName().compareToIgnoreCase("FILTERS")!=0) {
// The root is not FILTER_LIST neither FILTERS so the file we are
// parsing is not of the right type
// We show a message to the user and abort
throw new Exception("Wrong xml file: the root is "+root.getName()+" instead of FILTER_LIST");
} else if (root.getName().compareToIgnoreCase("FILTER_LIST")==0) {
// The file is an old version (show a message to the user to explain what's
// happening and how to avoid this message appears again in future
System.err.println("The format of this filter file is deprecated. \nSave the filters to avoid this message appears in future.");
// Convert the format of the file
File newFormatFile = convertOldFilterFile(f);
if (newFormatFile==null) {
// Something went wrong while converting: abort
throw new Exception("Error converting the file to the new format");
} else {
// Recursively call loadFilters but now the file has
// the new format
loadFilters(newFormatFile,eraseFilters,f.getAbsolutePath());
return;
}
}
Element filtersElement = root.getChild("FILTER_LIST");
if (filtersElement==null) return; // No filters defined
List children = filtersElement.getChildren("FILTER");
// Check if the vector has elements
if (children==null || children.size()==0) {
return;
}
if (eraseFilters) {
// The user whish to substitute the existing filters
// with those he's loading
clear();
}
// Read all the filters from the file
Iterator it = children.iterator();
while (it.hasNext()) {
// Temporary Strings to store values read from the file
String lethalStr = null;
String notStr = null;
String minStr = null;
String maxStr = null;
String exactStr = null;
String wcharStr = null;
String fieldStr=null;
Boolean enabled=null;
Element element = (Element) it.next();
String type = element.getAttributeValue("type");
Constraint constraint = Constraint.fromName(type);
Element lethalElement = element.getChild("LETHAL");
if (lethalElement!=null) {
lethalStr=lethalElement.getText();
}
Element notElement = element.getChild("APPLYNOT");
if (notElement!=null) {
notStr=notElement.getText();
}
Element minElement = element.getChild("MIN");
String minType=null;
if (minElement!=null) {
minStr=minElement.getText();
minType = minElement.getAttributeValue("class");
}
Element maxElement = element.getChild("MAX");
String maxType=null;
if (maxElement!=null) {
maxStr=maxElement.getText();
maxType = maxElement.getAttributeValue("class");
}
Element exactElement = element.getChild("EXACT");
String exactType=null;
if (exactElement!=null) {
exactStr=exactElement.getText();
exactType=exactElement.getAttributeValue("class");
}
Element wcharElement = element.getChild("WILDCHAR");
if (wcharElement!=null) {
wcharStr=wcharElement.getText();
}
Element fieldElement = element.getChild("FIELD");
if (fieldElement!=null) {
fieldStr=fieldElement.getText();
}
// Build the Field.
LogField field = LogField.fromName(fieldStr);
if (field==null) {
// Ooops the field has not been found
// Check if this String contains an Integer representing the
// position of this field in the enum LogField (it was in the old format)
Integer i = Integer.parseInt(fieldStr);
field = LogField.values()[i];
}
Element enabledElement = element.getChild("ENABLED");
if (enabledElement!=null) {
enabled= new Boolean(enabledElement.getText());
} else {
// Tag not found: enable the filter per default
enabled=Boolean.TRUE;
}
// Build the filter
Filter filter = Filter.buildFilter(
constraint,
field,
lethalStr,
notStr,
minStr,
minType,
maxStr,
maxType,
exactStr,
exactType,
wcharStr);
// bulidFilter throws an exception but ner return a null filter
if (filter==null) {
throw new IllegalStateException("The filter should not be null");
}
addFilter(filter,enabled);
}
}
/**
* Convert a filter file from the old to the new format
* The difference between the old stile and this new format is the presence
* of the history as well as the document type.
* At the present the history is not needed anymore but I keep this
* method for converting the xml type.
*
* NOTE: This method will be removed
*
* @return The file with new format
* @throws {@link IOException}
* throws {@link FileNotFoundException}
*
* @deprecated
*/
private File convertOldFilterFile(File oldFile) throws IOException, FileNotFoundException {
if (oldFile==null) {
throw new IllegalArgumentException("The file can't be null");
}
// Check if the file is readable and writable
if (!oldFile.canRead() || !oldFile.canWrite()) {
throw new IllegalArgumentException("The file must have read and write permissions");
}
//Create a new temporary file
File tempFile = File.createTempFile("jlog_",null);
FileOutputStream outStream = new FileOutputStream(tempFile);
DataOutputStream dataOutStream = new DataOutputStream(outStream);
// Write the header of the new file
dataOutStream.writeBytes("<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n");
dataOutStream.writeBytes("<FILTERS>\n");
// Write the list of filters as it is in the old file
FileInputStream inStream = null;
try {
inStream = new FileInputStream(oldFile);
} catch (FileNotFoundException fnfe) {
dataOutStream.close();
throw fnfe;
}
byte[] buffer = new byte[1024];
// strBuffer will contain the whole old file
StringBuffer strBuffer = new StringBuffer();
// The number of bytes read
try {
int nBytes;
do {
nBytes=inStream.read(buffer);
if (nBytes>0) {
strBuffer.append(new String(buffer,0,nBytes));
}
} while (nBytes!=-1); //EOF
String startTagStr = new String("<FILTER_LIST>");
String endTagStr = new String("</FILTER_LIST>");
int startTagPos = strBuffer.indexOf(startTagStr);
int endTagPos = strBuffer.indexOf(endTagStr);
if (startTagPos>0 && endTagPos>0) {
dataOutStream.writeBytes(strBuffer.substring(startTagPos,endTagPos+endTagStr.length()));
} else {
// Strange.. it seems I read no filters definition...
dataOutStream.writeBytes(startTagStr);
dataOutStream.writeBytes(endTagStr);
}
// Close the file with the termination tags
dataOutStream.writeBytes("</FILTERS>");
} finally {
dataOutStream.close();
inStream.close();
}
return tempFile;
}
/**
* Save the filters on a file
*
* @param f The xml file to store the filters in (java.io.File)
*/
public void saveFilters(File f) throws IOException {
if (f==null) {
throw new IllegalArgumentException("The file to save can't be null");
}
if (size()==0) return; // No filters to save
if (f.exists()) {
if (!f.delete()) {
throw new IOException("Error deleting "+f.getAbsolutePath());
}
}
if (!f.createNewFile()) {
throw new IOException("Error creating "+f.getAbsolutePath());
}
FileOutputStream outStream = new FileOutputStream(f);
DataOutputStream dataOutStream = new DataOutputStream(outStream);
try {
dataOutStream.writeBytes("<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n");
dataOutStream.writeBytes("<FILTERS>\n");
dataOutStream.writeBytes("<FILTER_LIST>\n");
for (int t=0; t<size(); t++) {
StringBuilder xmlString=new StringBuilder(get(t).toXMLString());
int pos=xmlString.indexOf("</FILTER>");
if (pos>=0) {
StringBuilder enabledStr = new StringBuilder("\t<ENABLED>");
enabledStr.append(isActive(t));
enabledStr.append("</ENABLED>\n\t");
xmlString.insert(pos, enabledStr);
}
dataOutStream.writeBytes(xmlString.toString());
}
dataOutStream.writeBytes("</FILTER_LIST>\n");
dataOutStream.writeBytes("</FILTERS>\n");
} finally {
dataOutStream.close();
}
}
/**
* Remove an element from the FiltersVector
* (We need to override this method because we need to keep the
* activeFilters aligned)
*/
public Filter remove(int index) {
if (index<0 || index>=this.size()) {
throw new IndexOutOfBoundsException("Invalid index");
}
// Get and remove the element from the Vector
Filter f = super.remove(index);
// The removed caused that all the filters following the removed one
// have been moved back by the remove
// @see java.util.Vector.remove for further details
// To keep up to date the activeFilters vector we have to:
// 1 remove index if present in the vector
// 2 decrease all the indexes in activeFilters, greater then index
int pos=-1; // pos of index in activeFilters
for (int t=0; t<activeFilters.size(); t++) {
int idx = activeFilters.get(t).intValue();
if (idx==index) {
pos=t;
} else if (idx>index) {
activeFilters.set(t,new Integer(idx-1));
}
}
if (pos!=-1) {
activeFilters.remove(pos);
}
return f;
}
/**
* Activate/deactivate a filter
*
* @param f The filter to activate/deactivate
* @param active If true, activate the filter
*/
public void activateFilter(Filter f, boolean active) {
if (f==null) {
throw new IllegalArgumentException("Invalid null filter");
}
Integer pos = indexOf(f);
if (pos==-1) {
throw new IllegalArgumentException("The filter is not in the vector");
}
if (active) {
if (!activeFilters.contains(pos)) {
activeFilters.add(pos);
}
} else {
activeFilters.remove(pos);
}
}
}