/*! * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * 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 Lesser General Public License for more details. * * Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved. */ package org.pentaho.reporting.designer.core.util; import org.pentaho.reporting.engine.classic.core.util.PropertyLookupParser; import org.pentaho.reporting.engine.classic.core.util.beans.BeanException; import org.pentaho.reporting.engine.classic.core.util.beans.ConverterRegistry; import java.text.NumberFormat; import java.text.ParseException; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.Locale; import java.util.Random; public class Anonymizer { private static class MessageParser extends PropertyLookupParser { private Random r; private MessageParser( final Random r ) { setMarkerChar( '$' ); setOpeningBraceChar( '(' ); setClosingBraceChar( ')' ); this.r = r; } protected String lookupVariable( final String property ) { return property; } protected char postProcessCharacter( final char c ) { if ( Character.isWhitespace( c ) ) { return ' '; } else if ( Character.isUpperCase( c ) ) { return (char) ( r.nextInt( 'Z' - 'A' ) + 'A' ); } else if ( Character.isLetter( c ) ) { return (char) ( r.nextInt( 'z' - 'a' ) + 'a' ); } else { return ( c ); } } } private Random r; private HashMap<Object, Object> mappedValues; public Anonymizer() { this.r = new Random(); this.mappedValues = new HashMap<Object, Object>(); } public Object anonymizeMessage( final Object attribute ) throws BeanException { if ( attribute instanceof String == false ) { return anonymize( attribute ); } final String message = (String) attribute; final MessageParser mp = new MessageParser( r ); return mp.translateAndLookup( message ); } public Object anonymize( final Object value ) throws BeanException { final Object anonymized = anonymizeInternal( value ); if ( value != anonymized ) { final Object reused = mappedValues.get( value ); if ( reused != null ) { return reused; } else { mappedValues.put( value, anonymized ); return anonymized; } } else { return anonymized; } } private Object anonymizeInternal( final Object value ) throws BeanException { if ( value == null ) { return null; } if ( value instanceof String ) { return generateRandomText( String.valueOf( value ) ); } if ( value instanceof Number ) { final Number n = generateRandomNumber( (Number) value ); return ConverterRegistry.toPropertyValue( ConverterRegistry.toAttributeValue( n ), value.getClass() ); } if ( value instanceof Date ) { final Date n = generateRandomDate( (Date) value ); return ConverterRegistry.toPropertyValue( ConverterRegistry.toAttributeValue( n ), value.getClass() ); } // else admit defeat and return the ordinary value. return value; } private Date generateRandomDate( final Date value ) { final GregorianCalendar c = new GregorianCalendar(); c.setTime( value ); final int min = c.getActualMinimum( GregorianCalendar.DAY_OF_YEAR ); final int max = c.getActualMaximum( GregorianCalendar.DAY_OF_YEAR ); c.set( GregorianCalendar.DAY_OF_YEAR, r.nextInt( max - min ) + min ); if ( value instanceof java.sql.Date ) { return c.getTime(); } c.set( GregorianCalendar.HOUR_OF_DAY, 0 ); c.set( GregorianCalendar.MINUTE, 0 ); c.set( GregorianCalendar.SECOND, 0 ); c.set( GregorianCalendar.MILLISECOND, 0 ); final long time = c.getTime().getTime(); return new Date( time + r.nextInt( 24 * 60 * 60 * 1000 ) ); } private Number generateRandomNumber( final Number value ) throws BeanException { final double v = value.doubleValue(); final NumberFormat fmt; if ( v == value.longValue() ) { fmt = NumberFormat.getIntegerInstance( Locale.US ); } else { fmt = NumberFormat.getNumberInstance( Locale.US ); } final String format = fmt.format( value ); final StringBuilder b = new StringBuilder( format ); for ( int i = 0; i < b.length(); i += 1 ) { final char c = b.charAt( i ); if ( Character.isDigit( c ) ) { b.setCharAt( i, (char) ( r.nextInt( '9' - '0' ) + '0' ) ); } else { b.setCharAt( i, c ); } } try { return fmt.parse( b.toString() ); } catch ( ParseException e ) { throw new BeanException( "Failed to parse text", e ); } } private Object generateRandomText( final String s ) { final StringBuilder b = new StringBuilder( s ); for ( int i = 0; i < s.length(); i += 1 ) { final char c = s.charAt( i ); if ( Character.isWhitespace( c ) ) { b.setCharAt( i, ' ' ); } else if ( Character.isUpperCase( c ) ) { b.setCharAt( i, (char) ( r.nextInt( 'Z' - 'A' ) + 'A' ) ); } else if ( Character.isLetter( c ) ) { b.setCharAt( i, (char) ( r.nextInt( 'z' - 'a' ) + 'a' ) ); } else if ( Character.isDigit( c ) ) { b.setCharAt( i, (char) ( r.nextInt( '9' - '0' ) + '0' ) ); } else { b.setCharAt( i, c ); } } return b.toString(); } }