/** * Copyright (C) 2001-2017 by RapidMiner and the contributors * * Complete list of developers available at our web site: * * http://rapidminer.com * * This program is free software: you can redistribute it and/or modify it under the terms of the * GNU Affero General Public License as published by the Free Software Foundation, either version 3 * 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 * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License along with this program. * If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.example; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; /** * A very basic and simple implementation of the {@link com.rapidminer.example.Attributes} interface * based on a linked list of {@link com.rapidminer.example.AttributeRole}s and simply delivers the * same {@link com.rapidminer.example.AttributeRoleIterator} which might skip either regular or * special attributes. * * @author Ingo Mierswa, Sebastian Land */ public class SimpleAttributes extends AbstractAttributes { private static final long serialVersionUID = 6388263725741578818L; private final List<AttributeRole> attributes; private transient Map<String, AttributeRole> nameToAttributeRoleMap = new HashMap<>(); private transient Map<String, AttributeRole> specialNameToAttributeRoleMap = new HashMap<>(); public SimpleAttributes() { this.attributes = Collections.synchronizedList(new ArrayList<AttributeRole>()); } private SimpleAttributes(SimpleAttributes attributes) { this.attributes = Collections.synchronizedList(new ArrayList<AttributeRole>(attributes.allSize())); for (AttributeRole role : attributes.attributes) { register((AttributeRole) role.clone(), false); } } public Object readResolve() { if (nameToAttributeRoleMap == null) { // in earlier versions we didn't have this map, so set it up here. anyway, the maps are // transient. nameToAttributeRoleMap = new HashMap<>(); specialNameToAttributeRoleMap = new HashMap<>(); } for (AttributeRole attributeRole : attributes) { register(attributeRole, true); } return this; } @Override public Object clone() { return new SimpleAttributes(this); } @Override public boolean equals(Object o) { if (!(o instanceof SimpleAttributes)) { return false; } SimpleAttributes other = (SimpleAttributes) o; return attributes.equals(other.attributes); } @Override public int hashCode() { return attributes.hashCode(); } @Override public Iterator<AttributeRole> allAttributeRoles() { final Iterator<AttributeRole> i = attributes.iterator(); return new Iterator<AttributeRole>() { private AttributeRole current; @Override public boolean hasNext() { return i.hasNext(); } @Override public AttributeRole next() { current = i.next(); return current; } @Override public void remove() { i.remove(); unregister(current, true); } }; } /** * @param onlyMaps * add only to maps, not to list. Useful for {@link #readResolve} */ private void register(AttributeRole attributeRole, boolean onlyMaps) { String name = attributeRole.getAttribute().getName(); if (nameToAttributeRoleMap.containsKey(name)) { throw new IllegalArgumentException("Duplicate attribute name: " + name); } String specialName = attributeRole.getSpecialName(); if (specialName != null) { if (specialNameToAttributeRoleMap.containsKey(specialName)) { throw new IllegalArgumentException("Duplicate attribute role: " + specialName); } } this.nameToAttributeRoleMap.put(name, attributeRole); if (specialName != null) { this.specialNameToAttributeRoleMap.put(specialName, attributeRole); } if (!onlyMaps) { this.attributes.add(attributeRole); } attributeRole.addOwner(this); attributeRole.getAttribute().addOwner(this); } /** * * @param onlyMap * if true, removes the attribute only from the maps, but not from the list. this is * useful for the iterator, which has already removed the attribute from the list. */ private boolean unregister(AttributeRole attributeRole, boolean onlyMap) { if (!nameToAttributeRoleMap.containsKey(attributeRole.getAttribute().getName())) { return false; } this.nameToAttributeRoleMap.remove(attributeRole.getAttribute().getName()); if (attributeRole.getSpecialName() != null) { this.specialNameToAttributeRoleMap.remove(attributeRole.getSpecialName()); } if (!onlyMap) { this.attributes.remove(attributeRole); } attributeRole.removeOwner(this); attributeRole.getAttribute().removeOwner(this); return true; } @Override public void rename(AttributeRole attributeRole, String newSpecialName) { if (attributeRole.getSpecialName() != null) { AttributeRole role = specialNameToAttributeRoleMap.get(attributeRole.getSpecialName()); if (role == null) { throw new NoSuchElementException( "Cannot rename attribute role. No such attribute role: " + attributeRole.getSpecialName()); } if (role != attributeRole) { throw new RuntimeException("Broken attribute role map."); } } specialNameToAttributeRoleMap.remove(attributeRole.getSpecialName()); if (newSpecialName != null) { specialNameToAttributeRoleMap.put(newSpecialName, attributeRole); } } @Override public void rename(Attribute attribute, String newName) { if (nameToAttributeRoleMap.containsKey(newName)) { throw new IllegalArgumentException("Cannot rename attribute. Duplicate name: " + newName); } AttributeRole role = nameToAttributeRoleMap.get(attribute.getName()); if (role == null) { throw new NoSuchElementException("Cannot rename attribute. No such attribute: " + attribute.getName()); } if (role.getAttribute() != attribute) { // this cannot happen throw new RuntimeException("Broken attribute map."); } nameToAttributeRoleMap.remove(role.getAttribute().getName()); nameToAttributeRoleMap.put(newName, role); } @Override public void add(AttributeRole attributeRole) { register(attributeRole, false); } @Override public boolean remove(AttributeRole attributeRole) { return unregister(attributeRole, false); } @Override public AttributeRole findRoleByName(String name, boolean caseSensitive) { if (caseSensitive) { return nameToAttributeRoleMap.get(name); } else { String lowerSearchTerm = name.toLowerCase(); for (Entry<String, AttributeRole> entry : nameToAttributeRoleMap.entrySet()) { if (lowerSearchTerm.equals(entry.getKey().toLowerCase())) { return entry.getValue(); } } return null; } } @Override public AttributeRole findRoleBySpecialName(String specialName, boolean caseSensitive) { if (caseSensitive) { return specialNameToAttributeRoleMap.get(specialName); } else { String lowerSearchTerm = specialName.toLowerCase(); for (Entry<String, AttributeRole> entry : specialNameToAttributeRoleMap.entrySet()) { if (lowerSearchTerm.equals(entry.getKey().toLowerCase())) { return entry.getValue(); } } return null; } } @Override public int size() { return nameToAttributeRoleMap.size() - specialNameToAttributeRoleMap.size(); } @Override public int allSize() { return nameToAttributeRoleMap.size(); } @Override public int specialSize() { return specialNameToAttributeRoleMap.size(); } }