I'm learning the concept of neural networks. I decided to try making the neuron class by myself. What is the best way to implement different activation functions in my code? Now it uses only the binary step function.
It's my first try in coding neural networks so if you have any suggestions about my code, or it is completely dumb, please let me know.
Here is my code:
public class Neuron {
// properties
private ArrayList<Neuron> input;
private ArrayList<Float> weight;
private float pot, bias, sense, out;
private boolean checked;
// methods
public float fire(){
pot = 0f;
if (input != null) {
for (Neuron n : input){
if (!n.getChecked()){
pot += n.fire()*weight.get(input.indexOf(n));
} else {
pot += n.getOut()*weight.get(input.indexOf(n));
} // end of condition (checked)
} // end of loop (for input)
} // end of condition (input exists)
checked = true;
pot -= bias;
pot += sense;
out = actFunc(pot);
return out;
} // end of fire()
// getting properties
public float getPot(){return pot;}
public boolean getChecked(){return checked;}
public float getOut(){return out;}
// setting properties
public void stimulate(float f){sense = f;}
public void setBias(float b){bias = b;}
public void setChecked(boolean c){checked = c;}
public void setOut(float o){out = o;}
// connection
public void connect(Neuron n, float w){
input.add(n);
weight.add(w);
}
public void deconnect(Neuron n){
weight.remove(input.indexOf(n));
input.remove(n);
}
// activation function
private float actFunc(float x){
if (x < 0) {
return 0f;
} else {
return 1f;
}
}
// constructor
public Neuron(Neuron[] ns, float[] ws, float b, float o){
if (ns != null){
input = new ArrayList<Neuron>();
weight = new ArrayList<Float>();
for (Neuron n : ns) input.add(n);
for (int i = 0; i < ws.length; i++) weight.add(ws[i]);
} else {
input = null;
weight = null;
}
bias = b;
out = o;
}
public Neuron(Neuron[] ns){
if (ns != null){
input = new ArrayList<Neuron>();
weight = new ArrayList<Float>();
for (Neuron n : ns) input.add(n);
for (int i = 0; i < input.size(); i++) weight.add((float)Math.random()*2f-1f);
} else {
input = null;
weight = null;
}
bias = (float)Math.random();
out = (float)Math.random();
}
}
First, define interface of any activation function:
public interface ActivationFunction {
float get(float f);
}
Then write some implementations:
public class StepFunction implements ActivationFunction {
#Override
public float get() {return (x < 0) ? 0f : 1f;}
}
public class SigmoidFunction implements ActivationFunction {
#Override
public float get() {return StrictMath.tanh(h);}
}
Finally, set some implementation to your Neuron:
public class Neuron {
private final ActivationFunction actFunc;
// other fields...
public Neuron(ActivationFunction actFunc) {
this.actFunc = actFunc;
}
public float fire(){
// ...
out = actFunc.get(pot);
return out;
}
}
as following:
Neuron n = new Neuron(new SigmoidFunction());
Note, neural netoworks are using signal propagation through neurons, where weights are produced. Computing of weight depends also on first derivative of an activation function. Therefore, I would extend ActivationFunction by method, which will return first derivative at specified point x:
public interface ActivationFunction {
float get(float f);
float firstDerivative(float x);
}
So the implemenations will look like:
public class StepFunction implements ActivationFunction {
#Override
public float get(float x) {return (x < 0) ? 0f : 1f;}
#Override
public float firstDerivative(float x) {return 1;}
}
public class SigmoidFunction implements ActivationFunction {
#Override
public float get(float x) {return StrictMath.tanh(x);}
// derivative_of tanh(x) = (4*e^(2x))/(e^(2x) + 1)^2 == 1-tanh(x)^2
#Override
public float firstDerivative(float x) {return 1 - Math.pow(StrictMath.tanh(x), 2);}
}
Then, use actFunction.firstDerivative(x); in fire() method where weight is being computed.
Related
I have created the class angle as shown in the codebox below, I want to calculate the difference( called "minus" in the code) of two angles with the following command.
Angle.degrees(135).minus(Angle.degrees(90)).getDegrees()
Unfortunately, I always get zero as result, because the intern values are always overwritten.
import java.lang.Math;
public class Angle {
private static double gradmass = 0;
private static double bogenmass = 0;
public static Angle degrees(double angle) {
Angle angleD = new Angle();
// gradmass = angle;
// bogenmass = Math.toRadians(angle);
angleD.setDegrees(angle);
angleD.setRadians(Math.toRadians(angle));
return angleD;
}
public static Angle radians(double angle) {
Angle angleR = new Angle();
// gradmass = Math.toDegrees(angle);
// bogenmass = angle;
angleR.setDegrees(Math.toDegrees(angle));
angleR.setRadians(angle);
return angleR;
}
public double getDegrees() {
return gradmass;
}
public void setDegrees(double gradM) {
gradmass = gradM;
}
public double getRadians() {
return bogenmass;
}
public void setRadians(double bogenM) {
bogenmass = bogenM;
}
public Angle plus(Angle other) {
Angle temp = new Angle();
temp.setDegrees(this.getDegrees() + other.getDegrees());
temp.setRadians(other.getRadians() + other.getRadians());
return temp;
}
public Angle minus(Angle other) {
Angle temp = new Angle();;
temp.setDegrees(this.getDegrees() - other.getDegrees());
temp.setRadians(this.getRadians() - other.getRadians());
return temp;
}
public Angle neg() {
Angle temp = new Angle();
temp.setDegrees(-this.getDegrees());
temp.setRadians(-this.getRadians());
return temp;
}
public double sin() {
double temp;
temp = Math.sin(this.getDegrees());
return temp;
}
public double cos() {
double temp;
temp = Math.cos(this.getDegrees());
return temp;
}
public boolean similarTo(Angle other){
boolean gleich = false;
if( 0 == (this.getDegrees() - other.getDegrees()) || this.neg().getDegrees() == other.getDegrees()){
gleich = true;
}
return gleich;
}
public String toString(){
return
"GradM " + this.getDegrees() + " BogenM " + this.getRadians();
}
}
I did not make a constructor on purpose! I'm looking for a solution without a constructor or nonstatic methods.
Both your data members are static, meaning there's a single instance of them for the entire class. You should declare them as instance members so that each instance of Angle can have its own values:
public class Angle {
private double gradmass = 0;
private double bogenmass = 0;
// rest of the code...
Don't do this. Your class won't be thread-safe this way. What you want is kind of something like BigDecimal class in java, I guess. You can check the documentation of BigDecimal class of java if you want something like this.
https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html
But if you want the exact same thing you asked, you can see this (not thread safe and not recommended as well).
class Angle {
static Double angle1 = null;
static Double angle2 = null;
private static void setAngle(double angle) {
if (angle1 == null) angle1 = angle;
else angle2 = angle;
}
static Angle degrees(Double angle) {
setAngle(angle);
return new Angle();
}
static Double getDegrees() {
return angle1;
}
static Angle minus(Angle angle) {
angle1 -= angle2;
return new Angle();
}
}
public class SolutionAngle {
public static void main(String... args) {
System.out.println(Angle.degrees(135).minus(Angle.degrees(90)).getDegrees());
}
}
I've written my first Genetic Algorithm in Java and I'm able to optimize functions with one argument x, but I don't know how to optimize functions with two arguments x and y. Algorithm class and main app works correctly so i send only Individual.java and Population.java. If I think correctly in genes I have only x-coordinate but I'm not sure how to add y-coordinate. Any advise will be helpfull.
Individual.java
public class Individual {
private int[] genes;
private int fitness;
private Random randomGenerator;
public Individual() {
this.genes = new int[Constants.CHROMOSOME_LENGTH];
this.randomGenerator = new Random();
}
public void generateIndividual() {
for(int i = 0; i < Constants.CHROMOSOME_LENGTH; i++) {
int gene = randomGenerator.nextInt(2);
genes[i] = gene;
}
}
public double f(double x) {
// return Math.pow(x,2);
return (Math.pow((1-x),2)) + (100*(Math.pow((1-Math.pow(x,2)),2)));
// return Math.sin(x)*((x-2)*(x-2))+3;
}
public double getFitness() {
double genesToDouble = genesToDouble();
return f(genesToDouble);
}
public double getFitnessResult() {
double genesToDouble = genesToDouble();
return genesToDouble;
}
public double genesToDouble() {
int base = 1;
double geneInDouble = 0;
for( int i =0; i < Constants.GENE_LENGTH; i++) {
if(this.genes[i] == 1)
geneInDouble += base;
base = base*2;
}
geneInDouble = (geneInDouble / 1024) * 10.1;
return geneInDouble;
}
public int getGene(int index) {
return this.genes[index];
}
public void setGene(int index, int value) {
this.genes[index] = value;
this.fitness = 0;
}
}
Population.java
public class Population {
private Individual[] individuals;
public Population(int populationSize) {
individuals = new Individual[populationSize];
}
public void initialize() {
for(int i = 0; i < individuals.length; i++) {
Individual newIndividual = new Individual();
newIndividual.generateIndividual();
saveIndividual(i, newIndividual);
}
}
public Individual getIndividual(int index) {
return this.individuals[index];
}
//maksimum lub minimum
public Individual getFittestIndividual() {
Individual fittest = individuals[0];
for(int i =0; i < individuals.length; i++) {
if(getIndividual(i).getFitness() < fittest.getFitness())
fittest = getIndividual(i);
}
return fittest;
}
public int size() {
return this.individuals.length;
}
public void saveIndividual(int index, Individual individual) {
this.individuals[index] = individual;
}
}
I`m trying to run BFS, when i get to PriorityQueue openList.add(state)
the first time it works and the secound time it dosent.
The error is:
Exception in thread "main" java.lang.ClassCastException: algorithms.mazeGenerators.Position cannot be cast to java.lang.Comparable
at java.util.PriorityQueue.siftUpComparable(Unknown Source)
at java.util.PriorityQueue.siftUp(Unknown Source)
at java.util.PriorityQueue.offer(Unknown Source)
at java.util.PriorityQueue.add(Unknown Source)
at algorithms.searchers.BFS.search(BFS.java:30)
at boot.Run.main(Run.java:18)
BFS CLASS:
public class BFS extends CommonSearcher {
#Override
public Solution search(Searchable s) {
State cur = null;
s.getStartState().setCost(0);
openList.add(s.getStartState());
HashSet<State> closedSet = new HashSet<State>();
while (!openList.isEmpty()) {
cur = popOpenList();
closedSet.add(cur);
if (cur.equals(s.getGoalState())) {
return backTrace(cur, s.getStartState());
}
ArrayList<State> successors = s.getAllPossibleStates(cur);
for (State state : successors) {
if (!closedSet.contains(state) && !openList.contains(state)) {
state.setCameFrom(cur);
state.setCost(cur.getCost() + 1);
openList.add(state);
} else {
if (openList.contains(state)) {
if (state.getCost() < returnWantedState(state).getCost()) {
openList.remove(state);
openList.add(state);
adjustPriorityList();
}
} else {
openList.add(state);
adjustPriorityList();
}
}
}
}
return null;
}
/*
* public State popOpenList() { State temp = openList.remove(); for (State
* state : openList) { if (temp.getCost() > state.getCost()) {
* openList.add(temp); temp = state; openList.remove(state); } } return
* temp;
*
* }
*/
public void adjustPriorityList() {
State temp = openList.remove();
for (State state : openList) {
if (temp.getCost() < state.getCost()) {
openList.add(temp);
temp = state;
openList.remove(state);
}
}
openList.add(temp);
}
public State returnWantedState(State state) {
for (State state1 : openList) {
if (state.equals(state1))
state = state1;
}
return state;
}
}
CommonSearcher Class:
package algorithms.searchers;
import java.util.PriorityQueue;
import algorithms.mazeGenerators.Searchable;
import algorithms.mazeGenerators.Solution;
import algorithms.mazeGenerators.State;
public abstract class CommonSearcher implements Searcher {
protected PriorityQueue<State> openList;
private int evaluatedNodes;
public CommonSearcher() {
openList = new PriorityQueue<State>();
evaluatedNodes = 0;
}
protected State popOpenList(){
evaluatedNodes++;
return openList.poll();
}
#Override
public abstract Solution search(Searchable s);
#Override
public int getNumberOfnodesEvaluated() {
// TODO Auto-generated method stub
return evaluatedNodes;
}
protected Solution backTrace(State goalState, State startState){
Solution sol = new Solution();
while(!goalState.equals(startState)){
sol.getSolutionList().add(goalState.getState());
goalState = goalState.getCameFrom();
}
return sol;
}
}
State Class:
package algorithms.mazeGenerators;
public abstract class State {
protected String state; // the state represented by a string
protected double cost; // cost to reach this state
protected State cameFrom; // the state we came from to this state
public State(){
}
public State(String state){ // CTOR
this.state = state;
}
#Override
public boolean equals(Object obj){ // we override Object's equals method
return state.equals(((State)obj).state);
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public double getCost() {
return cost;
}
public void setCost(double cost) {
this.cost = cost;
}
public State getCameFrom() {
return cameFrom;
}
public void setCameFrom(State cameFrom) {
this.cameFrom = cameFrom;
}
}
Position Class:
package algorithms.mazeGenerators;
import java.util.ArrayList;
public class Position extends State {
// Data members
private int x, y, z;
private int wallOrNot;
private boolean visted;
// Constructor
public Position() {
visted = false;
wallOrNot = 1;
}
/*
* The method gets the position details
* and checks if its a wall or not
* if its a wall then its marked as visited.
* */
public void setPos(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
if (z % 2 != 0 || x % 2 != 0 || y % 2 != 0)
visted = true;
setState("{" + x+"," + y+","+ z +"}");
}
// getrs and setters
public int getWallOrNot() {
return wallOrNot;
}
public void setWallOrNot(int wallOrNot) {
this.wallOrNot = wallOrNot;
}
public boolean isVisted() {
return visted;
}
public void setVisted(boolean visted) {
this.visted = visted;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getZ() {
return z;
}
public void setZ(int z) {
this.z = z;
}
/*
* This method gets returns all a list of neighbors that hasn't marked as visited for a specific Position.
* returns the list of neighbors.
* */
public ArrayList<Position> getNeighbors(Position[][][] maze) {
ArrayList<Position> neighbors = new ArrayList<Position>();
if (this.x > 1)
if (maze[x - 2][y][z].isVisted() == false)
neighbors.add(maze[x - 2][y][z]);
if (this.x < maze.length - 2)
if (maze[x + 2][y][z].isVisted() == false)
neighbors.add(maze[x + 2][y][z]);
if (this.y > 1)
if (maze[x][y - 2][z].isVisted() == false)
neighbors.add(maze[x][y - 2][z]);
if (this.y < maze[x].length - 2)
if (maze[x][y + 2][z].isVisted() == false)
neighbors.add(maze[x][y + 2][z]);
if (this.z > 1)
if (maze[x][y][z - 2].isVisted() == false)
neighbors.add(maze[x][y][z - 2]);
if (this.z < maze[x][y].length - 2)
if (maze[x][y][z + 2].isVisted() == false)
neighbors.add(maze[x][y][z + 2]);
return neighbors;
}
public String toString(){
return "{" + x+"," + y+","+ z +"}";
}
public boolean equals(Object obj){ // we override Object's equals method
return state.equals(((Position)obj).state);
}
}
The purpose of a priority queue requires an ordering of its elements.
In Java's PriorityQueue this can be done by either making the elements implement the Comparable interface,
or by specifying a Comparator.
I`m trying to run BFS, when i get to PriorityQueue openList.add(state) the first time it works and the secound time it dosent.
If you insert only one object into a PriorityQueue,
it will work even if the object doesn't implement the Comparable interface,
because a single object doesn't need to be compared to anything.
You get a ClassCastException when you insert a second object,
if the objects don't implement Comparable and you didn't provide a Comparator.
public abstract class State implements Comparable<State> {
// ...
#Override
public int compareTo(State other) {
if (getCost() > other.getCost()) {
return -1;
}
if (getCost() < other.getCost()) {
return 1;
}
return 0;
}
}
PriorityQueue requires its element to implement the Comparable interface, yet your State class does not do it.
From the java docs:
A priority queue relying on natural ordering also does not permit
insertion of non-comparable objects (doing so may result in
ClassCastException).
You need to make your State class something like:
public abstract class State implements Comparable<State> {
....
#Override
public int compareTo(State s) {
...
}
}
I've got this custom class and I want to sort it by saturation and brightness.
I've tried a custom comperator class, but it doesn't work.
Now I've tried to implement Comparable. The program runs through the code but doesn't sort the list at the end.
Here is my code.
Part of the testing class:
ArrayList<HSBColor> colorList = new ArrayList<HSBColor>(colors.values());
Collections.sort(colorList);
for(HSBColor co : colorList){
System.out.println(co.toString());
}
Custom Class HSBColor
public class HSBColor implements Comparable<HSBColor>{
private float H;
private float S;
private float B;
public HSBColor(float h, float s, float b) {
H = h;
S = s;
B = b;
}
public float getH() {
return H;
}
#Override
public String toString() {
return String.format("%.2f %.2f %.2f", H,S,B);
}
public void setH(float h) {
H = h;
}
public float getS() {
return S;
}
public void setS(float s) {
S = s;
}
public float getB() {
return B;
}
public void setB(float b) {
B = b;
}
#Override
public int compareTo(HSBColor o) {
if(this.getS() > o.getS() && this.getB() > o.getB()){
return 1;
}
else{
return -1;
}
}
}
Thanks in advance!
EDIT: Extra code
This are the colors before the sort:
Color HSB H:28.60465 S:71.07438 B:47.45098
Color HSB H:4.4999995 S:73.059364 B:85.882355 >> This is the one i need
Color HSB H:64.18605 S:79.62963 B:21.176472
Color HSB H:65.714294 S:39.873417 B:61.960785
Color HSB H:23.333332 S:40.0 B:70.588234
This are the colors after the sort
28,60 71,07 47,45
65,71 39,87 61,96
23,33 40,00 70,59
4,50 73,06 85,88
64,19 79,63 21,18
**EDIT new Algorithm **
This one compares it right, but doesn't sort them right..
#Override
public int compareTo(HSBColor o) {
if(this.getS() > o.getS()) {
if(this.getB() >o.getB()){
return 1;
}
else{
return 0;
}
}
else{
if(this.getB() < o.getB()){
return -1;
}
else{
return 0;
}
}
}
Your compareTo method doesn't define a proper ordering.
Suppose that this.getS() > o.getS() but this.getB() < o.getB().
this.compareTo(o) would return -1, but o.compareTo(this) would also return -1.
If A < B and B < A this is not a proper ordering.
A proper ordering would first compare by the more important property, and then, in case of equality, by the less important property.
For example :
#Override
public int compareTo(HSBColor o) {
if(this.getS() > o.getS()){
return 1;
} else if (this.getS() < o.getS()) {
return -1;
} else {
if (this.getB() > o.getB()) {
return 1;
} else if (this.getB() < o.getB()) {
return -1;
} else {
return 0;
}
}
}
There is an issue around compareTo method. You should use it like:
#Override
public int compareTo(HSBColor o) {//if saturation is equal then compare brightness.
if (this.S == o.getS()) {
return Float.compare(B, o.getB());
}
return Float.compare(S, o.getS());
}
I would like to create a JSpinner which can take every possible Double value between a specified minimum and a specified maximum.
Also, the JSpinner should be able to display a text instead of a specific value. Let's say our JSpinner can take values from -1 to 10. I would like to display a text, e.g. "Auto", instead of -1 .
How to replace by
Here is the Model I wrote, but it seems not to be enough, because it says in JSpinner there is an error because the text is not a Double.
public class SpinnerSpecialModel
extends AbstractSpinnerModel implements SpinnerMinMaxModel {
public static final double DEFAULT_MINIMUM = 0.0;
public static final double DEFAULT_MAXIMUM = Double.POSITIVE_INFINITY;
public static final double DEFAULT_STEP = 1.0;
public static final double DEFAULT_VALUE = 1.0;
public static final double DEFAULT_SPECIAL_NUMBER = -1.0;
public static final String DEFAULT_SPECIAL_TEXT = "Auto";
private double maximum;
private double minimum;
private double stepSize;
private double currentNumber;
private double specialNumber;
private String specialText;
private Object m_Value;
public SpinnerSpecialModel(double max, double min, double step, double num,
double specialNum, String specialTxt) {
maximum = max;
minimum = min;
stepSize = step;
currentNumber = num;
specialNumber = specialNum;
specialText = specialTxt;
setAccurateValue(num);
}
public SpinnerSpecialModel(double specialNum, String specialTxt) {
this(DEFAULT_MAXIMUM, DEFAULT_MINIMUM,
DEFAULT_STEP, DEFAULT_VALUE, specialNum, specialTxt);
}
public SpinnerSpecialModel() {
this(DEFAULT_SPECIAL_NUMBER, DEFAULT_SPECIAL_TEXT);
}
#Override
public Object getValue() {
if (currentNumber == specialNumber) {
m_Value = specialText;
}
else {
m_Value = currentNumber;
}
return m_Value;
}
#Override
public void setValue(Object value) {
setAccurateValue(value);
}
private void setAccurateValue(Object value) {
if (value instanceof Double) {
double doubleValue = (Double) value;
if (doubleValue != currentNumber) {
if (doubleValue == specialNumber) {
currentNumber = specialNumber;
m_Value = specialText;
}
else if (doubleValue > maximum) {
currentNumber = maximum;
m_Value = maximum;
}
else if (doubleValue < minimum) {
currentNumber = maximum;
m_Value = minimum;
}
else {
currentNumber = doubleValue;
m_Value = doubleValue;
}
fireStateChanged();
}
}
if (value instanceof String) {
String stringValue = (String) value;
if (stringValue.equals(specialText)) {
this.currentNumber = specialNumber;
this.m_Value = specialText;
fireStateChanged();
}
}
}
#Override
public Object getNextValue() {
return getNewValue(+1);
}
#Override
public Object getPreviousValue() {
return getNewValue(-1);
}
/**
*
* #param direction
* #return
*/
private Object getNewValue(int direction) {
double newValue = currentNumber + direction * stepSize;
setAccurateValue(newValue);
return m_Value;
}
#Override
public double getMaximum() {
return maximum;
}
#Override
public double getMinimum() {
return minimum;
}
#Override
public double getStepSize() {
return stepSize;
}
#Override
public void setMaximum(double max) {
maximum = max;
}
#Override
public void setMinimum(double min) {
minimum = min;
}
#Override
public void setStepSize(double step) {
stepSize = step;
}
}
The best and proper way to do this is not as simple as just writing a model, but it is not very complicated. You actually need to write an Editor and a Formatter to have a true MVC spinner:
A class that extends JSpinner : SpecialValuesSpinner.
A class that implements SpinnerModel : SpecialValuesSpinnerModel
A class that extends DefaultEditor and implements DocumentListener : SpecialValuesSpinnerEditor
A class that extends NumberFormatter : SpecialValuesSpinnerFormatter
I am not going to show you the code for all classes, but here is basically what you have to do in each :
SpecialValuesSpinner :
public class SpecialValuesSpinner() extends SpinnerNumberModel {
// in your constructor do this
setModel(new SpecialValuesSpinnerModel(YOUR_SPECIAL_VALUES);
setEditor(new SpecialValuesSpinnerEditor());
}
SpecialValuesSpinnerModel :
public class SpinnerSpecialValuesModel() extends JSpinner {
// in this class you handle the fact that now, you have an
// interval of values and a list of special values that are allowed.
// here is what I did :
#Override
public Object getNextValue() {
return incrValue(+1);
}
#Override
public Object getPreviousValue() {
return incrValue(-1);
}
private Object incrValue(int dir) {
// NB : BigDecimal here because this is what I used,
// but use what you want in your model
BigDecimal result = null;
BigDecimal numberBD = new BigDecimal(getNumber().toString());
BigDecimal stepSizeBD = new BigDecimal(getStepSize().toString());
BigDecimal dirBD = new BigDecimal(dir);
BigDecimal nextValue = numberBD.add(stepSizeBD.multiply(dirBD));
TreeSet<BigDecimal> currentAllowedValues = new TreeSet<BigDecimal>();
currentAllowedValues.addAll(m_SpecialValues);
if (getMaximum() != null) {
currentAllowedValues.add((BigDecimal) getMaximum());
}
if (getMinimum() != null) {
currentAllowedValues.add((BigDecimal) getMinimum());
}
if (isIncludedInBounds(nextValue)) {
currentAllowedValues.add(nextValue);
}
if (dir > 0) {
try {
result = currentAllowedValues.higher(numberBD);
}
catch (NoSuchElementException e) {}
}
else if (dir < 0) {
try {
result = currentAllowedValues.lower(numberBD);
}
catch (NoSuchElementException e) {}
}
return result;
}
}
In SpecialValuesSpinnerEditor, we use Document Listener to have autocompletion (easy to do, just search on SO).
public class SpecialValuesSpinnerEditor extends DefaultEditor implements DocumentListener {
// You have to do in your contructor
SpecialValuesSpinnerFormatter formatter =
new SpecialValuesSpinnerFormatter (spinner.getSpecialValues(), format);
getTextField().setFormatterFactory(new DefaultFormatterFactory(formatter));
}
And now, the most important, the Formatter which does conversion between user input (string) and numbers, and handle the model's display :
public class SpecialValuesSpinnerFormatter extends NumberFormatter {
// Just override the methos StringToValue and ValueToString.
// You can check here if the value is special
// i.e you must display its special text instead. e.g. : "Auto" instead of -1
}
I think you can achieve that by implementing your own SpinnerModel and supplying that as argument to the JSpinner constructor.