package controlP5; /** * controlP5 is a processing gui library. * * 2006-2015 by Andreas Schlegel * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 * of the License, or (at your option) any later version. * This library 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. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA * * @author Andreas Schlegel (http://www.sojamo.de) * @modified ##date## * @version ##version## * */ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.StringReader; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.logging.Logger; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import processing.core.PApplet; import processing.data.JSONArray; import processing.data.JSONObject; /** * Values of controllers can be stored inside properties files which can be saved to file or memory. * * @example controllers/ControlP5properties */ public class ControllerProperties { public final static int OPEN = 0; public final static int CLOSE = 1; public static String defaultName = "controlP5"; PropertiesStorageFormat format; /** * all ControllerProperties will be stored inside Map allProperties. ControllerProperties need to be unique or will * otherwise be overwritten. * * A hashSet containing names of PropertiesSets is assigned to each ControllerProperty. HashSets are used instead of * ArrayList to only allow unique elements. */ private Map< ControllerProperty , HashSet< String >> allProperties; /** * A set of unique property-set names. */ private Set< String > allSets; final ControlP5 controlP5; private String _myDefaultSetName = "default"; public static final Logger logger = Logger.getLogger( ControllerProperties.class.getName( ) ); private Map< String , Set< ControllerProperty >> _mySnapshots; public ControllerProperties( ControlP5 theControlP5 ) { controlP5 = theControlP5; // setFormat( new SerializedFormat( ) ); setFormat( new JSONFormat( ) ); allProperties = new HashMap< ControllerProperty , HashSet< String >>( ); allSets = new HashSet< String >( ); addSet( _myDefaultSetName ); _mySnapshots = new LinkedHashMap< String , Set< ControllerProperty >>( ); } public Map< ControllerProperty , HashSet< String >> get( ) { return allProperties; } /** * adds a property based on names of setter and getter methods of a controller. * * @param thePropertySetter * @param thePropertyGetter */ public ControllerProperty register( ControllerInterface< ? > theController , String thePropertySetter , String thePropertyGetter ) { ControllerProperty p = new ControllerProperty( theController , thePropertySetter , thePropertyGetter ); if ( !allProperties.containsKey( p ) ) { // register a new property with the main properties container allProperties.put( p , new HashSet< String >( ) ); // register the property wit the default properties set allProperties.get( p ).add( _myDefaultSetName ); } return p; } /** * registering a property with only one parameter assumes that there is a setter and getter function present for the * Controller. register("value") for example would create a property reference to setValue and getValue. Notice that * the first letter of value is being capitalized. * * @param theProperty * @return */ public ControllerProperties register( ControllerInterface< ? > theController , String theProperty ) { theProperty = Character.toUpperCase( theProperty.charAt( 0 ) ) + theProperty.substring( 1 ); register( theController , "set" + theProperty , "get" + theProperty ); return this; } public ControllerProperties remove( ControllerInterface< ? > theController , String theSetter , String theGetter ) { ControllerProperty cp = new ControllerProperty( theController , theSetter , theGetter ); allProperties.remove( cp ); return this; } public ControllerProperties remove( ControllerInterface< ? > theController ) { ArrayList< ControllerProperty > list = new ArrayList< ControllerProperty >( allProperties.keySet( ) ); for ( ControllerProperty cp : list ) { if ( cp.getController( ).equals( theController ) ) { allProperties.remove( cp ); } } return this; } public ControllerProperties remove( ControllerInterface< ? > theController , String theProperty ) { return remove( theController , "set" + theProperty , "get" + theProperty ); } public List< ControllerProperty > get( ControllerInterface< ? > theController ) { List< ControllerProperty > props = new ArrayList< ControllerProperty >( ); List< ControllerProperty > list = new ArrayList< ControllerProperty >( allProperties.keySet( ) ); for ( ControllerProperty cp : list ) { if ( cp.getController( ).equals( theController ) ) { props.add( cp ); } } return props; } public ControllerProperty getProperty( ControllerInterface< ? > theController , String theSetter , String theGetter ) { ControllerProperty cp = new ControllerProperty( theController , theSetter , theGetter ); Iterator< ControllerProperty > iter = allProperties.keySet( ).iterator( ); while ( iter.hasNext( ) ) { ControllerProperty p = iter.next( ); if ( p.equals( cp ) ) { return p; } } // in case the property has not been registered before, it will be // registered here automatically - you don't need to call // Controller.registerProperty() but can use Controller.getProperty() // instead. return register( theController , theSetter , theGetter ); } public ControllerProperty getProperty( ControllerInterface< ? > theController , String theProperty ) { theProperty = Character.toUpperCase( theProperty.charAt( 0 ) ) + theProperty.substring( 1 ); return getProperty( theController , "set" + theProperty , "get" + theProperty ); } public HashSet< ControllerProperty > getPropertySet( ControllerInterface< ? > theController ) { HashSet< ControllerProperty > set = new HashSet< ControllerProperty >( ); Iterator< ControllerProperty > iter = allProperties.keySet( ).iterator( ); while ( iter.hasNext( ) ) { ControllerProperty p = iter.next( ); if ( p.getController( ).equals( theController ) ) { set.add( p ); } } return set; } public ControllerProperties addSet( String theSet ) { allSets.add( theSet ); return this; } /** * Moves a ControllerProperty from one set to another. */ public ControllerProperties move( ControllerProperty theProperty , String fromSet , String toSet ) { if ( !exists( theProperty ) ) { return this; } if ( allProperties.containsKey( theProperty ) ) { if ( allProperties.get( theProperty ).contains( fromSet ) ) { allProperties.get( theProperty ).remove( fromSet ); } addSet( toSet ); allProperties.get( theProperty ).add( toSet ); } return this; } public ControllerProperties move( ControllerInterface< ? > theController , String fromSet , String toSet ) { HashSet< ControllerProperty > set = getPropertySet( theController ); for ( ControllerProperty cp : set ) { move( cp , fromSet , toSet ); } return this; } /** * copies a ControllerProperty from one set to other(s); */ public ControllerProperties copy( ControllerProperty theProperty , String ... theSet ) { if ( !exists( theProperty ) ) { return this; } for ( String s : theSet ) { allProperties.get( theProperty ).add( s ); if ( !allSets.contains( s ) ) { addSet( s ); } } return this; } public ControllerProperties copy( ControllerInterface< ? > theController , String ... theSet ) { HashSet< ControllerProperty > set = getPropertySet( theController ); for ( ControllerProperty cp : set ) { copy( cp , theSet ); } return this; } /** * removes a ControllerProperty from one or multiple sets. */ public ControllerProperties remove( ControllerProperty theProperty , String ... theSet ) { if ( !exists( theProperty ) ) { return this; } for ( String s : theSet ) { allProperties.get( theProperty ).remove( s ); } return this; } public ControllerProperties remove( ControllerInterface< ? > theController , String ... theSet ) { HashSet< ControllerProperty > set = getPropertySet( theController ); for ( ControllerProperty cp : set ) { remove( cp , theSet ); } return this; } /** * stores a ControllerProperty in one particular set only. */ public ControllerProperties only( ControllerProperty theProperty , String theSet ) { // clear all the set-references for a particular property allProperties.get( theProperty ).clear( ); // add theSet to the empty collection of sets for this particular // property allProperties.get( theProperty ).add( theSet ); return this; } ControllerProperties only( ControllerInterface< ? > theController , String ... theSet ) { return this; } private boolean exists( ControllerProperty theProperty ) { return allProperties.containsKey( theProperty ); } public ControllerProperties print( ) { for ( Entry< ControllerProperty , HashSet< String >> entry : allProperties.entrySet( ) ) { System.out.println( entry.getKey( ) + "\t" + entry.getValue( ) ); } return this; } /** * deletes a ControllerProperty from all Sets including the default set. */ public ControllerProperties delete( ControllerProperty theProperty ) { if ( !exists( theProperty ) ) { return this; } allProperties.remove( theProperty ); return this; } private boolean updatePropertyValue( ControllerProperty theProperty ) { Method method; try { method = theProperty.getController( ).getClass( ).getMethod( theProperty.getGetter( ) ); Object value = method.invoke( theProperty.getController( ) ); theProperty.setType( method.getReturnType( ) ); theProperty.setValue( value ); if ( checkSerializable( value ) ) { return true; } } catch ( Exception e ) { logger.severe( "" + e ); } return false; } private boolean checkSerializable( Object theProperty ) { try { ByteArrayOutputStream out = new ByteArrayOutputStream( ); ObjectOutputStream stream = new ObjectOutputStream( out ); stream.writeObject( theProperty ); stream.close( ); return true; } catch ( Exception e ) { return false; } } /** * logs all registered properties in memory. Here, clones of properties are stored inside a map and can be accessed * by key using the getLog method. * * @see controlP5.ControllerProperties#getSnapshot(String) * @param theKey * @return ControllerProperties */ public ControllerProperties setSnapshot( String theKey ) { Set< ControllerProperty > l = new HashSet< ControllerProperty >( ); for ( ControllerProperty cp : allProperties.keySet( ) ) { updatePropertyValue( cp ); try { l.add( ( ControllerProperty ) cp.clone( ) ); } catch ( CloneNotSupportedException e ) { // TODO Auto-generated catch block } } _mySnapshots.put( theKey , l ); return this; } /** * convenience method, setSnapshot(String) also works here since it will override existing log with the same key. */ public ControllerProperties updateSnapshot( String theKey ) { return setSnapshot( theKey ); } /** * removes a snapshot by key. */ public ControllerProperties removeSnapshot( String theKey ) { _mySnapshots.remove( theKey ); return this; } ControllerProperties setSnapshot( String theKey , String ... theSets ) { return this; } /** * saves a snapshot into your sketch's sketch folder. */ public ControllerProperties saveSnapshot( String theKey ) { saveSnapshotAs( controlP5.papplet.sketchPath( theKey ) , theKey ); return this; } /** * saves a snapshot to the file with path given by the first parameter (thePropertiesPath). */ public ControllerProperties saveSnapshotAs( String thePropertiesPath , String theKey ) { Set< ControllerProperty > log = _mySnapshots.get( theKey ); if ( log == null ) { return this; } thePropertiesPath = getPathWithExtension( format , controlP5.checkPropertiesPath( thePropertiesPath ) ); format.compile( log , thePropertiesPath ); return this; } private String getPathWithExtension( PropertiesStorageFormat theFormat , String thePropertiesPath ) { return ( thePropertiesPath.endsWith( "." + theFormat.getExtension( ) ) ) ? thePropertiesPath : thePropertiesPath + "." + theFormat.getExtension( ); } /** * restores properties previously stored as snapshot in memory. * * @see controlP5.ControllerProperties#setSnapshot(String) */ public ControllerProperties getSnapshot( String theKey ) { Set< ControllerProperty > l = _mySnapshots.get( theKey ); if ( l != null ) { for ( ControllerProperty cp : l ) { ControllerInterface< ? > ci = controlP5.getController( cp.getAddress( ) ); ci = ( ci == null ) ? controlP5.getGroup( cp.getAddress( ) ) : ci; ControlP5.invoke( ( Controller ) ci , cp.getSetter( ) , cp.getValue( ) ); } } return this; } /** * properties stored in memory can be accessed by index, getSnapshotIndices() returns the index of the snapshot * list. */ public ArrayList< String > getSnapshotIndices( ) { return new ArrayList< String >( _mySnapshots.keySet( ) ); } /** * load properties from the default properties file 'controlP5.properties' */ public boolean load( ) { return load( controlP5.papplet.sketchPath( defaultName + "." + format.getExtension( ) ) ); } public boolean load( String thePropertiesPath ) { return format.load( getPathWithExtension( format , controlP5.checkPropertiesPath( thePropertiesPath ) ) ); } /** * use ControllerProperties.SERIALIZED, ControllerProperties.XML or ControllerProperties.JSON as parameter. */ public void setFormat( PropertiesStorageFormat theFormat ) { format = theFormat; } public void setFormat( String theFormat ) { if ( theFormat.equals( ControlP5.JSON ) ) { setFormat( new JSONFormat( ) ); } else if ( theFormat.equals( ControlP5.SERIALIZED ) ) { setFormat( new SerializedFormat( ) ); } else { System.out.println( "sorry format " + theFormat + " does not exist." ); } } /** * saves all registered properties into the default 'controlP5.properties' file into your sketch folder. */ public boolean save( ) { System.out.println( "save properties using format " + format + " (" + format.getExtension( ) + ") " + controlP5.papplet.sketchPath( defaultName ) ); format.compile( allProperties.keySet( ) , getPathWithExtension( format , controlP5.papplet.sketchPath( defaultName ) ) ); return true; } /** * saves all registered properties into a file specified by parameter thePropertiesPath. */ public boolean saveAs( final String thePropertiesPath ) { format.compile( allProperties.keySet( ) , getPathWithExtension( format , controlP5.checkPropertiesPath( thePropertiesPath ) ) ); return true; } /** * saves a list of properties sets into a file specified by parameter thePropertiesPath. */ public boolean saveAs( String thePropertiesPath , String ... theSets ) { thePropertiesPath = controlP5.checkPropertiesPath( thePropertiesPath ); HashSet< ControllerProperty > sets = new HashSet< ControllerProperty >( ); Iterator< ControllerProperty > iter = allProperties.keySet( ).iterator( ); while ( iter.hasNext( ) ) { ControllerProperty p = iter.next( ); if ( allProperties.containsKey( p ) ) { HashSet< String > set = allProperties.get( p ); for ( String str : set ) { for ( String s : theSets ) { if ( str.equals( s ) ) { sets.add( p ); } } } } } format.compile( sets , getPathWithExtension( format , thePropertiesPath ) ); return true; } /** * @exclude {@inheritDoc} */ public String toString( ) { String s = ""; s += this.getClass( ).getName( ) + "\n"; s += "total num of properties:\t" + allProperties.size( ) + "\n"; for ( ControllerProperty c : allProperties.keySet( ) ) { s += "\t" + c + "\n"; } s += "total num of sets:\t\t" + allSets.size( ) + "\n"; for ( String set : allSets ) { s += "\t" + set + "\n"; } return s; } interface PropertiesStorageFormat { public void compile( Set< ControllerProperty > theProperties , String thePropertiesPath ); public boolean load( String thePropertiesPath ); public String getExtension( ); } class XMLFormat implements PropertiesStorageFormat { public void compile( Set< ControllerProperty > theProperties , String thePropertiesPath ) { System.out.println( "Dont use the XMLFormat yet, it is not fully implemented with 0.5.9, use SERIALIZED instead." ); System.out.println( "Compiling with XMLFormat" ); StringBuffer xml = new StringBuffer( ); xml.append( "\n" ); xml.append( "\n" ); for ( ControllerProperty cp : theProperties ) { if ( cp.isActive( ) ) { updatePropertyValue( cp ); xml.append( getXML( cp ) ); } } xml.append( "" ); controlP5.papplet.saveStrings( thePropertiesPath , PApplet.split( xml.toString( ) , "\n" ) ); System.out.println( "saving xml, " + thePropertiesPath ); } public String getExtension( ) { return "xml"; } public boolean load( String thePropertiesPath ) { String s; try { s = PApplet.join( controlP5.papplet.loadStrings( thePropertiesPath ) , "\n" ); } catch ( Exception e ) { logger.warning( thePropertiesPath + ", file not found." ); return false; } System.out.println( "loading xml \n" + s ); try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance( ); DocumentBuilder db = dbf.newDocumentBuilder( ); InputSource is = new InputSource( ); is.setCharacterStream( new StringReader( s ) ); Document doc = db.parse( is ); doc.getDocumentElement( ).normalize( ); NodeList nodeLst = doc.getElementsByTagName( "property" ); for ( int i = 0 ; i < nodeLst.getLength( ) ; i++ ) { Node node = nodeLst.item( i ); if ( node.getNodeType( ) == Node.ELEMENT_NODE ) { Element fstElmnt = ( Element ) node; String myAddress = getElement( fstElmnt , "address" ); String mySetter = getElement( fstElmnt , "setter" ); String myType = getElement( fstElmnt , "type" ); String myValue = getElement( fstElmnt , "value" ); // String myClass = getElement(fstElmnt, "class"); // String myGetter = getElement(fstElmnt, "getter"); try { System.out.print( "setting controller " + myAddress + " " ); ControllerInterface< ? > ci = controlP5.getController( myAddress ); ci = ( ci == null ) ? controlP5.getGroup( myAddress ) : ci; System.out.println( ci ); Method method; try { Class< ? > c = getClass( myType ); System.out.println( myType + " / " + c ); method = ci.getClass( ).getMethod( mySetter , new Class[] { c } ); method.setAccessible( true ); method.invoke( ci , new Object[] { getValue( myValue , myType , c ) } ); } catch ( Exception e ) { logger.severe( e.toString( ) ); } } catch ( Exception e ) { logger.warning( "skipping a property, " + e ); } } } } catch ( SAXException e ) { logger.warning( "SAXException, " + e ); return false; } catch ( IOException e ) { logger.warning( "IOException, " + e ); return false; } catch ( ParserConfigurationException e ) { logger.warning( "ParserConfigurationException, " + e ); return false; } return true; } private Object getValue( String theValue , String theType , Class< ? > theClass ) { if ( theClass == int.class ) { return Integer.parseInt( theValue ); } else if ( theClass == float.class ) { return Float.parseFloat( theValue ); } else if ( theClass == boolean.class ) { return Boolean.parseBoolean( theValue ); } else if ( theClass.isArray( ) ) { System.out.println( "this is an array: " + theType + ", " + theValue + ", " + theClass ); int dim = 0; while ( true ) { if ( theType.charAt( dim ) != '[' || dim >= theType.length( ) ) { break; } dim++; } } else { System.out.println( "is array? " + theClass.isArray( ) ); } return theValue; } private Class< ? > getClass( String theType ) { if ( theType.equals( "int" ) ) { return int.class; } else if ( theType.equals( "float" ) ) { return float.class; } else if ( theType.equals( "String" ) ) { return String.class; } try { return Class.forName( theType ); } catch ( ClassNotFoundException e ) { logger.warning( "ClassNotFoundException, " + e ); } return null; } private String getElement( Element theElement , String theName ) { NodeList fstNmElmntLst = theElement.getElementsByTagName( theName ); Element fstNmElmnt = ( Element ) fstNmElmntLst.item( 0 ); NodeList fstNm = fstNmElmnt.getChildNodes( ); return ( ( Node ) fstNm.item( 0 ) ).getNodeValue( ); } public String getXML( ControllerProperty theProperty ) { // Mapping Between JSON and Java Entities // http://code.google.com/p/json-simple/wiki/MappingBetweenJSONAndJavaEntities String s = "\t\n"; s += "\t\t
" + theProperty.getAddress( ) + "
\n"; s += "\t\t" + CP.formatGetClass( theProperty.getController( ).getClass( ) ) + "\n"; s += "\t\t" + theProperty.getSetter( ) + "\n"; s += "\t\t" + theProperty.getGetter( ) + "\n"; s += "\t\t" + CP.formatGetClass( theProperty.getType( ) ) + "\n"; s += "\t\t" + cdata( OPEN , theProperty.getValue( ).getClass( ) ) + ( theProperty.getValue( ).getClass( ).isArray( ) ? CP.arrayToString( theProperty.getValue( ) ) : theProperty.getValue( ) ) + cdata( CLOSE , theProperty.getValue( ).getClass( ) ) + "\n"; s += "\t
\n"; return s; } private String cdata( int a , Class< ? > c ) { if ( c == String.class || c.isArray( ) ) { return ( a == OPEN ? "" ); } return ""; } } public class JSONFormat implements PropertiesStorageFormat { public void compile( Set< ControllerProperty > theProperties , String thePropertiesPath ) { long t = System.currentTimeMillis( ); JSONObject json = new JSONObject( ); for ( ControllerProperty cp : theProperties ) { if ( cp.isActive( ) ) { if ( updatePropertyValue( cp ) ) { cp.setId( cp.getController( ).getId( ) ); if ( !json.keys( ).contains( cp.getAddress( ) ) ) { json.setJSONObject( cp.getAddress( ) , new JSONObject( ) ); } JSONObject item = json.getJSONObject( cp.getAddress( ) ); String key = cp.getSetter( ); key = Character.toLowerCase( key.charAt( 3 ) ) + key.substring( 4 ); if ( cp.getValue( ) instanceof Number ) { if ( cp.getValue( ) instanceof Integer ) { item.setInt( key , ControlP5.i( cp.getValue( ) ) ); } else if ( cp.getValue( ) instanceof Float ) { item.setFloat( key , ControlP5.f( cp.getValue( ) ) ); } else if ( cp.getValue( ) instanceof Double ) { item.setDouble( key , ControlP5.d( cp.getValue( ) ) ); } } else if ( cp.getValue( ) instanceof Boolean ) { item.setBoolean( key , ControlP5.b( cp.getValue( ) ) ); } else { if ( cp.getValue( ).getClass( ).isArray( ) ) { JSONArray arr = new JSONArray( ); if ( cp.getValue( ) instanceof int[] ) { for ( Object o : ( int[] ) cp.getValue( ) ) { arr.append( ControlP5.i( o ) ); } } else if ( cp.getValue( ) instanceof float[] ) { for ( Object o : ( float[] ) cp.getValue( ) ) { arr.append( ControlP5.f( o ) ); } } item.setJSONArray( key , arr ); } else { item.setString( key , cp.getValue( ).toString( ) ); } } } } } json.save( new File( getPathWithExtension( this , thePropertiesPath ) ) , "" ); } public String getExtension( ) { return "json"; } public boolean load( String thePropertiesPath ) { JSONReader reader = new JSONReader( controlP5.papplet ); Map< ? , ? > entries = ControlP5.toMap( reader.parse( thePropertiesPath ) ); for ( Map.Entry entry : entries.entrySet( ) ) { String name = entry.getKey( ).toString( ); Controller c = controlP5.getController( name ); Map< ? , ? > values = ControlP5.toMap( entry.getValue( ) ); for ( Map.Entry value : values.entrySet( ) ) { String i0 = value.getKey( ).toString( ); String member = "set" + Character.toUpperCase( i0.charAt( 0 ) ) + i0.substring( 1 ); Object i1 = value.getValue( ); if ( i1 instanceof Number ) { ControlP5.invoke( c , member , ControlP5.f( value.getValue( ) ) ); } else if ( i1 instanceof String ) { ControlP5.invoke( c , member , ControlP5.s( value.getValue( ) ) ); } else if ( i1 instanceof float[] ) { ControlP5.invoke( c , member , (float[])i1 ); } else { if ( i1 instanceof List ) { List l = ( List ) i1; float[] arr = new float[ l.size( ) ]; for ( int i = 0 ; i < l.size( ) ; i++ ) { arr[ i ] = ControlP5.f( l.get( i ) ); } ControlP5.invoke( c , member , arr ); } else { ControlP5.invoke( c , member , value.getValue( ) ); } } } } return false; } } private class JSONReader { private final PApplet papplet; public JSONReader( Object o ) { if ( o instanceof PApplet ) { papplet = ( PApplet ) o; } else { papplet = null; System.out.println( "Sorry, argument is not of instance PApplet" ); } } public Object parse( String s ) { if ( s.indexOf( "{" ) >= 0 ) { return get( JSONObject.parse( s ) , new LinkedHashMap( ) ); } else { return get( papplet.loadJSONObject( s ) , new LinkedHashMap( ) ); } } Object get( Object o , Object m ) { if ( o instanceof JSONObject ) { if ( m instanceof Map ) { Set set = ( ( JSONObject ) o ).keys( ); for ( Object o1 : set ) { Object o2 = ControlP5.invoke( o , "opt" , o1.toString( ) ); if ( o2 instanceof JSONObject ) { Map m1 = new LinkedHashMap( ); ( ( Map ) m ).put( o1.toString( ) , m1 ); get( o2 , m1 ); } else if ( o2 instanceof JSONArray ) { List l1 = new ArrayList( ); ( ( Map ) m ).put( o1.toString( ) , l1 ); get( o2 , l1 ); } else { ( ( Map ) m ).put( o1.toString( ) , o2 ); } } } } else if ( o instanceof JSONArray ) { if ( m instanceof List ) { List l = ( ( List ) m ); int n = 0; Object o3 = ControlP5.invoke( o , "opt" , n ); while ( o3 != null ) { if ( o3 instanceof JSONArray ) { List l1 = new ArrayList( ); l.add( l1 ); get( o3 , l1 ); } else if ( o3 instanceof JSONObject ) { Map l1 = new LinkedHashMap( ); l.add( l1 ); get( o3 , l1 ); } else { l.add( o3 ); } o3 = ControlP5.invoke( o , "opt" , ++n ); } } else { System.err.println( "JSONReader type mismatch." ); } } return m; } } public class SerializedFormat implements PropertiesStorageFormat { public boolean load( String thePropertiesPath ) { try { FileInputStream fis = new FileInputStream( thePropertiesPath ); ObjectInputStream ois = new ObjectInputStream( fis ); int size = ois.readInt( ); logger.info( "loading " + size + " property-items. " ); for ( int i = 0 ; i < size ; i++ ) { try { ControllerProperty cp = ( ControllerProperty ) ois.readObject( ); ControllerInterface< ? > ci = controlP5.getController( cp.getAddress( ) ); ci = ( ci == null ) ? controlP5.getGroup( cp.getAddress( ) ) : ci; ci.setId( cp.getId( ) ); Method method; try { method = ci.getClass( ).getMethod( cp.getSetter( ) , new Class[] { cp.getType( ) } ); method.setAccessible( true ); method.invoke( ci , new Object[] { cp.getValue( ) } ); } catch ( Exception e ) { logger.severe( e.toString( ) ); } } catch ( Exception e ) { logger.warning( "skipping a property, " + e ); } } ois.close( ); } catch ( Exception e ) { logger.warning( "Exception during deserialization: " + e ); return false; } return true; } public String getExtension( ) { return "ser"; } public void compile( Set< ControllerProperty > theProperties , String thePropertiesPath ) { int active = 0; int total = 0; HashSet< ControllerProperty > propertiesToBeSaved = new HashSet< ControllerProperty >( ); for ( ControllerProperty cp : theProperties ) { if ( cp.isActive( ) ) { if ( updatePropertyValue( cp ) ) { active++; cp.setId( cp.getController( ).getId( ) ); propertiesToBeSaved.add( cp ); } } total++; } int ignored = total - active; try { FileOutputStream fos = new FileOutputStream( thePropertiesPath ); ObjectOutputStream oos = new ObjectOutputStream( fos ); logger.info( "Saving property-items to " + thePropertiesPath ); oos.writeInt( active ); for ( ControllerProperty cp : propertiesToBeSaved ) { if ( cp.isActive( ) ) { oos.writeObject( cp ); } } logger.info( active + " items saved, " + ( ignored ) + " items ignored. Done saving properties." ); oos.flush( ); oos.close( ); fos.close( ); } catch ( Exception e ) { logger.warning( "Exception during serialization: " + e ); } } } }