/*********************************************************************** This file is part of KEEL-software, the Data Mining tool for regression, classification, clustering, pattern mining and so on. Copyright (C) 2004-2010 F. Herrera (herrera@decsai.ugr.es) L. S�nchez (luciano@uniovi.es) J. Alcal�-Fdez (jalcala@decsai.ugr.es) S. Garc�a (sglopez@ujaen.es) A. Fern�ndez (alberto.fernandez@ujaen.es) J. Luengo (julianlm@decsai.ugr.es) 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/ **********************************************************************/ /* * HtmlToKeel.java */ package keel.Algorithms.Preprocess.Converter; import java.io.*; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jdom.*; import org.jdom.input.*; import org.w3c.tidy.Tidy; /** * <p> * <b> HtmlToKeel </b> * </p> * * Clase extendida de la clase Importer. * Esta clase es utilizada para leer datos localizados en la tabla del fichero Html * y convertirlos a formato keel. * * @author Teresa Prieto López (UCO) * @version 1.0 */ public class HtmlToKeel extends Importer { //Variable auxiliar para almacenar el texto de todos los descendientes de un nodo. private String lineAux = new String();//Variable almacena el elemento o etiqueta principal que forma el documento xml. private Element root; /* * Constructor de la Clase HtmlToKeel. Inicializa el valor * de la variable miembro nullValue (valor nulo para un dato dentro de * la tabla Html) con el valor del parámetro nullValueUser. * * @param nullValueUser. Variable de tipo String con el valor nulo para un dato dentro de * la tabla Html. */ public HtmlToKeel(String nullValueUser) { nullValue = nullValueUser; } /* * Metodo utilizado para convertir los datos de la tabla dentro del fichero * html indicado mediante la variable pathnameInput a formato keel en el fichero * indicado por la ruta pathnameOutput. * * @param pathnameInput ruta del fichero con formato html donde se almacena la tabla * @param pathnameOutput ruta con los datos en formato keel * * @throws Exception */ public void Start(String pathnameInput, String pathnameOutput) throws Exception { Pattern p; Matcher m; File f; Element children; String nameChildren = new String(); String value = new String(); String fileXhtml = new String(); int type = -1; int numElements = 0; int j = 0; int i = 0; int k = 0; int actualValueInt; double actualValue; double min; double max; List<Element> firstInstance; String vowel[] = {"a", "e", "i", "o", "u", "A", "E", "I", "O", "U"}; String vowel_accent[] = {"�", "�", "�", "�", "�", "�", "�", "�", "�", "�"}; File fileInput = new File(pathnameInput); // Transformamos el fichero html a formato xhtml p = Pattern.compile("\\.[A-Za-z]+"); m = p.matcher(fileInput.getName()); fileXhtml = m.replaceAll(""); fileXhtml = "tempOf" + fileXhtml + ".xthml"; ConverterXhtml(pathnameInput, fileXhtml); try { SAXBuilder builder = new SAXBuilder(false); Document doc = builder.build(new File(fileXhtml)); root = doc.getRootElement(); FindParent(root, "tr"); //todos los hijos que tengan List elements = root.getChildren(); numElements = elements.size(); //Calculamos el número de hijos que tiene cada elemento if (numElements > 0) { i = 0; children = (Element) elements.get(i); nameChildren = children.getName(); while (!nameChildren.equalsIgnoreCase("tr")) { i++; children = (Element) elements.get(i); nameChildren = children.getName(); } firstInstance = ((Element) elements.get(i)).getChildren(); } else { System.out.println("No hay elementos"); return; } //Reservamos memoria para almacenar la definición de los atributos y de los datos numAttributes = firstInstance.size(); attribute = new keel.Dataset.Attribute[numAttributes]; data = new Vector[numAttributes]; types = new Vector[numAttributes]; for (i = 0; i < numAttributes; i++) { attribute[i] = new keel.Dataset.Attribute(); data[i] = new Vector(); types[i] = new Vector(); } Iterator it = elements.iterator(); if(processHeader){ // El primer hijo contiene el nombre de los atributos while (it.hasNext()) { children = (Element) it.next(); nameChildren = (String) children.getName(); if (nameChildren.equalsIgnoreCase("tr")) { List instances = children.getChildren(); Iterator it_instance = instances.iterator(); j = 0; while (it_instance.hasNext()) { Element element_children = (Element) it_instance.next(); String element_name = (String) element_children.getName(); if (element_name.equalsIgnoreCase("td") || element_name.equalsIgnoreCase("th")) { String nameAttribute = ""; nameAttribute = element_children.getText(); p = Pattern.compile("^\\s+"); m = p.matcher(nameAttribute); nameAttribute = m.replaceAll(""); p = Pattern.compile("\\s+$"); m = p.matcher(nameAttribute); nameAttribute = m.replaceAll(""); nameAttribute = nameAttribute.replace("'", ""); nameAttribute = nameAttribute.replace("\"", ""); nameAttribute = nameAttribute.replace(" ", ""); nameAttribute = nameAttribute.replace("<", "<"); nameAttribute = nameAttribute.replace(">", ">"); nameAttribute = nameAttribute.replace(""", "\""); nameAttribute = nameAttribute.replace("­", "-"); nameAttribute = nameAttribute.replace("&", "&"); nameAttribute = nameAttribute.replace("<", "<"); nameAttribute = nameAttribute.replace(">", ">"); for (k = 0; k < vowel.length; k++) { nameAttribute = nameAttribute.replace("&" + vowel[k] + "acute;", vowel_accent[k]); } p = Pattern.compile("\\s+"); m = p.matcher(nameAttribute); nameAttribute = m.replaceAll(" "); p = Pattern.compile("\\s"); m = p.matcher(nameAttribute); nameAttribute = m.replaceAll(""); if (nameAttribute.contains(" ")) { StringTokenizer tokenUcfirts = new StringTokenizer(nameAttribute, " "); String lineUcfirts = ""; if (tokenUcfirts.hasMoreTokens()) { lineUcfirts = tokenUcfirts.nextToken(); } while (tokenUcfirts.hasMoreTokens()) { lineUcfirts = lineUcfirts.concat(UcFirst(tokenUcfirts.nextToken())); } nameAttribute = lineUcfirts; } if (nameAttribute.equals("") || nameAttribute.equals("?") || nameAttribute.equals("<null>") || nameAttribute.equals(nullValue)) { nameAttribute = "ATTRIBUTE_" + (j + 1) + ""; } attribute[j].setName(nameAttribute); j++; }//end if }//end while }//end if if (nameChildren.equalsIgnoreCase("tr")) { break; } } } else{ for (i = 0; i < numAttributes; i++) { attribute[i].setName("a" + i); } } // Los demás hijos contienen los datos while (it.hasNext()) { children = (Element) it.next(); nameChildren = (String) children.getName(); if (nameChildren.equalsIgnoreCase("tr")) { List instances = children.getChildren(); Iterator it_instance = instances.iterator(); j = 0; while (it_instance.hasNext()) { Element element_children = (Element) it_instance.next(); String element_name = (String) element_children.getName(); if (element_name.equalsIgnoreCase("td") || element_name.equalsIgnoreCase("th")) { value = ""; value = element_children.getText(); p = Pattern.compile("^\\s+"); m = p.matcher(value); value = m.replaceAll(""); p = Pattern.compile("\\s+$"); m = p.matcher(value); value = m.replaceAll(""); value = value.replace(" ", ""); value = value.replace("<", "<"); value = value.replace(">", ">"); value = value.replace(""", "\""); value = value.replace("­", "-"); value = value.replace("&", "&"); value = value.replace("<", "<"); value = value.replace(">", ">"); for (k = 0; k < vowel.length; k++) { value = value.replace("&" + vowel[k] + "acute;", vowel_accent[k]); } if (value.equals("") || value.equals(" ") || value.equals("<null>") || value.equals(nullValue)) { value = "?"; } data[j].addElement(value); j++; }//end if }//end while }//end if }//end while for (i = 0; i < data[0].size(); i++) { for (j = 0; j < numAttributes; j++) { value = (String) data[j].elementAt(i); types[j].addElement(DataType(value)); } } for (i = 0; i < numAttributes; i++) { if (types[i].contains(NOMINAL)) { attribute[i].setType(NOMINAL); } else { if (types[i].contains(REAL)) { attribute[i].setType(REAL); } else { if (types[i].contains(INTEGER)) { attribute[i].setType(INTEGER); } else { attribute[i].setType(-1); } } } } for (i = 0; i < data[0].size(); i++) { for (j = 0; j < numAttributes; j++) { value = (String) data[j].elementAt(i); type = attribute[j].getType(); if (type == NOMINAL) { p = Pattern.compile("[^A-ZÑa-zñ0-9_-]+"); m = p.matcher(value); /** * Cambio hecho para que los nominales con espacios en blanco se dejen * con subrayado bajo "_" y sin comillas simples. Se añade la siguiente linea */ value = value.replace(" ", "_"); if (m.find() && !value.startsWith("'") && !value.endsWith("'") && !value.equals("?")) { /** * Cambio hecho para que los nominales con espacios en blanco se dejen * con subrayado bajo "_" y sin comillas simples. Se comenta la siguiente linea */ /* //value="'"+value+"'"; */ data[j].set(i, value); } if (!(attribute[j].isNominalValue(value)) && !value.equals("?")) { attribute[j].addNominalValue(value); } } if (type == INTEGER) { if (!value.equals("?")) { actualValueInt = Integer.valueOf(value); data[j].set(i, actualValueInt); if ((attribute[j].getFixedBounds()) == false) { attribute[j].setBounds(actualValueInt, actualValueInt); } else { min = attribute[j].getMinAttribute(); max = attribute[j].getMaxAttribute(); if (actualValueInt < min) { attribute[j].setBounds(actualValueInt, max); } if (actualValueInt > max) { attribute[j].setBounds(min, actualValueInt); } } } } if (type == REAL) { if (!value.equals("?")) { actualValue = Double.valueOf(value); data[j].set(i, actualValue); if ((attribute[j].getFixedBounds()) == false) { attribute[j].setBounds(actualValue, actualValue); } else { min = attribute[j].getMinAttribute(); max = attribute[j].getMaxAttribute(); if (actualValue < min) { attribute[j].setBounds(actualValue, max); } if (actualValue > max) { attribute[j].setBounds(min, actualValue); } } } } }//end while }//end while /* Insertamos el nombre de la relación que será el mismo que el del * fichero pasado, pero sin extensión*/ nameRelation = fileInput.getName(); p = Pattern.compile("\\.[A-Za-z]+"); m = p.matcher(nameRelation); nameRelation = m.replaceAll(""); p = Pattern.compile("\\s+"); m = p.matcher(nameRelation); nameRelation = m.replaceAll(""); f = new File(fileXhtml); f.delete(); super.Save(pathnameOutput); } catch (Exception e) { f = new File(fileXhtml); f.delete(); System.out.println(e); System.exit(1); } } /* * Método recursivo que devuelve el texto que contiene todos los descendientes * de un nodo o etiqueta de un elemento xml. * * @param Element current que indica que nodo o etiqueta xml actual. * @param int cont Variable que se utiliza como contador de descendientes. * * @return String . Devuelve el valor de la variable auxiliar lineAux * que almacena el texto de todos los descendientes de un nodo separado * cada uno por un espacio en blanco. * * @throws Exception */ public String ListChildrenText(Element current, int cont) { if (cont == 0) { lineAux = ""; } if ((current.getChildren()).size() == 0) { lineAux = lineAux.concat(current.getText() + " "); } List children = current.getChildren(); Iterator iterator = children.iterator(); while (iterator.hasNext()) { Element child = (Element) iterator.next(); ListChildrenText(child, cont++); } return lineAux; } //end listChildrenText() /* Método utilizado para convertir el fichero de datos con formato html * pasado por el parámetro fileHtml a un fichero con formato xhtml * en la ruta que indica el parámetro String fileXhtmlAux. * * @param String fileHtml fichero de datos html. * @param String fileXhtmlAux ruta del fichero donde se almacenará el fichero * xhtml generado tras la conversión del fichero html a xhtml. * * @throws Exception */ public void ConverterXhtml(String fileHtml, String fileXhtmlAux) throws Exception { Tidy tidy = new Tidy(); FileInputStream in = new FileInputStream(fileHtml); FileOutputStream out = new FileOutputStream(fileXhtmlAux); tidy.setTidyMark(false); tidy.setDocType("omit"); tidy.setAltText(""); tidy.setFixBackslash(true); tidy.setFixComments(true); tidy.setXmlPi(true); tidy.setQuoteAmpersand(true); tidy.setQuoteNbsp(true); tidy.setNumEntities(true); tidy.setXmlOut(true); tidy.setWraplen(999); tidy.setWriteback(true); tidy.setQuoteMarks(true); tidy.setLogicalEmphasis(true); tidy.setEncloseText(true); tidy.setHideEndTags(true); tidy.setShowWarnings(false); tidy.setQuiet(true); tidy.setXHTML(true); tidy.parse(in, out); in.close(); out.close(); }//end ConverterXhtml() /* * Método encargado de recorrer todo el árbol xml para encontrar * el nodo padre del nodo o etiqueta cuyo nombre coincida con el valor del parámetro childrenName. * El nodo padre de dicha etiqueta será asignado a la variable miembro * root. * * @param Element current. Elemento o nodo xml actual. * @param String childrenName. Variable String que indica el * nombre de la etiqueta a buscar. * */ public void FindParent(Element current, String childrenName) { if (current.getName().equalsIgnoreCase(childrenName)) { this.root = current.getParentElement(); return; } else { List children = current.getChildren(); Iterator iterator = children.iterator(); while (iterator.hasNext()) { Element child = (Element) iterator.next(); FindParent(child, childrenName); } } } //end FindParent() }//end class HtmlToKeel()