/* * 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 3 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, see <http://www.gnu.org/licenses/>. */ /* * Array.java * Copyright (C) 2009-2012 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: 8034 $ */ 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(); } }