/* * $Id$ * * Copyright (c) 2004 by Rodney Kinney * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License (LGPL) as published by the Free Software Foundation. * * 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, copies are available * at http://www.opensource.org. */ package VASSAL.tools; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import VASSAL.build.Buildable; import VASSAL.configure.ConfigureTree; import VASSAL.configure.ValidationReport; import VASSAL.configure.ValidityChecker; /** * A class for assigning unique identifiers to objects. Identifiers will be * of the form prefix#, where prefix is specified at initialization and the # * is an increasing digit. Components will have the same ID provided they * are loaded in the same order. * * Unfortunately, this approach is flawed. If a module is edited, saved games * from previous versions can become broken. Worse, two players with different * extensions loaded could have incompatible behavior. * * The preferred way to have unique identifiers is to allow the user to provide * names and use a {@link VASSAL.configure.ValidityChecker} to ensure that the * names are unique. This class provides some support for using this approach * while providing backward compatibility with old saved games and modules. * * Usage: an {@link Identifyable} instance invokes {@link #add}, typically * during the {@link Buildable#build} method. Classes can use the * {@link #getIdentifier} method to look up an identifier for that instance, * and can use {@link #findInstance} to look up a component by id. */ public class UniqueIdManager implements ValidityChecker { private List<Identifyable> instances = new ArrayList<Identifyable>(); private String prefix; public UniqueIdManager(String prefix) { this.prefix = prefix; } public void add(Identifyable i) { i.setId(prefix + instances.size()); instances.add(i); } public void remove(Identifyable i) { int index = instances.indexOf(i); if (index >= 0) { for (int j = index + 1, n = instances.size(); j < n; ++j) { instances.get(j).setId(prefix + (j - 1)); } instances.remove(index); } } /** * Make a best guess for a unique identifier for the target. * Use {@link Identifyable#getConfigureName if non-null, otherwise * use {@link Identifyable#getId * @param target * @return */ public static String getIdentifier(Identifyable target) { String id = target.getConfigureName(); if (id == null || id.length() == 0) { id = target.getId(); } return id; } public Iterator<Identifyable> getAllInstances() { return instances.iterator(); } /** * Return the first instance whose name or id matches the argument * @param id * @return */ public Identifyable findInstance(String id) { if (id != null) { for (Identifyable i : instances) { if (id.equals(i.getConfigureName()) || id.equals(i.getId())) { return i; } } } return null; } /** Ensures that no other instance of the same class has the same name */ public void validate(Buildable target, ValidationReport report) { if (target instanceof Identifyable) { Identifyable iTarget = (Identifyable) target; if (iTarget.getConfigureName() == null || iTarget.getConfigureName().length() == 0) { report.addWarning("A " + ConfigureTree.getConfigureName(target.getClass()) + " has not been given a name"); } else if (instances.contains(iTarget)) { Identifyable compare = null; for (Iterator<Identifyable> i = instances.iterator(); i.hasNext() && compare != iTarget; ) { compare = i.next(); if (compare != iTarget && iTarget.getConfigureName().equals(compare.getConfigureName())) { report.addWarning("More than one " + ConfigureTree.getConfigureName(target.getClass()) + " named " + iTarget.getConfigureName()); break; } } } } } /** * An object with an identifier that can be manipulated by a * {@link UniqueIdManager} */ public static interface Identifyable { void setId(String id); String getId(); String getConfigureName(); // User-assigned name } }