/* * This program 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 2 of the License, or * (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * LabelsMetaDataImpl.java * Copyright (C) 2009-2010 Aristotle University of Thessaloniki, Thessaloniki, Greece */ package mulan.data; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; import mulan.core.ArgumentNullException; import mulan.core.MulanRuntimeException; import mulan.core.WekaException; import weka.core.SerializedObject; /** * Implementation of {@link LabelsMetaData} info about labels and their structure. * * @author Jozef Vilcek * @see LabelsMetaData */ @XmlRootElement(name = "labels", namespace = LabelsBuilder.LABELS_SCHEMA_NAMESPACE) @XmlAccessorType(XmlAccessType.NONE) @XmlType(name = "labelsRootType", propOrder = {"rootLabelNodes"}) public class LabelsMetaDataImpl implements LabelsMetaData, Serializable, Externalizable { private static final long serialVersionUID = 5098050799557336378L; private Map<String, LabelNode> allLabelNodes; @XmlElement(type = LabelNodeImpl.class, name = "label", required = true, namespace = LabelsBuilder.LABELS_SCHEMA_NAMESPACE) private Set<LabelNode> rootLabelNodes; /** * Creates a new instance of {@link LabelsMetaDataImpl}. */ public LabelsMetaDataImpl() { allLabelNodes = new HashMap<String, LabelNode>(); rootLabelNodes = new HashSet<LabelNode>(); } /** * Adds a root {@link LabelNode}. The node is assumed to has linked all * related child nodes, if they exists. * The node is added into root set and all child nodes are added into internal mapping. * The node is uniquely identified by the label name. * * @param rootNode the root node with underlying hierarchy of nodes * @throws ArgumentNullException if specified root node is null */ public void addRootNode(LabelNode rootNode) { if (rootNode == null) { throw new ArgumentNullException("rootNode"); } if (rootLabelNodes.contains(rootNode)) { throw new IllegalArgumentException( String.format("The root label node '%s' is already added.", rootNode.getName())); } rootLabelNodes.add(rootNode); processNodeIndex(rootNode, IndexingAction.Add); } public LabelNode getLabelNode(String labelName) { return allLabelNodes.get(labelName); } public Set<String> getLabelNames() { return new HashSet<String>(allLabelNodes.keySet()); } public boolean containsLabel(String labelName) { return allLabelNodes.containsKey(labelName); } public boolean isHierarchy() { return (allLabelNodes.size() == rootLabelNodes.size()) ? false : true; } public int getNumLabels() { return allLabelNodes.size(); } public Set<LabelNode> getRootLabels() { return Collections.unmodifiableSet(rootLabelNodes); } @SuppressWarnings("unchecked") @Override public LabelsMetaData clone() { Set<LabelNode> rootNodes = null; try { SerializedObject obj = new SerializedObject(rootLabelNodes); rootNodes = (Set<LabelNode>) obj.getObject(); } catch (Exception ex) { throw new WekaException("Failed to create copy of 'root label nodes'.", ex); } LabelsMetaDataImpl copyResult = new LabelsMetaDataImpl(); for (LabelNode rootNode : rootNodes) { copyResult.addRootNode(rootNode); } return copyResult; } /** * Removes {@link LabelNode} specified by the name. If there is a hierarchy between * label nodes, whole subtree with all its children is also removed. * * @param labelName the name of {@link LabelNode} to be removed * @return the number of removed nodes */ public int removeLabelNode(String labelName) { if (!allLabelNodes.containsKey(labelName)) { return 0; } LabelNode labelNode = allLabelNodes.get(labelName); int result = processNodeIndex(labelNode, IndexingAction.Remove); if (result > 0 && rootLabelNodes.contains(labelNode)) { rootLabelNodes.remove(labelNode); } return result; } private int processNodeIndex(LabelNode node, IndexingAction action) { int processedNodes = 0; if (node.hasChildren()) { Set<LabelNode> childNodes = node.getChildren(); for (LabelNode child : childNodes) { processedNodes += processNodeIndex(child, action); } } if (action == IndexingAction.Add) { if (allLabelNodes.containsKey(node.getName())) { throw new IllegalArgumentException( String.format("The node '%s' is already in the nodes index.", node.getName())); } allLabelNodes.put(node.getName(), node); processedNodes++; } else if (action == IndexingAction.Remove) { if (allLabelNodes.containsKey(node.getName())) { allLabelNodes.remove(node.getName()); processedNodes++; } } else { throw new MulanRuntimeException( String.format("Indexing action '%s' is not supported.", action)); } return processedNodes; } /** * Will do initialization and indexing of labels structure * Prior motivation of this method to be at disposal to {@link LabelsBuilder}. * When instance creation from XML is finished (via reflection) some additional * processing is required to have API fully operational * (e.g. {@link LabelsMetaDataImpl#getLabelNode(String)}). */ void doReInit() { allLabelNodes.clear(); for (LabelNode node : rootLabelNodes) { processNodeIndex(node, IndexingAction.Add); } } private enum IndexingAction { Add, Remove } @SuppressWarnings("unchecked") @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { rootLabelNodes = (Set<LabelNode>)in.readObject(); doReInit(); } @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(this.rootLabelNodes); } }