I have the following class in which I am trying to implement the prototype pattern:
public class Element extends Group implements Cloneable{
private final double ELEMENT_WIDTH = 50;
private final double ELEMENT_HEIGHT = 70;
private final double CIRCLE_RADIUS = 1;
private int minimalNumberOfInputs;
private Shape body = new Rectangle(ELEMENT_WIDTH, ELEMENT_HEIGHT, Color.WHITE);
private Circle output = new Circle(CIRCLE_RADIUS);
private Line outputLine = new Line(ELEMENT_WIDTH, ELEMENT_HEIGHT / 2, ELEMENT_WIDTH + 15, ELEMENT_HEIGHT / 2);
private ArrayList<Circle> inputs;
private ArrayList<Line> inputLines;
private Circle inversionDesignation;
private Text symbol;
private Integer identifier;
private double bodyCorX;
private double bodyCorY;
private double corX = 0;
private double corY = 0;
private double mouseX = 0;
private double mouseY = 0;
private boolean dragging = false;
public Integer getIdentifier() {
return identifier;
}
public ArrayList<Circle> getInputs() {
return inputs;
}
public Circle getOutput() {
return output;
}
public Circle getInversionDesignation() {
return inversionDesignation;
}
public double getELEMENT_WIDTH() {
return ELEMENT_WIDTH;
}
public double getELEMENT_HEIGHT() {
return ELEMENT_HEIGHT;
}
public double getCIRCLE_RADIUS() {
return CIRCLE_RADIUS;
}
public int getMinimalNumberOfInputs() {
return minimalNumberOfInputs;
}
public Shape getBody() {
return body;
}
public Line getOutputLine() {
return outputLine;
}
public ArrayList<Line> getInputLines() {
return inputLines;
}
public Text getSymbol() {
return symbol;
}
public double getBodyCorX() {
return bodyCorX;
}
public double getBodyCorY() {
return bodyCorY;
}
public double getCorX() {
return corX;
}
public double getCorY() {
return corY;
}
public double getMouseX() {
return mouseX;
}
public double getMouseY() {
return mouseY;
}
public boolean isDragging() {
return dragging;
}
public Element(){
}
public Element(Circle inversionDesignation, Text symbol, int minimalNumberOfInputs) {
this.minimalNumberOfInputs = minimalNumberOfInputs;
this.body.setStroke(Color.BLACK);
this.body.setStrokeType(StrokeType.INSIDE);
this.body.setStrokeWidth(2.5);
this.output.setFill(Color.BLACK);
this.output.toFront();
this.inversionDesignation = inversionDesignation;
this.symbol = symbol;
this.inputs = new ArrayList<>();
this.inputLines = new ArrayList<>();
this.identifier = this.hashCode();
this.outputLine.setStrokeWidth(2);
this.createStartInputs();
this.configureInputPoints();
this.bindGraphicalElements();
elementMovementEvents();
elementEnteredEvents();
}
private void bindGraphicalElements() {
this.getChildren().add(body);
this.getChildren().add(output);
this.getChildren().add(outputLine);
this.getChildren().addAll(inputs);
this.getChildren().addAll(inputLines);
if (this.symbol != null) {
this.getChildren().add(symbol);
symbol.relocate((ELEMENT_WIDTH / 2) - symbol.getTabSize() / 2, ELEMENT_HEIGHT / 8);
symbol.setFont(new Font("Consolas", 14));
}
if (this.inversionDesignation != null) {
this.getChildren().add(inversionDesignation);
this.inversionDesignation.setStrokeType(StrokeType.INSIDE);
this.inversionDesignation.setStrokeWidth(1);
this.inversionDesignation.setStroke(Color.BLACK);
this.inversionDesignation.relocate((this.bodyCorX + this.ELEMENT_WIDTH) - (this.inversionDesignation.getRadius() + 1), (this.bodyCorY + this.ELEMENT_HEIGHT / 2) - this.inversionDesignation.getRadius());
inversionDesignation.toFront();
}
}
private void addGraphicalElement(Shape shape) {
this.getChildren().add(shape);
}
private void createStartInputs() {
for (int i = 0; i < this.minimalNumberOfInputs; i++) {
inputs.add(new Circle(CIRCLE_RADIUS));
inputs.get(i).setFill(Color.BLACK);
inputs.get(i).toFront();
}
configureInputPoints();
for (int i = 0; i < inputs.size(); i++) {
Line line = new Line(inputs.get(i).getLayoutX() - 15, inputs.get(i).getLayoutY(), inputs.get(i).getLayoutX(), inputs.get(i).getLayoutY());
line.setStrokeWidth(2);
inputLines.add(line);
}
}
private void addNewInput() {
Circle newCircle = new Circle(CIRCLE_RADIUS);
newCircle.setFill(Color.BLACK);
this.inputs.add(newCircle);
this.configureInputPoints();
this.addGraphicalElement(newCircle);
}
private void configureInputPoints() {
this.output.relocate((this.bodyCorX + this.ELEMENT_WIDTH) - (output.getRadius() + 1), (this.bodyCorY + this.ELEMENT_HEIGHT / 2) - output.getRadius());
int distance = (int) ELEMENT_HEIGHT / (inputs.size() + 1); //Растояние между точками входа.
for (int i = 0; i < inputs.size(); i++) {
inputs.get(i).relocate(this.bodyCorX - (CIRCLE_RADIUS - 1), this.bodyCorY + (distance * (i + 1) - CIRCLE_RADIUS));
}
}
private void elementMovementEvents() {
onMousePressedProperty().set(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
mouseX = event.getSceneX();
mouseY = event.getSceneY();
corX = getLayoutX();
corY = getLayoutY();
}
});
onMouseDraggedProperty().set(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
double offsetX = event.getSceneX() - mouseX; //смещение по X
double offsetY = event.getSceneY() - mouseY;
corX += offsetX;
corY += offsetY;
double scaledX = corX;
double scaledY = corY;
setLayoutX(scaledX);
setLayoutY(scaledY);
dragging = true;
mouseX = event.getSceneX();
mouseY = event.getSceneY();
event.consume();
}
});
onMouseClickedProperty().set(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
dragging = false;
}
});
}
private void testEvent() {
this.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
this.addNewInput();
});
}
private void elementEnteredEvents() {
onMouseClickedProperty().set(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
if (event.getTarget() instanceof Circle) {
System.out.println("Circle!");
}
}
});
}
@Override
public Element clone() throws CloneNotSupportedException{
return (Element)super.clone();
}
@Override
public String toString() {
return "Element " + this.hashCode() + ": location = " + this.getLayoutX() + ": output = " + this.minimalNumberOfInputs;
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof Element)) return false;
Element element = (Element) obj;
return element.getBodyCorX() == bodyCorX && element.getBodyCorY() == bodyCorY && element.getBody() == body;
}
}
I am trying to implement a pattern using the following method:
@Override
public Element clone() throws CloneNotSupportedException{
return (Element)super.clone();
}
I have a controller like this:
public class FXMLController {
@FXML
private AnchorPane anchorPane;
@FXML
private AnchorPane workPane;
//prototypes
private Element AndPrototype = new Element(null, new Text("&"), 2);
private Element OrPrototype = new Element(null, new Text("1"), 2);
private Element NotPrototype = new Element(new Circle(5, Color.WHITE), null, 1);
private Element AndNotPrototype = new Element(new Circle(5, Color.WHITE), new Text("&"), 2);
private Element OrNotPrototype = new Element(new Circle(5, Color.WHITE), new Text("1"), 2);
@FXML
public void initialize() {
}
@FXML
private void method() throws CloneNotSupportedException {
workPane.getChildren().add(AndPrototype.clone());
}
}
In this method, I am trying to make a clone and add it to the AnchorPane
@FXML
private void method() throws CloneNotSupportedException {
workPane.getChildren().add(AndPrototype.clone());
}
As a result, when I click on the button, I get an error of the following content:
Exception in thread "JavaFX Application Thread" java.lang.IndexOutOfBoundsException: Index -1 out of bounds for length 8
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:266)
at java.base/java.util.Objects.checkIndex(Objects.java:359)
at java.base/java.util.ArrayList.get(ArrayList.java:427)
at javafx.base/com.sun.javafx.collections.ObservableListWrapper.get(ObservableListWrapper.java:89)
at javafx.base/com.sun.javafx.collections.VetoableListDecorator.get(VetoableListDecorator.java:305)
at javafx.graphics/javafx.scene.Parent.updateCachedBounds(Parent.java:1704)
at javafx.graphics/javafx.scene.Parent.recomputeBounds(Parent.java:1648)
at javafx.graphics/javafx.scene.Parent.doComputeGeomBounds(Parent.java:1501)
at javafx.graphics/javafx.scene.Parent$1.doComputeGeomBounds(Parent.java:115)
at javafx.graphics/com.sun.javafx.scene.ParentHelper.computeGeomBoundsImpl(ParentHelper.java:84)
at javafx.graphics/com.sun.javafx.scene.NodeHelper.computeGeomBounds(NodeHelper.java:115)
at javafx.graphics/javafx.scene.Node.updateGeomBounds(Node.java:3847)
at javafx.graphics/javafx.scene.Node.getGeomBounds(Node.java:3809)
at javafx.graphics/javafx.scene.Node.doComputeLayoutBounds(Node.java:3657)
at javafx.graphics/javafx.scene.Node$1.doComputeLayoutBounds(Node.java:449)
at javafx.graphics/com.sun.javafx.scene.NodeHelper.computeLayoutBoundsImpl(NodeHelper.java:166)
at javafx.graphics/com.sun.javafx.scene.GroupHelper.computeLayoutBoundsImpl(GroupHelper.java:63)
at javafx.graphics/com.sun.javafx.scene.NodeHelper.computeLayoutBounds(NodeHelper.java:106)
at javafx.graphics/javafx.scene.Node$13.computeBounds(Node.java:3509)
at javafx.graphics/javafx.scene.Node$LazyBoundsProperty.get(Node.java:9782)
at javafx.graphics/javafx.scene.Node$LazyBoundsProperty.get(Node.java:9752)
at javafx.graphics/javafx.scene.Node.getLayoutBounds(Node.java:3524)
at javafx.graphics/javafx.scene.layout.AnchorPane.computeWidth(AnchorPane.java:272)
at javafx.graphics/javafx.scene.layout.AnchorPane.computeMinWidth(AnchorPane.java:248)
at javafx.graphics/javafx.scene.Parent.minWidth(Parent.java:1048)
at javafx.graphics/javafx.scene.layout.Region.minWidth(Region.java:1553)
at javafx.graphics/javafx.scene.layout.Region.computeChildPrefAreaWidth(Region.java:2012)
at javafx.graphics/javafx.scene.layout.AnchorPane.computeChildWidth(AnchorPane.java:315)
at javafx.graphics/javafx.scene.layout.AnchorPane.layoutChildren(AnchorPane.java:353)
at javafx.graphics/javafx.scene.Parent.layout(Parent.java:1207)
at javafx.graphics/javafx.scene.Scene.doLayoutPass(Scene.java:576)
at javafx.graphics/javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2476)
at javafx.graphics/com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Toolkit.java:413)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at javafx.graphics/com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:412)
at javafx.graphics/com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:439)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:563)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:543)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:536)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(QuantumToolkit.java:342)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
at java.base/java.lang.Thread.run(Thread.java:831)
I have absolutely no idea what this error may be connected with and in which direction they are moving.