/*! ****************************************************************************** * * Pentaho Data Integration * * Copyright (C) 2002-2017 by Pentaho : http://www.pentaho.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.pentaho.di.core.row; import java.math.BigDecimal; import java.util.Date; import org.pentaho.di.core.Const; import org.pentaho.di.core.exception.KettleValueException; import org.pentaho.di.core.row.value.ValueMetaBase; import org.pentaho.di.core.row.value.ValueMetaBigNumber; import org.pentaho.di.core.row.value.ValueMetaBinary; import org.pentaho.di.core.row.value.ValueMetaBoolean; import org.pentaho.di.core.row.value.ValueMetaDate; import org.pentaho.di.core.row.value.ValueMetaInteger; import org.pentaho.di.core.row.value.ValueMetaNumber; import org.pentaho.di.core.row.value.ValueMetaSerializable; import org.pentaho.di.core.row.value.ValueMetaString; import org.pentaho.di.core.xml.XMLHandler; import org.w3c.dom.Node; public class ValueMetaAndData { public static final String XML_TAG = "value"; private ValueMetaInterface valueMeta; private Object valueData; public ValueMetaAndData() { } /** * @param valueMeta * @param valueData */ public ValueMetaAndData( ValueMetaInterface valueMeta, Object valueData ) { this.valueMeta = valueMeta; this.valueData = valueData; } public ValueMetaAndData( String valueName, Object valueData ) throws KettleValueException { this.valueData = valueData; if ( valueData instanceof String ) { this.valueMeta = new ValueMetaString( valueName ); } else if ( valueData instanceof Double ) { this.valueMeta = new ValueMetaNumber( valueName ); } else if ( valueData instanceof Long ) { this.valueMeta = new ValueMetaInteger( valueName ); } else if ( valueData instanceof Date ) { this.valueMeta = new ValueMetaDate( valueName, ValueMetaInterface.TYPE_DATE ); } else if ( valueData instanceof BigDecimal ) { this.valueMeta = new ValueMetaBigNumber( valueName ); } else if ( valueData instanceof Boolean ) { this.valueMeta = new ValueMetaBoolean( valueName ); } else if ( valueData instanceof byte[] ) { this.valueMeta = new ValueMetaBinary( valueName ); } else { this.valueMeta = new ValueMetaSerializable( valueName ); } } @Override public Object clone() { ValueMetaAndData vmad = new ValueMetaAndData(); try { vmad.valueData = valueMeta.cloneValueData( valueData ); } catch ( KettleValueException e ) { vmad.valueData = null; // TODO: should we really do this? Is it safe? } vmad.valueMeta = valueMeta.clone(); return vmad; } public static final String VALUE_REPOSITORY_NUMBER_CONVERSION_MASK = "#.#"; public static final String VALUE_REPOSITORY_INTEGER_CONVERSION_MASK = "#"; public static final String VALUE_REPOSITORY_DATE_CONVERSION_MASK = "yyyy/MM/dd HH:mm:ss.SSS"; public static final String VALUE_REPOSITORY_DECIMAL_SYMBOL = "."; public static final String VALUE_REPOSITORY_GROUPING_SYMBOL = ","; @Override public String toString() { try { return valueMeta.getString( valueData ); } catch ( KettleValueException e ) { return "<![" + e.getMessage() + "]!>"; } } /** * Produce the XML representation of this value. * * @return a String containing the XML to represent this Value. * @throws KettleValueException * in case there is a data conversion error, only throws in case of lazy conversion */ public String getXML() throws KettleValueException { ValueMetaInterface meta = valueMeta.clone(); meta.setDecimalSymbol( "." ); meta.setGroupingSymbol( null ); meta.setCurrencySymbol( null ); StringBuilder retval = new StringBuilder( 128 ); retval.append( "<" + XML_TAG + ">" ); retval.append( XMLHandler.addTagValue( "name", meta.getName(), false ) ); retval.append( XMLHandler.addTagValue( "type", meta.getTypeDesc(), false ) ); try { retval.append( XMLHandler.addTagValue( "text", meta.getCompatibleString( valueData ), false ) ); } catch ( KettleValueException e ) { // LogWriter.getInstance().logError(toString(), Const.getStackTracker(e)); retval.append( XMLHandler.addTagValue( "text", "", false ) ); } retval.append( XMLHandler.addTagValue( "length", meta.getLength(), false ) ); retval.append( XMLHandler.addTagValue( "precision", meta.getPrecision(), false ) ); retval.append( XMLHandler.addTagValue( "isnull", meta.isNull( valueData ), false ) ); retval.append( XMLHandler.addTagValue( "mask", meta.getConversionMask(), false ) ); retval.append( "</" + XML_TAG + ">" ); return retval.toString(); } /** * Construct a new Value and read the data from XML * * @param valnode * The XML Node to read from. */ public ValueMetaAndData( Node valnode ) { this(); loadXML( valnode ); } /** * Read the data for this Value from an XML Node * * @param valnode * The XML Node to read from * @return true if all went well, false if something went wrong. */ public boolean loadXML( Node valnode ) { valueMeta = null; try { String valname = XMLHandler.getTagValue( valnode, "name" ); int valtype = ValueMetaBase.getType( XMLHandler.getTagValue( valnode, "type" ) ); String text = XMLHandler.getTagValue( valnode, "text" ); boolean isnull = "Y".equalsIgnoreCase( XMLHandler.getTagValue( valnode, "isnull" ) ); int len = Const.toInt( XMLHandler.getTagValue( valnode, "length" ), -1 ); int prec = Const.toInt( XMLHandler.getTagValue( valnode, "precision" ), -1 ); String mask = XMLHandler.getTagValue( valnode, "mask" ); valueMeta = new ValueMeta( valname, valtype ); valueData = text; valueMeta.setLength( len ); valueMeta.setPrecision( prec ); if ( mask != null ) { valueMeta.setConversionMask( mask ); } if ( valtype != ValueMetaInterface.TYPE_STRING ) { ValueMetaInterface originMeta = new ValueMetaString( valname ); if ( valueMeta.isNumeric() ) { originMeta.setDecimalSymbol( "." ); originMeta.setGroupingSymbol( null ); originMeta.setCurrencySymbol( null ); } if ( valtype == ValueMetaInterface.TYPE_DATE ) { originMeta.setConversionMask( ValueMetaBase.COMPATIBLE_DATE_FORMAT_PATTERN ); } valueData = Const.trim( text ); valueData = valueMeta.convertData( originMeta, valueData ); } if ( isnull ) { valueData = null; } } catch ( Exception e ) { valueData = null; return false; } return true; } public String toStringMeta() { return valueMeta.toStringMeta(); } /** * @return the valueData */ public Object getValueData() { return valueData; } /** * @param valueData * the valueData to set */ public void setValueData( Object valueData ) { this.valueData = valueData; } /** * @return the valueMeta */ public ValueMetaInterface getValueMeta() { return valueMeta; } /** * @param valueMeta * the valueMeta to set */ public void setValueMeta( ValueMetaInterface valueMeta ) { this.valueMeta = valueMeta; } }