/* * $Id$ * This file is a part of the Arakhne Foundation Classes, http://www.arakhne.org/afc * * Copyright (c) 2000-2012 Stephane GALLAND. * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports, * Universite de Technologie de Belfort-Montbeliard. * Copyright (c) 2013-2016 The original authors, and other authors. * * Licensed 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.arakhne.afc.math.geometry.d2.i; import java.util.AbstractList; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.eclipse.xtext.xbase.lib.Pure; import org.arakhne.afc.math.geometry.d2.ai.MultiShape2ai; import org.arakhne.afc.vmutil.asserts.AssertMessages; /** Container for grouping of shapes. * * <p>The coordinates of the shapes inside the multishape are global. They are not relative to the multishape. * * <p>Caution: The multishape does not detect the bound change of the stored shapes. * * @param <T> the type of the shapes inside the multishape. * @author $Author: tpiotrow$ * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 13.0 */ public class MultiShape2i<T extends Shape2i<?>> extends AbstractShape2i<MultiShape2i<T>> implements MultiShape2ai<Shape2i<?>, MultiShape2i<T>, T, PathElement2i, Point2i, Vector2i, Rectangle2i> { private static final long serialVersionUID = -4727279807601027239L; private List<T> elements = new ListResponseModel(); private Rectangle2i bounds; /** * Construct an empty multishape. */ public MultiShape2i() { // } /** Construct a multishape with shapes inside. * * @param shapes the shapes to add into the multishape. */ public MultiShape2i(@SuppressWarnings("unchecked") T... shapes) { assert shapes != null : AssertMessages.notNullParameter(); addAll(Arrays.asList(shapes)); } /** Construct a multishape with shapes inside. * * @param shapes the shapes to add into the multishape. */ public MultiShape2i(Iterable<? extends T> shapes) { assert shapes != null : AssertMessages.notNullParameter(); for (final T element : shapes) { add(element); } } @SuppressWarnings("unchecked") @Override public MultiShape2i<T> clone() { final MultiShape2i<T> clone = super.clone(); final List<T> clonedList = new ArrayList<>(); for (final T shape : this.elements) { clonedList.add((T) shape.clone()); } clone.elements = clonedList; if (this.bounds != null) { clone.bounds = this.bounds.clone(); } return clone; } @Override public int hashCode() { return this.elements.hashCode(); } @Override public void onBackendDataChange() { this.bounds = null; fireGeometryChange(); } /** Invoked when the geometry of the content has changed. */ protected void onContentGeometryChange() { this.bounds = null; fireGeometryChange(); } @Pure @Override public List<T> getBackendDataList() { return this.elements; } @Pure @Override public Rectangle2i toBoundingBox() { if (this.bounds == null) { this.bounds = getGeomFactory().newBox(); MultiShape2ai.super.toBoundingBox(this.bounds); } return this.bounds; } @Pure @Override public void toBoundingBox(Rectangle2i box) { assert box != null : AssertMessages.notNullParameter(); if (this.bounds == null) { this.bounds = getGeomFactory().newBox(); MultiShape2ai.super.toBoundingBox(this.bounds); } box.set(this.bounds); } @Override public void translate(int dx, int dy) { if (dx != 0 || dy != 0) { final Rectangle2i box = this.bounds; MultiShape2ai.super.translate(dx, dy); if (box != null) { box.translate(dx, dy); this.bounds = box; } fireGeometryChange(); } } /** List responsive model. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 13.0 */ private class ListResponseModel extends AbstractList<T> implements ShapeGeometryChangeListener { private List<T> delegate = new ArrayList<>(); /** Construct an empty model. */ ListResponseModel() { // } @Override public void add(int index, T element) { assert element != null; this.delegate.add(index, element); if (element instanceof AbstractShape2i<?>) { ((AbstractShape2i<?>) element).addShapeGeometryChangeListener(this); } } @Override public T remove(int index) { final T element = this.delegate.remove(index); if (element instanceof AbstractShape2i<?>) { ((AbstractShape2i<?>) element).removeShapeGeometryChangeListener(this); } return element; } @Override public T set(int index, T element) { assert element != null; final T oldElement = this.delegate.set(index, element); if (oldElement instanceof AbstractShape2i<?>) { ((AbstractShape2i<?>) oldElement).removeShapeGeometryChangeListener(this); } if (element instanceof AbstractShape2i<?>) { ((AbstractShape2i<?>) element).addShapeGeometryChangeListener(this); } return oldElement; } @Override public T get(int index) { return this.delegate.get(index); } @Override public int size() { return this.delegate.size(); } @Override public void shapeGeometryChange(Shape2i<?> shape) { onContentGeometryChange(); } @Override public boolean equals(Object obj) { return this.delegate.equals(obj); } @Override public int hashCode() { return this.delegate.hashCode(); } } }