controlp5/src/controlP5/ControlP5Base.java

795 lines
24 KiB
Java
Executable File

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.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import processing.core.PApplet;
import processing.event.Event;
import static controlP5.Controller.*;
/**
* The ControlP5Base supports the ControlP5 class and
* implements all adder methods to add controllers to
* controlP5.
*/
@SuppressWarnings( { "unchecked" , "rawtypes" } ) public class ControlP5Base extends ControlP5Legacy implements ControlP5Constants {
protected ControlP5 cp5;
protected ControllerProperties _myProperties;
private ControllerAutomator _myAutomator;
protected Map< Object , ArrayList< ControllerInterface< ? >>> _myObjectToControllerMap = new HashMap< Object , ArrayList< ControllerInterface< ? >>>( );
protected Map< String , FieldChangedListener > _myFieldChangedListenerMap = new HashMap< String , FieldChangedListener >( );
protected Map< KeyCode , List< ControlKey >> keymap = new HashMap< KeyCode , List< ControlKey >>( );
protected ControllerGroup< ? > currentGroupPointer;
protected boolean isCurrentGroupPointerClosed = true;
protected int autoDirection = HORIZONTAL;
public Tab getDefaultTab( ) {
return ( Tab ) cp5.controlWindow.getTabs( ).get( 1 );
}
protected void init( ControlP5 theControlP5 ) {
super.init( theControlP5 );
cp5 = theControlP5;
_myProperties = new ControllerProperties( cp5 );
_myAutomator = new ControllerAutomator( cp5 );
currentGroupPointer = cp5.controlWindow.getTab( "default" );
}
public ControllerLayout getLayout( ) {
return new ControllerLayout( cp5 );
}
public Tab addTab( String theName ) {
for ( int i = 0 ; i < cp5.getWindow( ).getTabs( ).size( ) ; i++ ) {
if ( cp5.getWindow( ).getTabs( ).get( i ).getName( ).equals( theName ) ) {
return ( Tab ) cp5.getWindow( ).getTabs( ).get( i );
}
}
Tab myTab = new Tab( cp5 , cp5.getWindow( ) , theName );
cp5.getWindow( ).getTabs( ).add( myTab );
return myTab;
}
/**
* A Bang triggers an event without passing a value.
*/
public Bang addBang( final String theName ) {
return addBang( null , theName );
}
public Bang addBang( final Object theObject , final String theName ) {
return addBang( theObject , theObject != null ? theObject.toString( ) : "" , theName );
}
/**
* Triggers an event and passing a value.
*/
public Button addButton( String theName ) {
return addButton( null , theName );
}
public Button addButton( final Object theObject , final String theName ) {
return addButton( theObject , theObject != null ? theObject.toString( ) : "" , theName , 1 );
}
public ButtonBar addButtonBar( String theName ) {
return addButtonBar( null , theName );
}
public ButtonBar addButtonBar( final Object theObject , final String theName ) {
return addButtonBar( theObject , theObject != null ? theObject.toString( ) : "" , theName , 1 );
}
/**
* Toggles a boolean field or passes a value when
* triggered.
*/
public Toggle addToggle( final Object theObject , final String theName ) {
return addToggle( theObject , theObject != null ? theObject.toString( ) : "" , theName );
}
public Toggle addToggle( final String theName ) {
return addToggle( null , theName );
}
/**
* Adds a default slider with a default width of 100 and
* height of 10. the default value range is from 0-100.
*
* By default it will be added to the default tab of the
* main window. Sliders can be arranged vertically and
* horizontally depending on their width and height. The
* look of a sliders control can either be a bar or a
* handle. you can add tickmarks to a slider or use the
* default free-control setting. A slider can be
* controller by mouse click, drag or mouse-wheel.
*/
public Slider addSlider( String theName ) {
return addSlider( null , theName );
}
public Slider addSlider( Object theObject , String theName ) {
return addSlider( theObject , theObject != null ? theObject.toString( ) : "" , theName );
}
/**
* A range controller, a slider that allows control on
* both ends of the slider.
*/
public Range addRange( final String theName ) {
return addRange( theName , 0 , 100 , 0 , 100 , 0 , 0 , 100 , 10 );
}
public Range addRange( final Object theObject , final String theName ) {
return addRange( theObject , theObject != null ? theObject.toString( ) : "" , theName , 0 , 100 , 0 , 100 , 0 , 0 , 100 , 10 );
}
public Numberbox addNumberbox( String theName ) {
return addNumberbox( null , theName );
}
public Numberbox addNumberbox( Object theObject , String theName ) {
return addNumberbox( theObject , theObject != null ? theObject.toString( ) : "" , theName );
}
/**
* Knobs can use limited and endless revolutions, custom
* angles and starting points. There are 2 control areas
* for a knob, an area closer to the edge allows
* 'click-and-adjust' control, a click and drag action
* at the inside allows to gradually change the value of
* a know when dragged. A knob can be controller by
* mouse click, drag or mouse-wheel.
*/
public Knob addKnob( String theName ) {
return addKnob( theName , 0 , 100 );
}
public Knob addKnob( Object theObject , String theName ) {
return addKnob( theObject , theObject != null ? theObject.toString( ) : "" , theName );
}
/**
* Matrix is a 2-D matrix controller using toggle
* controllers in a rows and a columns setup. useful for
* software drum machines.
*/
public Matrix addMatrix( final String theName ) {
return addMatrix( theName , 10 , 10 , 0 , 0 , 100 , 100 );
}
public Matrix addMatrix( final Object theObject , final String theName ) {
return addMatrix( theObject , theObject != null ? theObject.toString( ) : "" , theName , 10 , 10 , 0 , 0 , 100 , 100 );
}
/**
* Adds a 2D slider to controlP5. A 2D slider is a 2D
* area with 1 cursor returning its xy coordinates.
*/
public Slider2D addSlider2D( final String theName ) {
return addSlider2D( null , theName );
}
public Slider2D addSlider2D( final Object theObject , final String theName ) {
return addSlider2D( theObject , theObject != null ? theObject.toString( ) : "" , theName , 0 , 100 , 0 , 100 , 0 , 0 , 0 , 0 , 100 , 100 );
}
public Textlabel addTextlabel( final String theName ) {
return addTextlabel( theName , "" , 0 , 0 );
}
/**
* A Textarea is a label without any controller
* functionality and can be used to leave notes,
* headlines, etc when extending the dedicated area of
* the Textrea, a scrollbar is added on the right.
*/
public Textarea addTextarea( final String theName ) {
return addTextarea( theName , "" , 0 , 0 , 200 , 100 );
}
// TODO
// addTextarea theObject
/**
* A Textfield allows single line text input. If text
* goes beyond the edges of a Textfield box, the text
* will automatically scroll. Use Arrow keys to navigate
* back and forth.
*/
public Textfield addTextfield( final String theIndex ) {
return addTextfield( theIndex , 0 , 0 , 200 , 20 );
}
public Textfield addTextfield( final Object theObject , final String theIndex ) {
return addTextfield( theObject , theObject != null ? theObject.toString( ) : "" , theIndex , 0 , 0 , 200 , 20 );
}
/**
* Use radio buttons for multiple choice options.
*/
public RadioButton addRadioButton( final String theName ) {
return addRadioButton( null , theName );
}
public RadioButton addRadioButton( final Object theObject , final String theName ) {
return addRadioButton( theObject , theObject != null ? theObject.toString( ) : "" , theName , 0 , 0 );
}
/**
* Use a checkbox for single choice options.
*/
public CheckBox addCheckBox( final String theName ) {
return addCheckBox( theName , 0 , 0 );
}
public CheckBox addCheckBox( final Object theObject , final String theName ) {
return addCheckBox( theObject , theObject != null ? theObject.toString( ) : "" , theName , 0 , 0 );
}
/**
* the ScrollableList replaces the DropwdownList and
* ListBox, the type for a ScrollableList can be set
* with setType(ControlP5.DROPDOWN | ControlP5.LIST).
*/
public ScrollableList addScrollableList( final String theName ) {
return addScrollableList( theName , 0 , 0 , 100 , 200 );
}
public ScrollableList addScrollableList( final Object theObject , String theName ) {
return addScrollableList( theObject , theObject != null ? theObject.toString( ) : "" , theName , 0 , 0 , 100 , 100 );
}
/**
* Multilist is a tree like menu.
*/
public MultiList addMultiList( final String theName ) {
return addMultiList( null , theName );
}
public MultiList addMultiList( final Object theObject , final String theName ) {
return addMultiList( theObject , theObject != null ? theObject.toString( ) : "" , theName , 0 , 0 , 100 , 100 );
}
public ColorWheel addColorWheel( final String theName ) {
return addColorWheel( null , theName );
}
public ColorWheel addColorWheel( final Object theObject , final String theName ) {
return addColorWheel( theObject , theObject != null ? theObject.toString( ) : "" , theName , 0 , 0 , 200 );
}
/**
* adds a simple RGBA colorpicker.
*/
public ColorPicker addColorPicker( final String theName ) {
return addColorPicker( null , theName );
}
public ColorPicker addColorPicker( final Object theObject , final String theName ) {
return addColorPicker( theObject , theObject != null ? theObject.toString( ) : "" , theName , 0 , 0 , 255 , 10 );
}
public Println addConsole( Textarea theTextarea ) {
return new Println( theTextarea );
}
/**
* returns the current framerate of the running sketch.
*/
public FrameRate addFrameRate( ) {
FrameRate myController = new FrameRate( cp5 , ( Tab ) cp5.controlWindow.getTabs( ).get( 1 ) , "-" , 0 , 4 );
cp5.register( null , "" , myController );
return myController;
}
/**
* adds chart support to display float array based data.
*/
public Chart addChart( String theName ) {
return addChart( theName , 0 , 0 , 200 , 100 );
}
/**
* A controller group can be used to group controllers
* for a better organization of single controllers.
*/
public Group addGroup( String theName ) {
return addGroup( theName , 0 , 0 );
}
public Group addGroup( final Object theObject , final String theName ) {
return addGroup( theObject , theObject != null ? theObject.toString( ) : "" , theName );
}
public Accordion addAccordion( final String theName ) {
return addAccordion( null , "" , theName );
}
public Accordion addAccordion( final Object theObject , final String theName ) {
return addAccordion( theObject , theObject != null ? theObject.toString( ) : "" , theName );
}
protected void setCurrentPointer( ControllerGroup< ? > theGroup ) {
currentGroupPointer = theGroup;
isCurrentGroupPointerClosed = false;
}
protected void releaseCurrentPointer( ControllerGroup< ? > theGroup ) {
if ( isCurrentGroupPointerClosed == false ) {
currentGroupPointer = theGroup;
isCurrentGroupPointerClosed = true;
} else {
ControlP5.logger( ).warning( "use .end() first before using .begin() again." );
}
}
public void setAutoAddDirection( int theDirection ) {
if ( theDirection == HORIZONTAL ) {
autoDirection = HORIZONTAL;
return;
}
autoDirection = VERTICAL;
}
public void setAutoSpacing( ) {
set( Controller.autoSpacing , 10 , 10 );
}
public void setAutoSpacing( float theX , float theY ) {
set( Controller.autoSpacing , theX , theY );
}
public void setAutoSpacing( float theX , float theY , float theZ ) {
setAutoSpacing( theX , theY );
}
@SuppressWarnings( "static-access" ) protected void linebreak( Controller< ? > theController , boolean theFlag , int theW , int theH , float[] theSpacing ) {
if ( x( currentGroupPointer.autoPosition ) + x( theController.autoSpacing ) + theW > cp5.papplet.width ) {
float x = x( currentGroupPointer.autoPosition ) + currentGroupPointer.autoPositionOffsetX;
float y = y( currentGroupPointer.autoPosition ) + currentGroupPointer.tempAutoPositionHeight;
set( currentGroupPointer.autoPosition , x , y );
currentGroupPointer.tempAutoPositionHeight = 0;
Controller.set( theController.position , Controller.x( currentGroupPointer.autoPosition ) , Controller.y( currentGroupPointer.autoPosition ) );
theFlag = false;
}
if ( theFlag == true ) {
float y = y( currentGroupPointer.autoPosition ) + currentGroupPointer.tempAutoPositionHeight;
set( currentGroupPointer.autoPosition , currentGroupPointer.autoPositionOffsetX , y );
currentGroupPointer.tempAutoPositionHeight = 0;
} else {
if ( theController instanceof Slider ) {
float x = x( currentGroupPointer.autoPosition ) + theController.getCaptionLabel( ).getWidth( );
float y = y( currentGroupPointer.autoPosition );
set( currentGroupPointer.autoPosition , x , y );
}
float x = x( currentGroupPointer.autoPosition ) + x( theController.autoSpacing ) + theW;
float y = y( currentGroupPointer.autoPosition );
set( currentGroupPointer.autoPosition , x , y );
if ( ( theH + y( theSpacing ) ) > currentGroupPointer.tempAutoPositionHeight ) {
currentGroupPointer.tempAutoPositionHeight = theH + y( theSpacing );
}
}
}
public ControlP5Base addControllersFor( PApplet theApplet ) {
addControllersFor( "" , theApplet );
return cp5;
}
/**
* Adds controllers for a specific object using
* annotations.
* <p>
* Uses a forward slash delimited address, for example:
* </p>
* <p>
* lets say the theAddressSpace parameter is set to
* "hello", and the Object (second parameter) contains
* an annotated field "x", addControllersFor("hello",
* o); will add a controller for field x with address
* /hello/x This address can be used with
* getController("/hello/x") to access the controller of
* that particular Object and field.
* </p>
*/
public ControlP5Base addControllersFor( final String theAddressSpace , Object t ) {
_myAutomator.addControllersFor( theAddressSpace , t );
return cp5;
}
public Object getObjectForController( ControllerInterface theController ) {
for ( Iterator it = _myObjectToControllerMap.entrySet( ).iterator( ) ; it.hasNext( ) ; ) {
Map.Entry entry = ( Map.Entry ) it.next( );
Object key = entry.getKey( );
ArrayList< ControllerInterface > value = ( ArrayList< ControllerInterface > ) entry.getValue( );
for ( ControllerInterface c : value ) {
if ( c.equals( theController ) ) {
return key;
}
}
}
return null;
}
public ControlP5Base setPosition( int theX , int theY , Object o ) {
if ( o != null && _myObjectToControllerMap.containsKey( o ) ) {
ArrayList< ControllerInterface< ? >> cs = _myObjectToControllerMap.get( o );
for ( ControllerInterface< ? > c : cs ) {
int x = ( int ) x( c.getPosition( ) ) + theX;
int y = ( int ) y( c.getPosition( ) ) + theY;
c.setPosition( x , y );
}
}
return cp5;
}
public ControlP5Base hide( Object theObject ) {
if ( theObject != null && _myObjectToControllerMap.containsKey( theObject ) ) {
ArrayList< ControllerInterface< ? >> cs = _myObjectToControllerMap.get( theObject );
for ( ControllerInterface< ? > c : cs ) {
c.hide( );
}
}
return cp5;
}
public ControlP5Base show( Object theObject ) {
if ( theObject != null && _myObjectToControllerMap.containsKey( theObject ) ) {
ArrayList< ControllerInterface< ? >> cs = _myObjectToControllerMap.get( theObject );
for ( ControllerInterface< ? > c : cs ) {
c.show( );
}
}
return cp5;
}
/**
* for internal use only. use Controller.remove()
* instead.
*
* @param theObject
* @return
*/
public ControlP5Base remove( Object theObject ) {
if ( theObject != null && _myObjectToControllerMap.containsKey( theObject ) ) {
ArrayList< ControllerInterface< ? >> cs = _myObjectToControllerMap.get( theObject );
for ( ControllerInterface< ? > c : cs ) {
c.remove( );
}
}
return cp5;
}
public ControlP5Base setColor( CColor theColor , Object theObject ) {
if ( _myObjectToControllerMap.containsKey( theObject ) ) {
ArrayList< ControllerInterface< ? >> cs = _myObjectToControllerMap.get( theObject );
for ( ControllerInterface< ? > c : cs ) {
c.setColor( theColor );
}
}
return cp5;
}
public ControlP5Base listenTo( String theFieldName , Object theObject ) {
String key = theObject.hashCode( ) + "" + theFieldName.hashCode( );
FieldChangedListener value = new FieldChangedListener( cp5 );
value.listenTo( theObject , theFieldName );
_myFieldChangedListenerMap.put( key , value );
return cp5;
}
public ControlP5Base stopListeningTo( String theFieldName , Object theObject ) {
String key = theObject.hashCode( ) + "" + theFieldName.hashCode( );
_myFieldChangedListenerMap.remove( key );
return cp5;
}
public ControlP5Base moveTo( ControllerGroup< ? > theController , Object theObject ) {
if ( _myObjectToControllerMap.containsKey( theObject ) ) {
ArrayList< ControllerInterface< ? >> cs = _myObjectToControllerMap.get( theObject );
for ( ControllerInterface< ? > c : cs ) {
c.moveTo( theController );
}
}
return cp5;
}
/* Properties */
public ControllerProperties getProperties( ) {
return _myProperties;
}
public void removeProperty( ControllerInterface< ? > theController ) {
_myProperties.remove( theController );
}
/**
* prints a list of public methods of requested class
* into the console. You can specify patterns that will
* print methods found with only these particular
* patterns in their name.
* <p>
* printed Format: returnType methodName(parameter type)
*/
public static void printPublicMethodsFor( Class< ? > theClass , String ... thePattern ) {
Set< String > set = getPublicMethodsFor( theClass , true , thePattern );
String str = "";
str += "/**\n";
str += "* ControlP5 " + theClass.getSimpleName( ) + "\n";
str += "*\n";
str += "*\n";
str += "* find a list of public methods available for the " + theClass.getSimpleName( ) + " Controller\n";
str += "* at the bottom of this sketch.\n";
str += "*\n";
str += "* by Andreas Schlegel, 2012\n";
str += "* www.sojamo.de/libraries/controlp5\n";
str += "*\n";
str += "*/\n\n";
str += "/*\n";
str += "a list of all methods available for the " + theClass.getSimpleName( ) + " Controller\n";
str += "use ControlP5.printPublicMethodsFor(" + theClass.getSimpleName( ) + ".class);\n";
str += "to print the following list into the console.\n\n";
str += "You can find further details about class " + theClass.getSimpleName( ) + " in the javadoc.\n\n";
str += "Format:\n";
str += "ClassName : returnType methodName(parameter type)\n\n\n";
for ( String s : set ) {
str += s + "\n";
}
str += "\n\n*/\n\n";
println( str );
}
public static void printPublicMethodsFor( Class< ? > theClass ) {
printPublicMethodsFor( theClass , "" );
}
public static Set< String > getPublicMethodsFor( Class< ? > theClass ) {
return getPublicMethodsFor( theClass , true , "" );
}
public static Set< String > getPublicMethodsFor( Class< ? > theClass , String ... thePattern ) {
return getPublicMethodsFor( theClass , true , thePattern );
}
public static Set< String > getPublicMethodsFor( Class< ? > theClass , boolean theFlag ) {
return getPublicMethodsFor( theClass , true , "" );
}
public static Set< String > getPublicMethodsFor( Class< ? > theClass , boolean isSuperclass , String ... thePattern ) {
Set< String > s = new TreeSet< String >( );
Class< ? > c = theClass;
while ( c != null ) {
for ( Method method : c.getDeclaredMethods( ) ) {
if ( !method.isAnnotationPresent( Deprecated.class ) && !method.isAnnotationPresent( ControlP5.Invisible.class ) && method.getModifiers( ) == Modifier.PUBLIC ) {
for ( String p : thePattern ) {
if ( p.length( ) > 0 ) {
if ( !method.getName( ).toLowerCase( ).contains( p.toLowerCase( ) ) ) {
continue;
}
}
String params = "";
for ( Class< ? > t : method.getParameterTypes( ) ) {
params += t.getSimpleName( ) + ", ";
}
if ( params.length( ) > 0 ) {
params = params.substring( 0 , params.length( ) - 2 );
}
s.add( c.getCanonicalName( ) + " : " + method.getReturnType( ).getSimpleName( ).replace( "Object" , theClass.getSimpleName( ) ) + " " + method.getName( ) + "(" + params + ") " );
}
}
}
if ( isSuperclass ) {
c = c.getSuperclass( );
} else {
c = null;
}
}
return s;
}
public int getKeyCode( ) {
return cp5.getWindow( ).keyCode;
}
public char getKey( ) {
return cp5.getWindow( ).key;
}
private char[] fromIntToChar( int ... theChar ) {
char[] n = new char[ theChar.length ];
for ( int i = 0 ; i < n.length ; i++ ) {
if ( theChar[ i ] >= 'a' && theChar[ i ] <= 'z' ) {
theChar[ i ] -= 32;
}
n[ i ] = ( char ) theChar[ i ];
}
return n;
}
public ControlP5 removeKeyFor( ControlKey theKey , int ... theChar ) {
removeKeyFor( theKey , fromIntToChar( theChar ) );
return cp5;
}
public ControlP5 mapKeyFor( ControlKey theKey , Object ... os ) {
List< Integer > l = new ArrayList< Integer >( );
for ( Object o : os ) {
if ( o instanceof Integer ) {
l.add( ( int ) ( Integer ) o );
} else if ( o instanceof Character ) {
char c = ( ( Character ) o );
if ( c >= 'a' && c <= 'z' ) {
c -= 32;
}
l.add( ( int ) c );
}
}
char[] n = new char[ l.size( ) ];
for ( int i = 0 ; i < l.size( ) ; i++ ) {
n[ i ] = ( char ) ( ( int ) l.get( i ) );
}
KeyCode kc = new KeyCode( n );
if ( !keymap.containsKey( kc ) ) {
keymap.put( kc , new ArrayList< ControlKey >( ) );
}
keymap.get( kc ).add( theKey );
cp5.enableShortcuts( );
return cp5;
}
public ControlP5 removeKeyFor( ControlKey theKey , char ... theChar ) {
List< ControlKey > l = keymap.get( new KeyCode( theChar ) );
if ( l != null ) {
l.remove( theKey );
}
return cp5;
}
public ControlP5 removeKeysFor( char ... theChar ) {
keymap.remove( new KeyCode( theChar ) );
return cp5;
}
public ControlP5 removeKeysFor( int ... theChar ) {
removeKeysFor( fromIntToChar( theChar ) );
return cp5;
}
protected int modifiers;
public boolean isShiftDown( ) {
return ( modifiers & Event.SHIFT & ( cp5.isShortcuts( ) ? -1 : 1 ) ) != 0;
}
public boolean isControlDown( ) {
return ( modifiers & Event.CTRL & ( cp5.isShortcuts( ) ? -1 : 1 ) ) != 0;
}
public boolean isMetaDown( ) {
return ( modifiers & Event.META & ( cp5.isShortcuts( ) ? -1 : 1 ) ) != 0;
}
public boolean isAltDown( ) {
return ( modifiers & Event.ALT & ( cp5.isShortcuts( ) ? -1 : 1 ) ) != 0;
}
static class KeyCode {
final char[] chars;
KeyCode( char ... theChars ) {
chars = theChars;
Arrays.sort( chars );
}
public String toString( ) {
String s = "";
for ( char c : chars ) {
s += c + "(" + ( ( int ) c ) + ") ";
}
return s;
}
public int size( ) {
return chars.length;
}
public char[] getChars( ) {
return chars;
}
public char get( int theIndex ) {
if ( theIndex >= 0 && theIndex < size( ) ) {
return chars[ theIndex ];
}
return 0;
}
public boolean equals( Object obj ) {
if ( ! ( obj instanceof KeyCode ) ) {
return false;
}
KeyCode k = ( KeyCode ) obj;
if ( k.size( ) != size( ) ) {
return false;
}
for ( int i = 0 ; i < size( ) ; i++ ) {
if ( get( i ) != k.get( i ) ) {
return false;
}
}
return true;
}
boolean contains( char n ) {
for ( char c : chars ) {
if ( n == c ) {
return true;
}
}
return false;
}
public int hashCode( ) {
int hashCode = 0;
int n = 1;
for ( char c : chars ) {
hashCode += c + Math.pow( c , n++ );
}
return hashCode;
}
}
}