Java tutorial
/* * Copyright (C) 2016 Singular Studios (a.k.a Atom Tecnologia) - www.opensingular.com * * Licensed 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 org.opensingular.internal.lib.commons.xml; import org.apache.commons.lang3.StringUtils; import org.opensingular.internal.lib.commons.json.JSONToolkit; import org.opensingular.lib.commons.base.SingularException; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.Text; import java.io.ByteArrayOutputStream; import java.io.CharArrayWriter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.io.PrintWriter; import java.io.Serializable; import java.io.StringWriter; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Iterator; import java.util.List; /** * Representa um Element com diversos mtodos utilitrios para * leitura e montagem do XML. Essa classe substitui a classe XMLToolkit. * O MElement um Element (implementa essa interface) adicionado dos mtodos * do XMLToolkit. * <p/> * possvel montar uma rvore XML com objeto org.w3c.dom.Element * usando os mtodos desta classe, no entanto, este procedimento no * prtico, pois exige mais de um passo para adicionar uma * nica informao. * <p/> * A montagem de um estrutura de objetos Element em vez do arquivo * XML tambm bem mais simples e de melhor performance Evita-se fazer um * parse do arquivo. * <p/> * <p/> * <b>Exemplo de uso</b>:<br> * <p/> * Passo 1: <i>Cria o elemento raiz:</i> * <xmp> * MElement raiz = MElement.newInstance("pedido"); * raiz.printTabulado(System.out); * // XML resultado: * // <pedido/> * </xmp> * <p/> * Passo 2: <i>Adicionar sub-elementos:</i> * <xmp> * MElement item1 = raiz.addElement("item"); * MElement item2 = raiz.addElement("item"); * raiz.printTabulado(System.out); * // XML resultado em raiz: * // <pedido> * // <item/> * // <item/> * // </pedido> * </xmp> * <p/> * Passo 3: <i>Adicionar elementos com valores:</i> * <xmp> * item1.addElement("@cod",310); * item1.addElement("nome","arroz"); * item1.addElement("qtd",10); * item2.addElement("@cod",410); * item2.addElement("nome","milho"); * item2.addElement("qtd",21); * item2.addElement("unidade","kg"); * raiz.addElement("responsavel","Paulo Santos"); * raiz.printTabulado(System.out); * // XML resultado em raiz: * // <pedido> * // <item cod="310"> * // <nome>arroz</nome> * // <qtd>10</qtd> * // </item> * // <item cod="410"> * // <nome>milho</nome> * // <qtd>21</qtd> * // <unidade>kg</unidade> * // </item> * // <responsavel>Paulo Santos</responsavel> * // </pedido> * </xmp> * <p/> * Passo 4: <i>Percorrendo todos os elementos filhos:</i> * <xmp> * //getPrimeiroFilho() e getProximoIrmao j retornam MElement * MElement filho = raiz.getPrimeiroFilho(); * while (filhos != null) { * System.out.println(filho.getNodeName()); * filhos = filhos.getProximoIrmao() * } * // XML resultado: * // item * // item * // responsavel * </xmp> * <p/> * Passo 5: <i>Percorrendo todos os elementos "item":</i> * <xmp> * //getPrimeiroFilho(String) e getProximoGemeo j retornam MElement * MElement item = raiz.getPrimeiroFilho("item"); * while (item != null) { * System.out.println( * item.getValor("@cod") + " - " + //Le um atributo * item.getValor("nome") + " - " + //Le o valor de um Element * item.formatNumber("qtd",1) + " " +//Le formatando a saida * item.getValor("unidade","n/d")); //Le valor (usando default) * <p/> * item = item.getProximoGemeo() * } * // XML resultado: * // 310 - arroz - 10.0 - kg * // 410 - milho - 21.0 - n/d * </xmp> * <p/> * Passo 6: <i>Percorrendo todos os elementos "item" com qtd=21:</i> * <xmp> * // selectElements aceita consultas xPath * MElementResult item = raiz.selectElements("item[qtd=21]"); * while (item.next()) { * System.out.println( * item.getValor("@cod") + " - " + //Le um atributo * item.getValor("nome") + " - "); //Le o valor de um Element * } * // XML resultado: * // 410 - milho * </xmp> * <p/> * <b>Criando um MElement.</b> Existe 3 formas de se obter um MElement: * <xmp> * //Novo element * MElement raiz1 = MElement.newIntance("pedido"); * <p/> * //Novo element com namespace * MElement raiz2 = MElement.newIntance("http://www.com/ordem", "pedido"); * <p/> * //Convertendo um Element * //Toda alterao em wrapper reflete-se no Element original. * Element original = .... * MElement wraper = MElement.toElement(original); * </xmp> * <p/> * <b>MElement e SQL.</b> Um problema muito comum o tratamento de null * tanto ao ler quanto ao gravar no banco de dados. O MElement possui alguns * facilitadores nesse sentido.<p> * <p/> * <i>Montando XML a partir do Banco</i> * <xmp> * <pre> * MElement raiz = MElement.newInstance("pedido"); * java.sql.Date agora = new java.sql.Date(System.currentTimeMillis()); * * Resultset rs = .... * * while(rs.next()) { * MElement item = raiz.addElement("item"); * * // Se o nome for null, o addElement(String,String) dispara erro * // Nesse caso no problema, pois no banco o campo not null * item.addElement("nome", rs.getString("nome")); * item.addElement("qtd" , rs.getInt("qtd"); * * // Por ser possvel que o campo und seja null, utilizado * // o mtodo addElement(String, String, String) onde o ltimo valor * // o default (se o segundo=null). Por ser o terceiro null, caso o * // rs.getString("und") seja null, simplesmente no adicionada a tag * item.addElement("und" , rs.getString("und"), null); * * // Nesse caso adiciona o terceiro valor se dt for null * item.addElement("dt" , rs.getDate("dt"), agora); * } * </pre> * </xmp> * <p/> * <i>Preenchendo um PreparedStatement a partir do MElement</i> * <pre> * <xmp> * MElement raiz =.... * PreparedStatement ps = .... * * //Caso qualquer um dos 4 campos sejam null, j chama ps.setNull(int) * * //Mtodos especficos para os tipos principais * raiz.setSQLInt(ps, 1, "id"); * raiz.setSQLString(ps, 2, "nome"); * raiz.setSQLDouble(ps, 3, "salario"); * * //Recebe como parmetro o tipo SQL a ser utilizado no parmetro * raiz.setSQL(ps, 4, TYPES.VARCHAR, "descr"); * </xmp> * </pre> * * @author Daniel C. Bordin */ public abstract class MElement implements Element, Serializable { /** * Pega em tempo de compilao situaes onde tenta-se converte MElement * para MElement. Como tal passo totalmente desnecessrio, retorna void. * Na prtica gera um erro em tempo de compilao em tal caso. * * @param no elemento no precisa ser convertido */ public static void toMElement(MElement no) { throw SingularException.rethrow("No deveria ser chamadado esse metodo com um parmetro MElement"); } public static MElement toMElement(Element no) { if (no == null) { return null; } else if (no instanceof MElement) { return (MElement) no; } return new MElementWrapper(no); } /** * Gerar um wrapper MElement baseado no Node informado. * * @param no Element original. * @return Null se no for null. O prprio se esse j for MElement. */ public static MElement toMElement(Node no) { if (no == null) { return null; } else if (no instanceof MElement) { return (MElement) no; } else if (!XmlUtil.isNodeTypeElement(no)) { throw new SingularException("no " + XPathToolkit.getFullPath(no) + " no Element"); } return new MElementWrapper((Element) no); } /** * Cria um novo MElement com tag raiz com o nome da classe informada. O * MElement contm internamente um Element embutido. * * @param toCall Classe cujo nome sera o nome da tag * @return MElement wrapper. */ public static MElement newInstance(Class<?> toCall) { return newInstance(toCall.getName().replace('.', '-')); } /** * Cria um novo MElement com tag raiz no nome informado. O MElement contm * internamente um Element embutido. * * @param nomeRaiz nome da tag raiz * @return MElement wrapper. */ public static MElement newInstance(String nomeRaiz) { return new MElementWrapper(nomeRaiz); } /** * Cria um novo MElement com tag raiz no nome e namespace especificados. O * MElement contm internamente um Element embutido. MElement retornado. * * @param nameSpaceURI Nome do namespace. Tipicamente o name space possui o * formato de uma URL (no obrigatrio) no formato, por exemplo, * http://www.miranteinfo.com/sisfinanceiro/cobranca/registraPagamento. * @param nomeRaiz o nome do elemento que ser criado. Pode conter prefixo * (ex.: "fi:ContaPagamento"). * @return - */ public static MElement newInstance(String nameSpaceURI, String nomeRaiz) { return new MElementWrapper(nameSpaceURI, nomeRaiz); } /** * Retorna o valor do no passado como parmetro. Se for um Element retorna o * texto imediatamente abaixo. * * @param no do qual ser extraido o texto * @return pdoe ser null */ static String getValorTexto(Node no) { //No private, pois a classe XMLToolkit tambm utiliza if (no == null) { return null; } switch (no.getNodeType()) { case Node.ELEMENT_NODE: Node n = no.getFirstChild(); if (XmlUtil.isNodeTypeText(n)) { return n.getNodeValue(); } break; case Node.ATTRIBUTE_NODE: case Node.TEXT_NODE: String valor = no.getNodeValue(); if (!StringUtils.isEmpty(valor)) { return valor; } break; default: throw new SingularException("getValorTexto(Node) no trata n " + XPathToolkit.getNomeTipo(no)); } return null; } public final MDocument getMDocument() { return MDocument.toMDocument(getOwnerDocument()); } public final void addElement(MElement e) { appendChild(e.getOriginal()); } abstract Element getOriginal(); /** * Adiciona um element com o nome informado e devolve a referncia. * * @param nome do novo element filho * @return Elemento criado */ public final MElement addElement(String nome) { return addElementNS(null, nome); } /** * Adiciona um element com o nome informado no namespace especificado. * * @param namespaceURI - * @param qualifiedName Nome do novo element filho * @return Elemento criado */ public final MElement addElementNS(String namespaceURI, String qualifiedName) { return toMElement(MElementWrapper.addElementNS(this, namespaceURI, qualifiedName)); } /** * Adiciona um element como o nome informado como filho do atual. * * @param nome do MElement a ser criado * @param valor Se for null, um exception disparada. * @return O MElement resultado. */ public final MElement addElement(String nome, String valor) { return toMElement(MElementWrapper.addElement(this, nome, valor)); } /** * Adiciona um no como o nome informado e com o valor informado ou com o * default na ausencia do primeiro. Se o default tambm for null, ento o no * no adicionado * * @param nome do MElement a ser criado * @param valor - * @param defaultV a ser utilizado se valor==null * @return O MElement resultado. */ public final MElement addElement(String nome, String valor, String defaultV) { if (valor != null) { return addElement(nome, valor); } else if (defaultV != null) { return addElement(nome, defaultV); } return null; } /** * Adiciona o elemento como o valor informado como objeto fazendo as devidas * converes se necessrio. Trata os seguintes objetos de forma especial: * Integer, Long, Double, java.util.Date. * * @param nome do elemento a ser criado * @param o Objeto a ser convertido para texto * @return MElement criado */ public final MElement addElement(String nome, Object o) { if (o == null) { return addElement(nome, (String) null); } else if (o instanceof String) { //Apenas para no ter que sempre passar por todos os if //em geral ser String return addElement(nome, (String) o); } else if (o instanceof Integer) { return addElement(nome, ((Integer) o).intValue()); } else if (o instanceof Long) { return addElement(nome, ((Long) o).longValue()); } else if (o instanceof Double) { return addElement(nome, ((Double) o).doubleValue()); } else if (o instanceof java.util.Date) { return addElement(nome, (java.util.Date) o); } else if (o instanceof Calendar) { return addElement(nome, (Calendar) o); } else if (o instanceof InputStream) { return addElement(nome, (InputStream) o); } else if (o instanceof byte[]) { return addElement(nome, (byte[]) o); } else { return addElement(nome, o.toString()); } } /** * Adiciona o elemento como o valor informado como objeto fazendo as devidas * converes se necessrio. Se o valor null, utiliza o valor default. Se o * default tambm for null, ento o no no adicionado. * * @param nome do elemento a ser criado * @param valor Objeto a ser convertido para texto * @param defaultV a ser utilizado se valor==null * @return MElement criado */ public final MElement addElement(String nome, Object valor, Object defaultV) { if (valor != null) { return addElement(nome, valor); } else if (defaultV != null) { return addElement(nome, defaultV); } return null; } /** * Cria um no indicado pelo nome com o texto resultado da convero do * double. * * @param nome do element ou atributo a ser criado * @param valor a ser atribuito * @return MElement criado ou dono do atributo criado */ public final MElement addElement(String nome, double valor) { return addElement(nome, Double.toString(valor)); } /** * Cria um no indicado pelo nome com o texto resultado da convero do * double segundo a preciso desejada. * * @param nome do element ou atributo a ser criado * @param valor a ser atribuito * @param precisao Informa quantas casas depois d virgula deseja-se manter. * Se for negativo arredonta os digitos antes da virgula. * @return MElement criado ou dono do atributo criado */ public final MElement addElement(String nome, double valor, int precisao) { double m = Math.pow(10, precisao); String sValor = Double.toString(Math.rint(Math.round(valor * m)) / m); return addElement(nome, sValor); } /** * Cria um no indicado pelo nome com o texto resultado da convero do int. * * @param nome do element ou atributo a ser criado * @param valor a ser atribuito * @return MElement criado ou dono do atributo criado */ public final MElement addElement(String nome, int valor) { return addElement(nome, Integer.toString(valor)); } /** * Cria um no indicado pelo nome com o texto resultado da convero do long. * * @param nome do element ou atributo a ser criado * @param valor a ser atribuito * @return MElement criado ou dono do atributo criado */ public final MElement addElement(String nome, long valor) { return addElement(nome, Long.toString(valor)); } /** * Adiciona um elemento binario no formato BASE64 dentro do elemento pai. O * formato BASE64 definido pelo RFC1521 do RFC1521. Ele transforma um * binrio em uma string, um codificao de 6 bits. Deste modo, um array * binrio ocupa 33% mais espao no formato BASE, contudo passa a ser uma * string simples. necessrio levar em considerao questes de gasto de * memria e de custo de converso de binrio para string e string para * binrio ao se decidir pelo uso deste formato. * * @param nome o nome do elemento que ser inserido * @param valor o array binrio do elemento adicionado (a ser convertido p/ * BASE64) * @return o elemento que foi adicionado */ public final MElement addElement(String nome, byte[] valor) { return addElement(nome, MElementWrapper.toBASE64(valor)); } /** * Adiciona um elemento binario no formato BASE64 dentro do elemento pai at * esgotar a InputStream. O formato BASE64 definido pelo RFC1521 do * RFC1521. Ele transforma um binrio em uma string, um codificao de 6 * bits. Deste modo, um array binrio ocupa 33% mais espao no formato BASE, * contudo passa a ser uma string simples. necessrio levar em * considerao questes de gasto de memria e de custo de converso de * binrio para string e string para binrio ao se decidir pelo uso deste * formato. * * @param nome o nome do elemento que ser inserido * @param in Stream com os dados a serem convertidos p/ BASE64. * @return o elemento que foi adicionado * @throws IOException se erro na leitura dos bytes */ public final MElement addElement(String nome, InputStream in) { return addElement(nome, MElementWrapper.toBASE64(in)); } /** * Adiciona o no com o nome indicado com o valor boolean informado. * * @param nome do MElement a ser criado * @param valor - * @return O MElement criado (a menos que nome aponte para um atributo). */ public final MElement addBoolean(String nome, boolean valor) { if (valor) { return addElement(nome, Boolean.TRUE.toString()); } else { return addElement(nome, Boolean.FALSE.toString()); } } /** * Adiciona o no com o nome indicado considerando-o como um inteiro. * * @param nome do MELement a ser criado * @param valor Se for null, uma exception disparada. * @return O MElement criado (a menos que nome aponte para um atributo). */ public final MElement addInt(String nome, String valor) { if (valor == null) { return addElement(nome, (String) null); } else { String s = valor.trim(); if (s.length() == 0) { return addElement(nome, (String) null); } Integer.parseInt(s); //NOSONAR Testa se um inteiro return addElement(nome, s); } } /** * Adiciona o no com o nome indicado considerando o valor como sendo uma * inteiro a qual ser convertida para o formato ISO8601. Caso o valor seja * null ou string em branco, ento considera o segundo valor para a * converso. Caso o valorDefault tambm seja null ou em braco, ento no * adiciona o n. * * @param nome do MELement a ser criado * @param valor - * @param valorDefault a ser utilizado se valor for null ou vazio * @return O MElement criado (a menos que nome aponte para um atributo). */ public final MElement addInt(String nome, String valor, Object valorDefault) { if (valor != null) { String v = valor.trim(); if (v.length() > 0) { Integer.parseInt(v); //NOSONAR Testa se um inteiro return addElement(nome, v); } } if (valorDefault != null) { if (valorDefault instanceof String) { String v = ((String) valorDefault).trim(); if (v.length() > 0) { Integer.parseInt(v); //NOSONAR Testa se um inteiro return addElement(nome, v); } } else if (valorDefault instanceof Integer) { return addElement(nome, valorDefault); } else { throw new SingularException( "Tipo default invlido (" + valorDefault.getClass().getName() + ") para um inteiro"); } } return null; } /** * Adiciona o no com o nome indicado considerando o valor como sendo uma * inteiro a qual ser convertida para o formato ISO8601. Caso o valor seja * null ou string em branco, ento considera o segundo valor para a * converso. Caso o valorDefault tambm seja null ou em braco, ento no * adiciona o n. * * @param nome do MELement a ser criado * @param valor - * @param valorDefault a ser utilizado se valor for null ou vazio * @return O MElement criado (a menos que nome aponte para um atributo). */ public final MElement addInt(String nome, String valor, int valorDefault) { if (valor != null) { String v = valor.trim(); if (v.length() > 0) { Integer.parseInt(v); //NOSONAR Testa se um inteiro return addElement(nome, v); } } return addElement(nome, valorDefault); } /** * Adiciona o no com o nome indicado considerando o valor como sendo uma * data a qual ser convertida para o formato ISO8601. * * @param nome do MELement a ser criado * @param valor Se for null, uma exception disparada. * @return O MElement criado (a menos que nome aponte para um atributo). */ public final MElement addDate(String nome, String valor) { if (valor == null) { return addElement(nome, (String) null); } else { return addElement(nome, ConversorToolkit.getDateFromData(valor)); } } /** * Adiciona o no com o nome indicado considerando o valor como sendo uma * data a qual ser convertida para o formato ISO8601. Caso o valor seja * null ou string em branco, ento considera o segundo valor para a * converso. Caso o valorDefault tambm seja null ou em braco, ento no * adiciona o n. * * @param nome do MELement a ser criado * @param valor - * @param valorDefault a ser utilizado se valor for null ou vazio * @return O MElement criado (a menos que nome aponte para um atributo). */ public final MElement addDate(String nome, String valor, String valorDefault) { String trim = StringUtils.trimToNull(valor); if (trim == null) { trim = StringUtils.trimToNull(valorDefault); } if (trim != null) { return addElement(nome, ConversorToolkit.getDateFromData(trim)); } return null; } /** * Adiciona um element como o nome e a data informada no formato ISO 8601. * * @param nome do MElement a ser criado * @param valor Se for null, um exception disparada. * @return O MElement resultado. */ public final MElement addElement(String nome, java.util.Date valor) { if (valor == null) { return addElement(nome, (String) null); } else { return addElement(nome, ConversorDataISO8601.format(valor)); } } /** * Adiciona um no como o nome informado e com o valor informado ou com o * default na ausencia do primeiro. Se o default tambm for null, ento o no * no adicionado. * * @param nome do MElement a ser criado * @param valor - * @param valorDefault a ser utilizado se valor==null * @return O MElement resultado. */ public final MElement addElement(String nome, java.util.Date valor, java.util.Date valorDefault) { if (valor != null) { return addElement(nome, ConversorDataISO8601.format(valor)); } else if (valorDefault != null) { return addElement(nome, ConversorDataISO8601.format(valorDefault)); } return null; } /** * Adiciona um element como o nome e o Calendar informada no formato ISO * 8601. * * @param nome do MElement a ser criado * @param valor Se for null, um exception disparada. * @return O MElement resultado. */ public final MElement addElement(String nome, Calendar valor) { if (valor == null) { return addElement(nome, (String) null); } else { return addElement(nome, ConversorDataISO8601.format(valor)); } } /** * Atualiza o Node (Element ou atributo) j exisitente. Se o Node no for * localizado, ento adiciona se o valor for diferente de null. * * @param xPath Caminho do Node a ser atualizado ou criado * @param value Novo valor do Node. Se for null, ento o valor limpo. Se o * element j existir e valor for null, ento transforma a tag em * empty, mas a mantm no XML. * @return O Node alterado ou criado, ou null se no for possvel atualizar * o valor do mesmo. */ public final Node updateNode(String xPath, String value) { Node n = getNode(xPath); if ((n == null) && !StringUtils.isEmpty(value)) { return addElement(xPath, value); } else if (n instanceof Element) { Node filho = n.getFirstChild(); if (filho == null) { if (!StringUtils.isEmpty(value)) { Document d = n.getOwnerDocument(); Text txt = d.createTextNode(value); n.appendChild(txt); } } else if (XmlUtil.isNodeTypeText(filho)) { if (!StringUtils.isEmpty(value)) { filho.setNodeValue(value); } else { n.removeChild(filho); } } else { return null; } } else if (n instanceof Attr) { //No h como saber quem o pai (n.getParentNode() retorna null) if (value == null) { return addElement(xPath, ""); //Fora a remoo do atributo } return addElement(xPath, value); } else { return null; } return n; } /** * Verifica se existe pelo menos um Element apontado pelo xPath. <br> * Dispara erro se existir um Node no endereo, mas esse no Element. * * @param xPath Endereo do Element ou consulta xpath * @return se getElement(xPath) != null * @see #possuiNode */ public final boolean possuiElement(String xPath) { return getElement(xPath) != null; } /** * Verifica se existe pelo menos um n apontado pelo xPath. * * @param xPath Endereo do n ou consulta xpath * @return se getNode(xPath) != null */ public final boolean possuiNode(String xPath) { return getNode(xPath) != null; } /** * Verifica se existe o n apontado pelo xPAth e se possui um texto. * * @param xPath Endereo do n ou consulta xpath * @return se getValor(xPath) != null */ public final boolean isNull(String xPath) { return getValor(xPath) == null; } /** * Conta o nmero de ocorrncias de Elements filhos do no. * * @return o nmero de ocorrncias */ public final int countFilhos() { return count(null); } /** * Conta o nmero de ocorrncias de Elements filhos do no com o nome * especificado. * * @param nome do elemento a ser procurado. Se for null conta todos. * @return o nmero de ocorrncias */ public final int count(String nome) { int qtd = 0; Node node = getFirstChild(); while (node != null) { if (XmlUtil.isNodeTypeElement(node, nome)) { qtd++; } node = node.getNextSibling(); } return qtd; } /** * Retorna o texto referente ao elemento atual (sub-texto). * * @return null se for um tag vazia (ex: <nome/>). */ public final String getValor() { return getValorTexto(this); } /** * Retorna o texto do elemento atual (sub-texto) com inteiro. Dispara * NullPointException se no houver um valor disponvel. * * @return - */ public final int getInt() { String s = getValor(); if (s == null) { throw new NullPointerException("Tag '" + getFullPath() + "' vazia"); } return Integer.parseInt(s); } /** * Retorna o texto do elemento atual (sub-texto) como long. Dispara * NullPointException se no houver um valor disponvel. * * @return - */ public final long getLong() { String s = getValor(); if (s == null) { throw new NullPointerException("Tag '" + getFullPath() + "' vazia"); } return Long.parseLong(s); } /** * Retorna o texto do elemento atual (sub-texto) como double. Dispara * NullPointException se no houver um valor disponvel. * * @return - */ public final double getDouble() { String s = getValor(); if (s == null) { throw new NullPointerException("Tag '" + getFullPath() + "' vazia"); } return Double.parseDouble(s); } /** * Obtem o valor no endereo fornecido. * * @param xPath Caminho para o valor (string) desejado * @return Se existe o destino e apontado pelo nome e nele existe um valor, * esse retornado. Caso contrrio devolve null. */ public final String getValor(String xPath) { return getValorTexto(getNode(xPath)); } /** * Obtem o valor no endereo fornecido, ou valor default, se a pesquisa * resultar em null. * * @param xPath Caminho para o valor (string) desejado * @param defaultV valor a retornado se a pesquisa for null * @return - */ public final String getValor(String xPath, String defaultV) { String s = getValorTexto(getNode(xPath)); if (s == null) { return defaultV; } return s; } /** * Obtem o valor no endereo fornecido. Se o xPAth no existir ou apontar * para um Element ou atributo sem texto, dispara um exception. * * @param xPath Caminho para o valor (string) desejado * @return - * @throws NullPointerException Se a pesquisa resultar em null */ public final String getValorNotNull(String xPath) throws NullPointerException { Node no = getNode(xPath); if (no == null) { throw new NullPointerException("xPath '" + xPath + "' no existe em '" + getFullPath() + "'"); } String valor = getValorTexto(no); if (valor == null) { throw new NullPointerException( "No '" + xPath + "' est vazio (fullPath=" + XPathToolkit.getFullPath(no) + ")"); } return valor; } /** * Busca os valores dos elementos filhos com o nome informado. Se o nome for * null, retorna o valor de todos os filhos. * * @param xPath dos elementos a terem os valores retornados * @return sempre diferente de null */ public final List<String> getValores(String xPath) { return XPathToolkit.getValores(this, xPath); } /** * Equivalente a getBoolean, serve para escrever cdigo mais legveis. * * @param xPath caminho xpath ou nome do elemento desejado * @return O boolean convertido de string */ public final boolean is(String xPath) { return getBoolean(xPath); } /** * Equivalente a getBoolean, serve para escrever cdigo mais legveis. * * @param xPath caminho xpath ou nome do elemento desejado * @param valorDefault Valor a ser utilziado se no encontrar nenhum valor * no caminho indicado no xPath * @return O boolean convertido de string */ public final boolean is(String xPath, boolean valorDefault) { return getBoolean(xPath, valorDefault); } /** * Retorna o valor em boolean do sub-elemento indicado pelo caminho xpath. * * @param xPath caminho xpath ou nome do elemento desejado * @return O boolean convertido de string */ public final boolean getBoolean(String xPath) { String s = getValorNotNull(xPath); if (Boolean.TRUE.toString().equals(s)) { return true; } else if (Boolean.FALSE.toString().equals(s)) { return false; } throw new SingularException("O valor em " + xPath + " no boolean = " + s); } /** * Retorna o valor em boolean do sub-elemento indicado pelo caminho xpath. * * @param xPath caminho xpath ou nome do elemento desejado * @param valorDefault Valor a ser utilziado se no encontrar nenhum valor * no caminho indicado no xPath * @return O boolean convertido de string */ public final boolean getBoolean(String xPath, boolean valorDefault) { String s = getValor(xPath); if (s == null) { return valorDefault; } else if (Boolean.TRUE.toString().equals(s)) { return true; } else if (Boolean.FALSE.toString().equals(s)) { return false; } throw new SingularException("O valor em " + xPath + " no boolean = " + s); } /** * Retorna o valor em int do sub-elemento indicado pelo caminho xpath. * * @param xPath caminho xpath ou nome do elemento desejado * @return O inteiro convertido de string */ public final int getInt(String xPath) { return Integer.parseInt(getValorNotNull(xPath)); } /** * Retorna o valor do no indicado pelo caminho xpath comvertido para * Integer. * * @param xPath caminho xpath ou nome do elemento desejado * @return O inteiro convertido de string ou null se getValor(xPath)==null. */ public final Integer getInteger(String xPath) { String s = getValor(xPath); if (s == null) { return null; } return Integer.valueOf(s); } /** * Retorna o valor em int do sub-elemento indicado pelo caminho xpath. * * @param xPath caminho xpath ou nome do elemento desejado * @param defaultV valor a se retornado se o xPath resultar em null * @return O long convertido de string */ public final int getInt(String xPath, int defaultV) { String s = getValor(xPath); if (s == null) { return defaultV; } return Integer.parseInt(s); } /** * Retorna o valor em long do sub-elemento indicado pelo caminho xpath. * * @param xPath caminho xpath ou nome do elemento desejado * @return O long convertido de string */ public final long getLong(String xPath) { return Long.parseLong(getValorNotNull(xPath)); } /** * Retorna o valor em long do sub-elemento indicado pelo caminho xpath. * * @param xPath caminho xpath ou nome do elemento desejado * @param defaultV valor a se retornado se o xPath resultar em null * @return O long convertido de string */ public final long getLong(String xPath, long defaultV) { String s = getValor(xPath); if (s == null) { return defaultV; } return Long.parseLong(s); } /** * Retorna o valor em double do sub-elemento indicado pelo caminho xpath. * Utiliza Double.parseDouble(), ou seja, o formato deve ser com ponto como * separador de decimal. * * @param xPath caminho xpath ou nome do elemento desejado * @return O double convertido de string */ public final double getDouble(String xPath) { return Double.parseDouble(getValorNotNull(xPath)); } /** * Retorna o valor em double do sub-elemento apontando pelo nome ou caminho * xpath apontado. Utiliza Double.parseDouble(), ou seja, o formato deve ser * com ponto como separador de decimal. * * @param xPath caminho xpath ou nome do elemento desejado * @param defaultV valor a se retornado se o xPath resultar em null * @return O double convertido de string */ public final double getDouble(String xPath, double defaultV) { String s = getValor(xPath); if (s == null) { return defaultV; } return Double.parseDouble(s); } /** * Retorna o valor do no indicado pelo caminho xpath comvertido para um * objeto Double. * * @param xPath caminho xpath ou nome do elemento desejado * @return O Double convertido de string ou null se getValor(xPath)==null. */ public final Double getDoubleObject(String xPath) { String s = getValor(xPath); if (s == null) { return null; } return new Double(s); } /** * Converte a String no endereo xPath com codificao BASE64 de volta para * um array de bytes. * * @param xPath endereo do valor (atributo, tag, etc.) a ser convertido * @return null se no encontrado */ public final byte[] getByteBASE64(String xPath) { return MElementWrapper.fromBASE64(getValor(xPath)); } /** * Converte a String no endereo xPath com codificao BASE64 de volta para * bytes escrevendo para a saida informada. * * @param xPath endereo do valor (atributo, tag, etc.) a ser convertido * @param out Destino do bytes convertidos * @throws IOException Se houver problemas de converso ou de escrita para a * saida. */ public final void getByteBASE64(String xPath, OutputStream out) { MElementWrapper.fromBASE64(getValorNotNull(xPath), out); } /** * Transforma o valor do campo para java.util.Date. Espera um campo no * formato "yyyy-mm-dd hh:mm:ss.fffffffff". * * @param xPath endereo do valor (atributo, tag, etc.) a ser convertido * @return - */ public final java.util.Date getDate(String xPath) { String valor = getValor(xPath); if (valor == null) { return null; } return ConversorDataISO8601.getDate(valor); } /** * Transforma o valor do campo para Calendar. Espera um campo no formato * "yyyy-mm-dd hh:mm:ss.fffffffff". * * @param xPath endereo do valor (atributo, tag, etc.) a ser convertido * @return - */ public final GregorianCalendar getCalendar(String xPath) { String valor = getValor(xPath); if (valor == null) { return null; } return ConversorDataISO8601.getCalendar(valor); } /** * Transforma o valor do campo em um nmero formato com separado de decimal * e milhar. Deixa livre a quantidade de casa decimais, ou seja, vai * colocar quanta forem necessrias. * * @param xPath endereo do valor (atributo, tag, etc.) a ser convertido * @return string vazia se o elemento no existir ou no tiver valor */ public final String formatNumber(String xPath) { return ConversorToolkit.printNumber(getDouble(xPath, 0), -1, false); } /** * Transforma o valor do campo em um nmero formato com separado de decimal * e milhar. Deixa livre a quantidade de casa decimais, ou seja, vai * colocar quanta forem necessrias. * * @param xPath endereo do valor (atributo, tag, etc.) a ser convertido * @param printZero Se falso e o valor zero, ento retorna string vazia * @return string vazia se o elemento no existir ou no tiver valor */ public final String formatNumber(String xPath, boolean printZero) { return ConversorToolkit.printNumber(getDouble(xPath, 0), -1, printZero); } /** * Transforma o valor do campo em um nmero formato com separado de decimal * e milhar. * * @param xPath endereo do valor (atributo, tag, etc.) a ser convertido * @param digitos qtd. de casas decimais a serem exibidas (-1 deixa livre). * @return zero se o elemento no existir ou no tiver valor */ public final String formatNumber(String xPath, int digitos) { return ConversorToolkit.printNumber(getDouble(xPath, 0), digitos); } /** * Transforma o valor do campo em um nmero formato com separado de decimal * e milhar com opo de no exibir zeros. * * @param xPath endereo do valor (atributo, tag, etc.) a ser convertido * @param digitos qtd. de casas decimais a serem exibidas (-1 deixa livre). * @param printZero Se falso e o valor zero, ento retorna string vazia * @return string vazia se o elemento no existir ou no tiver valor */ public final String formatNumber(String xPath, int digitos, boolean printZero) { return ConversorToolkit.printNumber(getDouble(xPath, 0), digitos, printZero); } /** * Transforma o valor do campo em uma string no formato de exibio de data * (dd/MM/yyyy). * * @param xPath endereo do valor (atributo, tag, etc.) a ser convertido * @return String vazia se o xPath no existir */ public final String formatDate(String xPath) { java.util.Date d = getDate(xPath); return (d == null) ? "" : ConversorToolkit.printDate(d); } /** * Transforma o valor do campo em uma string no formato de exibio de data * segundo o formato solicitado. * * @param xPath endereo do valor (atributo, tag, etc.) a ser convertido * @param formato a ser convertido pode ser "short", "medium", "long", * "full" ou ento customizado (ver java.text.SimpleDateFormat). * @return String vazia se o xPath no existir */ public final String formatDate(String xPath, String formato) { java.util.Date d = getDate(xPath); return (d == null) ? "" : ConversorToolkit.printDate(d, formato); } /** * Transforma o valor do campo em uma string no formato de exibio de hora * (hh:mm:ss). * * @param xPath endereo do valor (atributo, tag, etc.) a ser convertido * @return String vazia se o xPath no existir */ public final String formatHour(String xPath) { java.util.Date d = getDate(xPath); return (d == null) ? "" : ConversorToolkit.printHora(d); } /** * A partir do atual procura o Node no xPath (Elemento, atributo, etc.). * * @param xPath caminho do elemento desejado * @return O Node no destino ou null se o xPath no existir * @see XPathToolkit */ public final Node getNode(String xPath) { return XPathToolkit.selectNode(this, xPath); } /** * A partir do atual retorna um percorredor de todos os Element que * satisfazem a consulta xPath. Se a consulta for null, retorna todos os * filhos. Exemplos: * <p/> * <xmp>raiz.selectElements(null); //Retorna todos os filhos imediatos * raiz.selectElements("aluno"); //todos as tags "aluno" imediatas * raiz.selectElements("aluno/nota"); //todos as tags nota de baixo de * //todos os filhos aluno raiz.selectElements("aluno[@id=20]");//Todos as * tags aluno que possuem //um atributo id igual a 20 </xmp> * * @param xPath caminho do elemento desejado * @return Sempre diferente de null * @see XPathToolkit XPathToolkit para entender mais sobre xPath */ public final MElementResult selectElements(String xPath) { return new MElementResult(this, xPath); } /** * Segue a mesma lgia de selectElements(String), mas retorna como iterator. * * @param xPath caminho dos elementos desejados * @return Sempre diferente de null */ public final Iterator<MElement> iterator(String xPath) { MElementResult rs = new MElementResult(this, xPath); return rs.iterator(); } /** * A partir do atual procura o Element no xPath. * * @param xPath caminho do elemento desejado * @return O Node no destino ou null se o xPath no existir * @throws SingularException Se o node no xPath no for um Element * @see XPathToolkit */ public final MElement getElement(String xPath) { return toMElement(XPathToolkit.selectElement(this, xPath)); } /** * Retornas todos os elementos com um nome especfico. Uma forma mais rpida * (e talvez mais prtica de pegar todos os filhos) :<br> * <xmp>MElementResult e = raiz.selectElements(xPAth); while( e.next) { * //faz algo com e..... } </xmp> * * @param xPath dos elementos a serem retornados. Se for null retorna todos * os MElement imediantamente filhos. * @return a lista com os elementos ou um array de tamanho zero */ public final MElement[] getElements(String xPath) { return selectElements(xPath).getTodos(); } /** * Mtodo utilizado para colocar apenas o contedo de um elemento dentro de * outro elemento. O elemento, portanto, no copiado, apenas todos os * elementos dentro dele. * * @param no a ter seus filhos copiados */ public final void copyConteudo(Element no) { MElementWrapper.copyElement(this, no); } /** * Mtodo utilizado para colocar um elemento e todo o seu contedo dentro de * outro elemento, podendo ser usado um outro nome ao invs do nome do * elemento sendo copiado. Para manter o nome do elemento original, passar * <code>novoNome</code> igual a <code>null</code>. * * @param no Element a ser inserido * @param novoNome se diferente de null * @return Elemento copia crido debaixo do atual. */ public final MElement copy(Element no, String novoNome) { return toMElement(MElementWrapper.copyElement(this, no, novoNome)); } /** * Gera o path completo (em xpath) do elemento a partir de seu raiz. Pode * ser utilizado para recuperar o elemento via gerElemento no raiz. Se for * um elemento repetido, ento adiciona um ndice (formato [N]). * * @return string xPath do elemento. */ public final String getFullPath() { return XPathToolkit.getFullPath(this); } /** * Escreve o XML organizado as sub-tags em nveis (acresenta espao). Um * parse do string gerada por esse mtodo pode no gera o mesmo xml devido a * incluso de formatao (utilize o mtodo print()). * * @param out sada destino. */ public final void printTabulado(PrintStream out) { PrintWriter out2 = new PrintWriter(out); XMLToolkitWriter.printDocumentIndentado(out2, this, true); out2.flush(); } /** * Escreve o XML organizado as sub-tags em nveis (acresenta espao). Um * parse do string gerada por esse mtodo pode no gera o mesmo xml devido a * incluso de formatao (utilize o mtodo print()). * * @param out sada destino. */ public final void printTabulado(PrintWriter out) { XMLToolkitWriter.printDocumentIndentado(out, this, true); } /** * Escreve para a saida padro (System.out) o XML organizado as sub-tags em * nveis (acresenta espao). */ public final void printTabulado() { printTabulado(System.out); } /** * Escreve o XML de forma que um eventual parse gere o mesmo XML. Para * impresses mais legveis utilize printTabulado(). * * @param out sada destino */ public final void print(PrintStream out) { PrintWriter out2 = new PrintWriter(out); XMLToolkitWriter.printDocument(out2, this, true); out2.flush(); } /** * Escreve o XML de forma que um eventual parse gere o mesmo XML. Para * impresses mais legveis utilize printTabulado(). * * @param out sada destino */ public final void print(PrintWriter out) { XMLToolkitWriter.printDocument(out, this, true); } /** * Escreve o XML de forma que um eventual parse gere o mesmo XML. Para * impresses mais legveis utilize printTabulado(). * * @param out sada destino * @param printHeader Se true, adiciona string de indentificao de arquivo * XML. Se false, depois no ser possvel fazer parse do resultado * sem informaoes complementares (header). */ public final void print(PrintWriter out, boolean printHeader) { XMLToolkitWriter.printDocument(out, this, printHeader); } /** * Escreve o XML de forma que um eventual parse gere o mesmo XML. Para * impresses mais legveis utilize printTabulado(). * * @param out sada destino * @param printHeader Se true, adiciona string de indentificao de arquivo * XML. Se false, depois no ser possvel fazer parse do resultado * sem informaoes complementares (header). * @param converteEspeciais se verdadeiro converte os caracteres '<' '>' e '&' para * seus respectivos escapes. */ public final void print(PrintWriter out, boolean printHeader, boolean converteEspeciais) { XMLToolkitWriter.printDocument(out, this, printHeader, converteEspeciais); } /** * Retorna o Element que est antes do atual mas no mesmo nvel. * * @return null se o atual j for o primeiro Element da lista. */ public final MElement getIrmaoAnterior() { return procurarElementAnterior(getPreviousSibling(), null); } /** * Retorna o Element que est antes do atual mas no mesmo nvel e que possui * o mesmo nome do elemento atual. * * @return null se o atual j for o primeiro Element da lista com o nome. */ public final MElement getGemeoAnterior() { return procurarElementAnterior(getPreviousSibling(), getNodeName()); } /** * Retorna o prximo Element que est no mesmo nvel do elemento atual. * * @return null se esse for o ltimo elemento. */ public final MElement getProximoIrmao() { return procurarProximoElement(getNextSibling(), null); } /** * Retorna o prximo Element que est no mesmo nvel do elemento atual e que * possui o mesmo nome do Element atual. * * @return null se esse for o ltimo elemento com o nome. */ public final MElement getProximoGemeo() { return procurarProximoElement(getNextSibling(), getNodeName()); } /** * Retorna o primeiro Element filho do atual. * * @return null se no houve nenhum n filho do tipo Element */ public final MElement getPrimeiroFilho() { return procurarProximoElement(getFirstChild(), null); } /** * Retorna o primeiro Element filho do atual com um nome especfico. * * @param nome do Element filho a ser encontrado. * @return null se no houve nenhum n filho do tipo Element */ public final MElement getPrimeiroFilho(String nome) { if (nome == null) { throw new IllegalArgumentException("O nome no pode ser null"); } return procurarProximoElement(getFirstChild(), nome); } /** * Retorna o ultimo Element filho do atual. * * @return null se no houve nenhum n filho do tipo Element */ public final MElement getUltimoFilho() { return procurarElementAnterior(getLastChild(), null); } /** * Procura pelo node do tipo Element anterior (incluindo o n informado). * * @param no Ponto de partida da pesquisa * @param nome Nome do Element a ser retornado. Se for null retorna o * primeiro a ser encontrado. * @return Um Element ou null se no encontrar. */ private MElement procurarElementAnterior(Node no2, String nome) { for (Node current = no2; current != null; current = current.getPreviousSibling()) { if (XmlUtil.isNodeTypeElement(current, nome)) { return toMElement(current); } } return null; } /** * Procura pelo proximo node do tipo Element (incluindo o n informado). * * @param no Ponto de partida da pesquisa * @param nome Nome do Element a ser retornado. Se for null retorna o * primeiro a ser encontrado. * @return Um Element ou null se no encontrar. */ private MElement procurarProximoElement(Node no, String nome) { return toMElement(XmlUtil.nextSiblingOfTypeElement(no, nome)); } /** * Gera o XML to elemento conforme o funcionamento do mtodo printTabulado * (utilizar preferencialment printTabulado). Existe como convenincia * quando no houver um PrintWriter ou PrintStream disponvel. * * @return o XML com um tag por linha e alinhado conforme o nvel */ @Override public String toString() { CharArrayWriter writer = new CharArrayWriter(); PrintWriter out = new PrintWriter(writer); printTabulado(out); out.flush(); return writer.toString(); } /** * Gera o XML do elemento conforme o funcionamento do mtodo print (utilizar * preferencialment printTabulado). Existe como convenincia quando no * houver um PrintWriter ou PrintStream disponvel. * * @return a String que feito parse, retorna o mesmo conteudo */ public final String toStringExato() { CharArrayWriter writer = new CharArrayWriter(); PrintWriter out = new PrintWriter(writer); print(out, true, true); out.flush(); return writer.toString(); } /** * Gera o XML do elemento conforme o funcionamento do mtodo print (utilizar * preferencialment printTabulado). Existe como convenincia quando no * houver um PrintWriter ou PrintStream disponvel. * * @param printHeader Indica se ser adiciona o identificado inicial de * arquivo XML. Se for false, no ser possvel fazer parse do * resultado sem a adio de informaes complementares. * @return a String que feito parse, retorna o mesmo conteudo */ public final String toStringExato(boolean printHeader) { CharArrayWriter writer = new CharArrayWriter(); PrintWriter out = new PrintWriter(writer); print(out, printHeader, true); out.flush(); return writer.toString(); } /** * Gera o XML do elemento conforme o funcionamento do mtodo print. * * @return a byte array que feito parse, retorna o mesmo conteudo */ public final byte[] toByteArray() { ByteArrayOutputStream out = new ByteArrayOutputStream(); PrintWriter pw = new PrintWriter(out); print(pw); pw.flush(); return out.toByteArray(); } public String toJSONString() { final StringWriter sw = new StringWriter(); JSONToolkit.printJSON(new PrintWriter(sw), this); return sw.toString(); } }