/* * (c) Copyright 2010-2011 AgileBirds * * This file is part of OpenFlexo. * * OpenFlexo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenFlexo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>. * */ package org.openflexo.fib.model; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.List; import java.util.Vector; import java.util.logging.Logger; import javax.swing.tree.TreeNode; import org.openflexo.fib.model.FIBPanel.Layout; public abstract class FIBContainer extends FIBComponent { private static final Logger logger = Logger.getLogger(FIBContainer.class.getPackage().getName()); private Vector<FIBComponent> subComponents; public static enum Parameters implements FIBModelAttribute { subComponents; } public FIBContainer() { subComponents = new Vector<FIBComponent>(); } /*public Class getDataClass() { // This was added to return a class when data is defined if (super.getDataClass() == null && getData() != null && getData().isValid()) { return TypeUtils.getBaseClass(getData().getBinding().getAccessedType()); } return super.getDataClass(); }*/ @Override public void updateBindingModel() { super.updateBindingModel(); if (deserializationPerformed) { for (FIBComponent child : getSubComponents()) { child.updateBindingModel(); } } } public Vector<FIBComponent> getSubComponents() { return subComponents; } public void setSubComponents(Vector<FIBComponent> someComponents) { FIBAttributeNotification<Vector<FIBComponent>> notification = requireChange(Parameters.subComponents, someComponents); if (notification != null) { subComponents = someComponents; hasChanged(notification); } } public void addToSubComponents(FIBComponent aComponent) { addToSubComponents(aComponent, null); } public void addToSubComponents(FIBComponent aComponent, ComponentConstraints someConstraints) { addToSubComponents(aComponent, someConstraints, subComponents.size()); } public void addToSubComponents(FIBComponent aComponent, ComponentConstraints someConstraints, int subComponentIndex) { aComponent.setParent(this); if (someConstraints != null) { aComponent.getConstraints().ignoreNotif = true; aComponent.getConstraints().putAll(someConstraints); aComponent.getConstraints().ignoreNotif = false; } if (deserializationPerformed) { updateComponentIndexForInsertionIndex(aComponent, subComponentIndex); } subComponents.add(subComponentIndex, aComponent); if (deserializationPerformed) { reorderComponents(); } if (aComponent instanceof FIBWidget && ((FIBWidget) aComponent).getManageDynamicModel()) { if (deserializationPerformed) { updateBindingModel(); } } setChanged(); notifyObservers(new FIBAddingNotification<FIBComponent>(Parameters.subComponents, aComponent)); } private void updateComponentIndexForInsertionIndex(FIBComponent component, int insertionIndex) { if (subComponents.size() > 0) { FIBComponent previous = null; FIBComponent next = null; if (insertionIndex < subComponents.size()) { next = subComponents.get(insertionIndex); } if (insertionIndex > 0) { previous = subComponents.get(insertionIndex - 1); } if (previous != null) { if (previous.getIndex() == null) { if (component.getIndex() != null && component.getIndex() < 0) { component.setIndex(null); } } else if (previous.getIndex() < 0) { if (component.getIndex() != null && component.getIndex() < previous.getIndex()) { component.setIndex(previous.getIndex()); } } else { if (component.getIndex() == null || component.getIndex() < previous.getIndex()) { component.setIndex(previous.getIndex()); } } } if (next != null) { if (next.getIndex() == null) { if (component.getIndex() != null && component.getIndex() >= 0) { component.setIndex(null); } } else if (next.getIndex() < 0) { if (component.getIndex() == null || component.getIndex() > next.getIndex()) { component.setIndex(next.getIndex()); } } else { if (component.getIndex() != null && component.getIndex() > next.getIndex()) { component.setIndex(next.getIndex()); } } } } } public FIBComponent getSubComponentNamed(String name) { for (FIBComponent c : subComponents) { if (c.getName() != null && c.getName().equals(name)) { return c; } } return null; } public void removeFromSubComponents(FIBComponent aComponent) { removeFromSubComponentsNoNotification(aComponent); setChanged(); notifyObservers(new FIBRemovingNotification<FIBComponent>(Parameters.subComponents, aComponent)); } public void removeFromSubComponentsNoNotification(FIBComponent aComponent) { aComponent.setParent(null); subComponents.remove(aComponent); } public void notifyComponentMoved(FIBComponent aComponent) { setChanged(); notifyObservers(new FIBAttributeNotification<FIBComponent>(Parameters.subComponents, aComponent)); } @Override public Enumeration<FIBComponent> children() { return getSubComponents().elements(); } @Override public boolean getAllowsChildren() { return true; } @Override public FIBComponent getChildAt(int childIndex) { return getSubComponents().get(childIndex); } @Override public int getChildCount() { return getSubComponents().size(); } @Override public int getIndex(TreeNode node) { return getSubComponents().indexOf(node); } @Override public boolean isLeaf() { return getSubComponents().size() == 0; } // public static final String INHERITED = "Inherited"; public void append(FIBContainer container) { // logger.info(toString()+" append "+container); // if (this instanceof FIBTab && ()) List<FIBComponent> mergedComponents = new ArrayList<FIBComponent>(); for (int i = container.getSubComponents().size() - 1; i >= 0; i--) { FIBComponent c2 = container.getSubComponents().get(i); if (c2.getName() != null && c2 instanceof FIBContainer) { for (FIBComponent c1 : getSubComponents()) { if (c2.getName().equals(c1.getName()) && c1 instanceof FIBContainer) { ((FIBContainer) c1).append((FIBContainer) c2); mergedComponents.add(c2); logger.fine("Merged " + c1 + " and " + c2); break; } } } } for (int i = 0; i < container.getSubComponents().size(); i++) { FIBComponent child = container.getSubComponents().get(i); if (mergedComponents.contains(child)) { continue; } // Is there a component already named same as the one to be added ? // (In this case, do NOT add it, the redefinition override parent behaviour) FIBComponent overridingComponent = getSubComponentNamed(child.getName()); if (overridingComponent == null) { /** * Merging Policy: We append the children of the container to this.subComponents 1. If child has no index, we insert it * after all subcomponents with a negative index 2. If child has a negative index, we insert before any subcomponent with a * null or positive index, or a negative index that is equal or greater than the child index 3. If the child has a positive * index, we insert after all subcomponents with a negative or null index, or a positive index which is smaller or equal to * the child index * * Moreover, when inserting, we always verify that we are not inserting ourselves in a consecutive series of indexed * components. Finally, when we insert the child, we also insert all the consecutive indexed components (two components with * a null index are considered to be consecutive) */ int indexInsertion; if (child.getIndex() == null) { indexInsertion = getSubComponents().size(); for (int j = 0; j < getSubComponents().size(); j++) { FIBComponent c = getSubComponents().get(j); if (c.getIndex() == null || c.getIndex() > -1) { indexInsertion = j; break; } } } else if (child.getIndex() < 0) { indexInsertion = 0; for (int j = 0; j < getSubComponents().size(); j++) { FIBComponent c = getSubComponents().get(j); if (c.getIndex() == null || c.getIndex() >= child.getIndex()) { // We have found where to insert indexInsertion = j; if (c.getIndex() != null && c.getIndex() < 0 && j > 0) { // This is a complex case FIBComponent previousComponent = getSubComponents().get(j - 1); // If the component that is just before the insertion point has an index which is right before the current // component one, then we need to skip all the consecutives indexed component. if (previousComponent.getIndex() != null && previousComponent.getIndex() + 1 == c.getIndex()) { int previous = c.getIndex(); j++; while (j < getSubComponents().size()) { c = getSubComponents().get(j); if (c.getIndex() != null && c.getIndex() == previous + 1) { previous = c.getIndex(); j++; } else { break; } } indexInsertion = j; break; } } break; } } } else { indexInsertion = getSubComponents().size(); for (int j = 0; j < getSubComponents().size(); j++) { FIBComponent c = getSubComponents().get(j); if (c.getIndex() != null && c.getIndex() > -1 && c.getIndex() >= child.getIndex()) { indexInsertion = j; if (j > 0) { // This is a complex case FIBComponent previousComponent = getSubComponents().get(j - 1); // If the component that is just before the insertion point has an index which is right before the current // component one, then we need to skip all the consecutives indexed component. if (previousComponent.getIndex() != null && previousComponent.getIndex() + 1 == c.getIndex()) { int previous = c.getIndex(); j++; while (j < getSubComponents().size()) { c = getSubComponents().get(j); if (c.getIndex() != null && c.getIndex() == previous + 1) { previous = c.getIndex(); j++; } else { break; } } indexInsertion = j; break; } } break; } } } boolean insert = true; Integer startIndex = child.getIndex(); while (insert) { child.setParent(this); subComponents.add(indexInsertion, child); indexInsertion++; if (i + 1 < container.getSubComponents().size()) { Integer previousInteger = child.getIndex(); child = container.getSubComponents().get(i + 1); insert = previousInteger == null && child.getIndex() == null || previousInteger != null && child.getIndex() != null && previousInteger + 1 == child.getIndex() || child.getIndex() != null && (startIndex == null && child.getIndex() == startIndex || startIndex != null && startIndex.equals(child.getIndex())) && !mergedComponents.contains(child); if (insert) { i++; overridingComponent = getSubComponentNamed(child.getName()); if (overridingComponent != null) { insert = false; if (overridingComponent.getParameter("hidden") != null && overridingComponent.getParameter("hidden").equalsIgnoreCase("true")) { removeFromSubComponents(overridingComponent); } } } else { break; } } else { break; } } } else { if (overridingComponent.getParameter("hidden") != null && overridingComponent.getParameter("hidden").equalsIgnoreCase("true")) { // Super property must be shadowed removeFromSubComponents(overridingComponent); } } } if (container.getLocalizedDictionary() != null) { retrieveFIBLocalizedDictionary().append(container.getLocalizedDictionary()); } updateBindingModel(); for (FIBComponent c : subComponents) { recursivelyFinalizeDeserialization(c); } finalizeDeserialization(); } private void recursivelyFinalizeDeserialization(FIBComponent c) { c.finalizeDeserialization(); if (c instanceof FIBContainer) { for (FIBComponent c2 : ((FIBContainer) c).getSubComponents()) { recursivelyFinalizeDeserialization(c2); } } } // Default layout is built-in: only FIBPanel manage a custom layout, // where this method is overriden public Layout getLayout() { return null; } // Not permitted since default layout is built-in: only FIBPanel // manage a custom layout, where this method is overriden public void setLayout(Layout layout) { } public void componentFirst(FIBComponent c) { if (c == null) { return; } subComponents.remove(c); updateComponentIndexForInsertionIndex(c, 0); subComponents.insertElementAt(c, 0); notifyComponentIndexChanged(c); } public void componentUp(FIBComponent c) { if (c == null) { return; } int index = subComponents.indexOf(c); if (index > 0) { subComponents.remove(c); updateComponentIndexForInsertionIndex(c, index - 1); subComponents.insertElementAt(c, index - 1); notifyComponentIndexChanged(c); } } public void componentDown(FIBComponent c) { if (c == null) { return; } int index = subComponents.indexOf(c); if (index < subComponents.size() - 1) { subComponents.remove(c); } updateComponentIndexForInsertionIndex(c, index + 1); subComponents.insertElementAt(c, index + 1); notifyComponentIndexChanged(c); } public void componentLast(FIBComponent c) { if (c == null) { return; } subComponents.remove(c); updateComponentIndexForInsertionIndex(c, subComponents.size()); subComponents.add(c); notifyComponentIndexChanged(c); } private void notifyComponentIndexChanged(FIBComponent component) { FIBAttributeNotification<ComponentConstraints> notification = new FIBAttributeNotification<ComponentConstraints>( FIBComponent.Parameters.constraints, component.getConstraints(), component.getConstraints()); component.notify(notification); setChanged(); notifyObservers(new FIBAttributeNotification<Vector<FIBComponent>>(Parameters.subComponents, subComponents)); } private void notifySubcomponentsIndexChanged() { setChanged(); notifyObservers(new FIBAttributeNotification<Vector<FIBComponent>>(Parameters.subComponents, subComponents)); } @Override public List<? extends FIBModelObject> getEmbeddedObjects() { return getSubComponents(); } public void reorderComponents() { // Rules to sort sub components // 1. Smallest negative index is placed first // 2. All unindexed components (index==null) are then placed // 3. Eventually, all the other components are placed after according to their defined index (0 is considered as a positive index) Collections.sort(subComponents, new Comparator<FIBComponent>() { @Override public int compare(FIBComponent o1, FIBComponent o2) { if (o1.getIndex() == null) { if (o2.getIndex() == null) { return 0; } if (o2.getIndex() < 0) { return 1; } else { return -1; } } else { if (o2.getIndex() == null) { return o1.getIndex(); } else { return o1.getIndex() - o2.getIndex(); } } } }); notifySubcomponentsIndexChanged(); } }