/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* Array.java
* Copyright (C) 2009 University of Waikato, Hamilton, New Zealand
*
*/
package weka.core.pmml;
import java.io.Serializable;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import org.w3c.dom.Element;
/**
* Class for encapsulating a PMML Array element.
*
* @author Mark Hall (mhall{[at]}pentaho{[dot]}com)
* @version $Revision: 6467 $
*/
public class Array implements Serializable {
/**
* Utility method to check if an XML element is an array.
*
* @param arrayE the XML element to check
* @return returns true if the XML element is an array
*/
public static boolean isArray(Element arrayE) {
String name = arrayE.getTagName();
if (name.equals("Array") || name.equals("NUM-ARRAY") || name.equals("INT-ARRAY")
|| name.equals("REAL-ARRAY") || name.equals("STRING-ARRAY")
|| isSparseArray(arrayE)) {
return true;
}
return false;
}
/**
* Utility method to check if an XML element is a sparse array.
*
* @param arrayE the XML element to check.
* @return true if the XML element is a sparse array.
*/
private static boolean isSparseArray(Element arrayE) {
String name = arrayE.getTagName();
if (name.equals("INT-SparseArray") || name.equals("REAL-SparseArray")) {
return true;
}
return false;
}
public static Array create(List<Object> values,
List<Integer> indices) throws Exception {
ArrayType type = null;
Object first = values.get(0);
if ((first instanceof Double) || (first instanceof Float)) {
type = ArrayType.REAL;
} else if ((first instanceof Integer) || (first instanceof Long)) {
type = ArrayType.INT;
} else if ((first instanceof String)) {
type = ArrayType.STRING;
} else {
throw new Exception("[Array] unsupport type!");
}
if (indices != null) {
// array is sparse
if (indices.size() != values.size()) {
throw new Exception("[Array] num values is not equal to num indices!!");
}
if (type == ArrayType.REAL) {
type = ArrayType.REAL_SPARSE;
} else if (type == ArrayType.INT) {
type = ArrayType.INT_SPARSE;
} else {
throw new Exception("[Array] sparse arrays can only be integer, long, float or double!");
}
return new SparseArray(type, values, indices);
}
return new Array(type, values);
}
/**
* Static factory method for creating non-sparse or sparse
* array types as needed.
*
* @param arrayE the XML element encapsulating the array
* @return an appropriate Array type
* @throws Exception if there is a problem when constructing the array
*/
public static Array create(Element arrayE) throws Exception {
if (!isArray(arrayE)) {
throw new Exception("[Array] the supplied element does not contain an array!");
}
if (isSparseArray(arrayE)) {
return new SparseArray(arrayE);
}
return new Array(arrayE);
}
public static enum ArrayType {
NUM("NUM-ARRAY"),
INT("INT-ARRAY"),
REAL("REAL-ARRAY"),
STRING("STRING-ARRAY"),
REAL_SPARSE("REAL-SparseArray"),
INT_SPARSE("INT-SparseArray");
private final String m_stringVal;
ArrayType(String name) {
m_stringVal = name;
}
public String toString() {
return m_stringVal;
}
}
/** The values of the array */
protected ArrayList<String> m_values = new ArrayList<String>();
/** The type of the array */
protected ArrayType m_type = ArrayType.NUM;
protected void initialize(Element arrayE) throws Exception {
String arrayS = arrayE.getTagName();
// get the type of the array
if (arrayS.equals("Array")) {
String type = arrayE.getAttribute("type");
if (type.equals("int")) {
m_type = ArrayType.INT;
} else if (type.equals("real")) {
m_type = ArrayType.REAL;
} else if (type.equals("string")) {
m_type = ArrayType.STRING;
}
} else {
for (ArrayType a : ArrayType.values()) {
if (a.toString().equals(arrayS)) {
m_type = a;
break;
}
}
}
// now read the values
String contents = arrayE.getChildNodes().item(0).getNodeValue();
StringReader sr = new StringReader(contents);
StreamTokenizer st = new StreamTokenizer(sr);
st.resetSyntax();
st.whitespaceChars(0, ' ');
st.wordChars(' '+1,'\u00FF');
st.whitespaceChars(' ',' ');
st.quoteChar('"');
st.quoteChar('\'');
//m_Tokenizer.eolIsSignificant(true);
st.nextToken();
while (st.ttype != StreamTokenizer.TT_EOF &&
st.ttype != StreamTokenizer.TT_EOL) {
m_values.add(st.sval);
st.nextToken();
}
}
/**
* Construct an array from an XML node
*
* @param arrayE the Element containing the XML
* @throws Exception if something goes wrong
*/
protected Array(Element arrayE) throws Exception {
initialize(arrayE);
}
/**
* Construct an array from the given values.
*
* @param type the type of the elements.
* @param values the values of the array.
*/
protected Array(ArrayType type, List<Object> values) {
m_values = new ArrayList<String>();
m_type = type;
for (Object o : values) {
m_values.add(o.toString());
}
}
/**
* Get the type of this array.
*
* @return the type of the array.
*/
public ArrayType getType() {
return m_type;
}
/**
* Is this array a SparseArray?
*
* @return true if this array is sparse.
*/
public boolean isSparse() {
return false;
}
/**
* Get the number of values in this array.
*
* @return the number of values in this array.
*/
public int numValues() {
return m_values.size();
}
/**
* Returns true if the array contains this string value.
*
* @param value the value to check for.
* @return true if the array contains this string value
*/
public boolean contains(String value) {
return m_values.contains(value);
}
/**
* Returns true if the array contains this integer value.
*
* @param value the value to check for
* @return true if the array contains this integer value
*/
public boolean contains(int value) {
return contains(new Integer(value).toString());
}
/**
* Returns true if the array contains this real value.
*
* @param value the value to check for
* @return true if the array contains this real value
*/
public boolean contains(double value) {
return contains(new Double(value).toString());
}
/**
* Returns true if the array contains this real value.
*
* @param value the value to check for
* @return true if the array contains this real value
*/
public boolean contains(float value) {
return contains(new Float(value).toString());
}
private void checkInRange(int index) throws Exception {
if (index >= m_values.size() || index < 0) {
throw new IllegalArgumentException("[Array] index out of range " + index);
}
}
/**
* Returns the index of the value stored at the given position
*
* @param position the position
* @return the index of the value stored at the given position
*/
public int index(int position) {
return position; // position is the index for dense arrays
}
/**
* Gets the value at index from the array.
*
* @param index the index of the value to get.
* @return the value at index in the array as as String.
* @throws Exception if index is out of bounds.
*/
public String value(int index) throws Exception {
return actualValue(index);
}
/**
* Gets the value at index from the array
*
* @param index the index of the value to get.
* @return the value at index in the array as as String.
* @throws Exception if index is out of bounds.
*/
protected String actualValue(int index) throws Exception {
checkInRange(index);
return m_values.get(index);
}
/**
* Gets the value at index from the array as a String. Calls
* value().
*
* @param index the index of the value to get.
* @return the value at index in the array as a String.
* @throws Exception if index is out of bounds.
*/
public String valueString(int index) throws Exception {
return value(index);
}
/**
* Gets the value at index from the array as a double.
*
* @param index the index of the value to get.
* @return the value at index in the array as a double.
* @throws Exception if index is out of bounds.
*/
public double valueDouble(int index) throws Exception {
if (m_type == ArrayType.STRING) {
throw new Exception("[Array] Array does not contain numbers!");
}
return Double.parseDouble(value(index));
}
/**
* Gets the value at index from the array as a float.
*
* @param index the index of the value to get.
* @return the value at index in the array as a float.
* @throws Exception if index is out of bounds.
*/
public float valueFloat(int index) throws Exception {
if (m_type == ArrayType.STRING) {
throw new Exception("[Array] Array does not contain numbers!");
}
return Float.parseFloat(value(index));
}
/**
* Gets the value at index from the array as an int.
*
* @param index the index of the value to get.
* @return the value at index in the array as an int.
* @throws Exception if index is out of bounds.
*/
public int valueInt(int index) throws Exception {
if (m_type != ArrayType.INT && m_type != ArrayType.INT_SPARSE) {
throw new Exception("[Array] Array does not contain integers!");
}
return Integer.parseInt(value(index));
}
/**
* Gets the value at indexOfIndex from the array. Does the
* same as value() if this array is not sparse.
*
* @param indexOfIndex the index of the index of the value to get.
* @return a value from the array as a String.
* @throws Exception if indexOfIndex is out of bounds.
*/
public String valueSparse(int indexOfIndex) throws Exception {
return actualValue(indexOfIndex);
}
/**
* Gets the value at indexOfIndex from the array. Does the
* same as value() if this array is not sparse.
*
* @param indexOfIndex the index of the index of the value to get.
* @return a value from the array as a String.
* @throws Exception if indexOfIndex is out of bounds.
*/
public String valueSparseString(int indexOfIndex) throws Exception {
return valueSparse(indexOfIndex);
}
/**
* Gets the value at indexOfIndex from the array. Does the
* same as value() if this array is not sparse.
*
* @param indexOfIndex the index of the index of the value to get.
* @return a value from the array as a double.
* @throws Exception if indexOfIndex is out of bounds.
*/
public double valueSparseDouble(int indexOfIndex) throws Exception {
return Double.parseDouble(actualValue(indexOfIndex));
}
/**
* Gets the value at indexOfIndex from the array. Does the
* same as value() if this array is not sparse.
*
* @param indexOfIndex the index of the index of the value to get.
* @return a value from the array as a float.
* @throws Exception if indexOfIndex is out of bounds.
*/
public float valueSparseFloat(int indexOfIndex) throws Exception {
return Float.parseFloat(actualValue(indexOfIndex));
}
/**
* Gets the value at indexOfIndex from the array. Does the
* same as value() if this array is not sparse.
*
* @param indexOfIndex the index of the index of the value to get.
* @return a value from the array as an int.
* @throws Exception if indexOfIndex is out of bounds.
*/
public int valueSparseInt(int indexOfIndex) throws Exception {
return Integer.parseInt(actualValue(indexOfIndex));
}
public String toString() {
StringBuffer text = new StringBuffer();
text.append("<");
for (int i = 0; i < m_values.size(); i++) {
text.append(m_values.get(i));
if (i < m_values.size() - 1) {
text.append(",");
}
}
text.append(">");
return text.toString();
}
}