/* * Licensed to "Neo Technology," Network Engine for Objects in Lund AB * (http://neotechnology.com) under one or more contributor license agreements. * See the NOTICE file distributed with this work for additional information * regarding copyright ownership. Neo Technology licenses this file to you 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.neo4j.neoclipse.property; import java.io.IOException; import java.io.StreamTokenizer; import java.io.StringReader; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.jface.dialogs.IInputValidator; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.CellEditor; import org.eclipse.jface.viewers.ICellEditorValidator; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Composite; import org.neo4j.neoclipse.Icons; /** * Transform between property values and representations for editors. * * @author Anders Nawroth */ public final class PropertyTransform { /** * Transform between editor representation and property value. */ public static abstract class PropertyHandler { private final Class<?> type; private final Icons icon; private final Object standard; private Validator validator = null; private final boolean isArray; private PropertyHandler( final Class<?> type, final Icons icon, final Object standard, final boolean array ) { this.type = type; this.icon = icon; this.standard = standard; this.isArray = array; } /** * Transform from editor representation to property value. * * @param o editor representation (mostly a String) * @return property value or null * @throws IOException */ public Object parse( final Object o ) throws IOException { if ( isType( o.getClass() ) ) { return o; } return parser( o ); } /** * Compare type to the type of this handler. */ public boolean isType( final Class<?> t ) { return t.equals( type ); } /** * Tell if type is array type. * * @return true if type is array type */ public boolean isArray() { return isArray; } protected abstract Object parser( Object o ) throws IOException; /** * Transform from property value to editor representation. * * @param o property value * @return editor representation of the value */ public String render( final Object o ) { return type.cast( o ).toString(); } /** * Get the icon image for this type. * * @return */ public ImageDescriptor descriptor() { return icon.descriptor(); } /** * Get the icon image for this type. * * @return */ public Image image() { return icon.image(); } /** * Get default value. * * @return default value */ public Object value() { return standard; } /** * Get simple name of the class. * * @return */ public String name() { return type.getSimpleName(); } private class Validator implements ICellEditorValidator, IInputValidator { /** * cell editor validator */ public String isValid( final Object value ) { try { parse( value ); } catch ( Exception e ) { return "Could not parse the input as type " + name() + "."; } return null; } /** * dialog field validator */ public String isValid( final String value ) { return isValid( (Object) value ); } } /** * get an input field validator * * @return */ public IInputValidator getValidator() { return getInternalValidator(); } /** * get a cell editor validator * * @return */ private ICellEditorValidator getCellValidator() { return getInternalValidator(); } /** * Get the real internal validator. * * @return */ private Validator getInternalValidator() { if ( validator == null ) { validator = new Validator(); } return validator; } /** * Editor for this property type. * * @return editor */ public CellEditor getEditor( final Composite parent ) { CellEditor editor = getEditorType().getEditor( parent, this ); editor.setValidator( getCellValidator() ); return editor; } /** * Gets editor type for this property type. Override if another editor * than TEXT should be used. * * @return editor type */ protected PropertyEditor getEditorType() { return PropertyEditor.TEXT; } } /** * Prevent instantiation. */ private PropertyTransform() { // no instances } /** * Transform a String to a List of Strings. * * @param input editor data for array * @return list containing the separated strings */ protected static List<String> arrayToCollection( final Object input ) { String in = removeBrackets( input ); List<String> out = new ArrayList<String>(); for ( String item : in.split( "," ) ) { item = item.trim(); if ( item.length() > 0 ) { out.add( item ); } } return out; } /** * Transform a String to a list of Strings that are used as Strings in the * property value. Adds a little more features like being able to have * spaces inside a string. Citation signs (") are escaped by a backslash * (\). * * @param input editor data for String array * @return list containing the separated strings * @throws IOException */ protected static List<String> stringArrayToCollection( final Object input ) throws IOException { String in = removeBrackets( input ); List<String> out = new ArrayList<String>(); StreamTokenizer tokenizer = new StreamTokenizer( new StringReader( in ) ); tokenizer.wordChars( '0', '9' ); while ( tokenizer.nextToken() != StreamTokenizer.TT_EOF ) { if ( tokenizer.sval != null ) { out.add( tokenizer.sval ); } } return out; } /** * Simple utility to remove [brackets] used as markers for an array. * * @param input user data * @return user data minus brackets and leading/trailing whitespace */ private static String removeBrackets( final Object input ) { String in = input.toString().trim(); if ( in.charAt( 0 ) == '[' ) { in = in.substring( 1 ); } if ( in.charAt( in.length() - 1 ) == ']' ) { in = in.substring( 0, in.length() - 1 ); } return in; } /** * Get a PropertyHandler for a specific type. * * @param cls type to handle * @return handler for type */ public static PropertyHandler getHandler( final Class<?> cls ) { return HANDLERS.get( cls ); } /** * Get a PropertyHandler for an object. * * @param o object to handle * @return handler for type */ public static PropertyHandler getHandler( final Object o ) { return HANDLERS.get( o.getClass() ); } /** * Render a property value. * * @param o the value * @return rendition of value */ public static String render( final Object o ) { return HANDLERS.get( o.getClass() ).render( o ); } /** * A Map from property type classes to property handlers. Use get() on the * map to retrieve the correct property handler with parse(), render() and * getEditorType() methods. */ private static final Map<Class<?>, PropertyHandler> HANDLERS = new HashMap<Class<?>, PropertyHandler>() { private static final long serialVersionUID = 1L; { put( String.class, new PropertyHandler( String.class, Icons.TYPE_STRING, "", false ) { @Override protected Object parser( final Object o ) { return o; } } ); put( String[].class, new PropertyHandler( String[].class, Icons.TYPE_STRINGS, new String[0], true ) { @Override protected Object parser( final Object o ) throws IOException { List<String> items = stringArrayToCollection( o ); String[] res = new String[items.size()]; for ( int i = 0; i < res.length; i++ ) { res[i] = items.get( i ); } return res; } @Override public String render( final Object o ) { String[] res = new String[( (String[]) o ).length]; for ( int i = 0; i < res.length; i++ ) { String s = ( (String[]) o )[i]; s = s.replaceAll( "\"", "\\\\\"" ); res[i] = '"' + s + '"'; } return Arrays.toString( res ); } } ); put( Integer.class, new PropertyHandler( Integer.class, Icons.TYPE_INT, 0, false ) { @Override protected Object parser( final Object o ) { return Integer.parseInt( (String) o ); } } ); put( int[].class, new PropertyHandler( int[].class, Icons.TYPE_INTS, new int[0], true ) { @Override protected Object parser( final Object o ) { List<String> items = arrayToCollection( o ); int[] res = new int[items.size()]; for ( int i = 0; i < res.length; i++ ) { res[i] = Integer.parseInt( items.get( i ) ); } return res; } @Override public String render( final Object o ) { return Arrays.toString( (int[]) o ); } } ); put( Double.class, new PropertyHandler( Double.class, Icons.TYPE_DOUBLE, 0d, false ) { @Override protected Object parser( final Object o ) { return Double.parseDouble( (String) o ); } } ); put( double[].class, new PropertyHandler( double[].class, Icons.TYPE_DOUBLES, new double[0], true ) { @Override protected Object parser( final Object o ) { List<String> items = arrayToCollection( o ); double[] res = new double[items.size()]; for ( int i = 0; i < res.length; i++ ) { res[i] = Double.parseDouble( items.get( i ) ); } return res; } @Override public String render( final Object o ) { return Arrays.toString( (double[]) o ); } } ); put( Float.class, new PropertyHandler( Float.class, Icons.TYPE_FLOAT, 0f, false ) { @Override protected Object parser( final Object o ) { return Float.parseFloat( (String) o ); } } ); put( float[].class, new PropertyHandler( float[].class, Icons.TYPE_FLOATS, new float[0], true ) { @Override protected Object parser( final Object o ) { List<String> items = arrayToCollection( o ); float[] res = new float[items.size()]; for ( int i = 0; i < res.length; i++ ) { res[i] = Float.parseFloat( items.get( i ) ); } return res; } @Override public String render( final Object o ) { return Arrays.toString( (float[]) o ); } } ); put( Boolean.class, new PropertyHandler( Boolean.class, Icons.TYPE_BOOLEAN, false, false ) { // has it's dedicated editor, handling transforms, // so we just pass things through here @Override protected Object parser( final Object o ) { return Boolean.parseBoolean( (String) o ); } } ); put( boolean[].class, new PropertyHandler( boolean[].class, Icons.TYPE_BOOLEANS, new boolean[0], true ) { @Override protected Object parser( final Object o ) { List<String> items = arrayToCollection( o ); boolean[] res = new boolean[items.size()]; for ( int i = 0; i < res.length; i++ ) { res[i] = Boolean.parseBoolean( items.get( i ) ); } return res; } @Override public String render( final Object o ) { return Arrays.toString( (boolean[]) o ); } } ); put( Byte.class, new PropertyHandler( Byte.class, Icons.TYPE_BYTE, (byte) 0, false ) { @Override protected Object parser( final Object o ) { return Byte.parseByte( (String) o ); } } ); put( byte[].class, new PropertyHandler( byte[].class, Icons.TYPE_BYTES, new byte[0], true ) { @Override protected Object parser( final Object o ) { List<String> items = arrayToCollection( o ); byte[] res = new byte[items.size()]; for ( int i = 0; i < res.length; i++ ) { res[i] = Byte.parseByte( items.get( i ) ); } return res; } @Override public String render( final Object o ) { return Arrays.toString( (byte[]) o ); } } ); put( Short.class, new PropertyHandler( Short.class, Icons.TYPE_SHORT, (short) 0, false ) { @Override protected Object parser( final Object o ) { return Short.parseShort( (String) o ); } } ); put( short[].class, new PropertyHandler( short[].class, Icons.TYPE_SHORTS, new short[0], true ) { @Override protected Object parser( final Object o ) { List<String> items = arrayToCollection( o ); short[] res = new short[items.size()]; for ( int i = 0; i < res.length; i++ ) { res[i] = Short.parseShort( items.get( i ) ); } return res; } @Override public String render( final Object o ) { return Arrays.toString( (short[]) o ); } } ); put( Long.class, new PropertyHandler( Long.class, Icons.TYPE_LONG, 0L, false ) { @Override protected Object parser( final Object o ) { return Long.parseLong( (String) o ); } } ); put( long[].class, new PropertyHandler( long[].class, Icons.TYPE_LONGS, new long[0], true ) { @Override protected Object parser( final Object o ) { List<String> items = arrayToCollection( o ); long[] res = new long[items.size()]; for ( int i = 0; i < res.length; i++ ) { res[i] = Long.parseLong( items.get( i ) ); } return res; } @Override public String render( final Object o ) { return Arrays.toString( (long[]) o ); } } ); put( Character.class, new PropertyHandler( Character.class, Icons.TYPE_CHAR, (char) 0, false ) { @Override protected Object parser( final Object o ) { String s = (String) o; if ( s.length() > 0 ) { return ( (String) o ).charAt( 0 ); } return null; } } ); put( char[].class, new PropertyHandler( char[].class, Icons.TYPE_CHARS, new char[0], true ) { @Override protected Object parser( final Object o ) { List<String> items = arrayToCollection( o ); char[] res = new char[items.size()]; for ( int i = 0; i < res.length; i++ ) { res[i] = items.get( i ).charAt( 0 ); } return res; } @Override public String render( final Object o ) { return Arrays.toString( (char[]) o ); } } ); } }; }