/* * The MIT License * * Copyright 2011 Sony Ericsson Mobile Communications. All rights reserved. * Copyright 2012 Sony Mobile Communications AB. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.sonyericsson.hudson.plugins.metadata.model.values; import com.sonyericsson.hudson.plugins.metadata.model.JsonUtils; import com.sonyericsson.hudson.plugins.metadata.model.Metadata; import com.sonyericsson.hudson.plugins.metadata.model.MetadataContainer; import com.sonyericsson.hudson.plugins.metadata.model.MetadataParent; import com.sonyericsson.hudson.plugins.metadata.model.definitions.MetadataDefinition; import com.sonyericsson.hudson.plugins.metadata.model.definitions.TreeNodeMetadataDefinition; import net.sf.json.JSON; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; import java.util.List; /** * Utility class for handling merge operation inside * {@link com.sonyericsson.hudson.plugins.metadata.model.MetadataParent}s. * * @author Robert Sandell <robert.sandell@sonyericsson.com> */ public final class ParentUtil { /** * Utility constructor. */ private ParentUtil() { } /** * Removes a child from a parent. * * @param parent the parent. * @param child the child to remove. */ public static void removeChild(MetadataParent parent, Metadata child) { if (child == null) { throw new IllegalArgumentException("The child value is null"); } Metadata metadata = parent.getChild(child.getName()); if (metadata != null) { parent.getChildren().remove(metadata); metadata.setParent(null); } } /** * Removes a child from a list of children. * * @param list the list. * @param value the child to remove. */ public static void removeChild(Collection<? extends Metadata> list, Metadata value) { if (value == null) { throw new IllegalArgumentException("The child value is null"); } Metadata metadata = TreeStructureUtil.getLeaf(list, value.getFullPath()); if (metadata != null) { list.remove(metadata); metadata.setParent(null); } } /** * Removes all tree nodes without children from the parent. * * @param parent the parent to start with. */ public static void removeEmptyBranches(MetadataParent parent) { Collection<Metadata> children = parent.getChildren(); removeEmptyBranches(children); } /** * Removes all tree nodes without children from the collection. * * @param collection the collection to remove empty trees from. * @param <T> the Metadata type. */ public static <T extends Metadata> void removeEmptyBranches(Collection<T> collection) { Iterator<T> iterator = collection.iterator(); while (iterator.hasNext()) { Metadata m = iterator.next(); if (m instanceof MetadataParent) { removeEmptyBranches((MetadataParent)m); if (((MetadataParent)m).getChildren().size() == 0) { m.setParent(null); iterator.remove(); } } } } /** * Replaces an existing value amongst the parent's children. * * @param parent the parent to add/replace the child to * @param value the value to replace */ public static void replaceChild(MetadataParent<MetadataValue> parent, MetadataValue value) { if (value == null) { throw new IllegalArgumentException("The child value is null"); } MetadataValue metadata = parent.getChild(value.getName()); if (metadata != null) { if (metadata instanceof MetadataParent && value instanceof MetadataParent) { MetadataParent<MetadataValue> myParent = (MetadataParent<MetadataValue>)metadata; MetadataParent<MetadataValue> valueParent = (MetadataParent<MetadataValue>)value; if (myParent.requiresReplacement() || valueParent.requiresReplacement()) { parent.setChild(parent.indexOf(metadata.getName()), value); value.setParent(parent); value.replacementOf(metadata); metadata.setParent(null); //It should be prepared to be gc'ed } else { replaceChildren(myParent, new ArrayList<MetadataValue>(valueParent.getChildren())); } } else { //it exists! then it is time to replace it. parent.setChild(parent.indexOf(metadata.getName()), value); value.setParent(parent); value.replacementOf(metadata); metadata.setParent(null); //It should be prepared to be gc'ed } } else { //didn't exist lets just add it. parent.getChildren().add(value); value.setParent(parent); } } /** * Adds the children in a list to a parent, replacing any existing children already present. * * @param parent the parent to add to. * @param children the children to add/replace */ public static void replaceChildren(MetadataParent<MetadataValue> parent, List<MetadataValue> children) { for (MetadataValue child : children) { replaceChild(parent, child); } } /** * Adds a value as a child to the parent. Help utility for those who implement {@link * com.sonyericsson.hudson.plugins.metadata.model.MetadataParent#addChild(Metadata)} * * @param parent the parent * @param children the direct list of the parents children. * @param value the value to add. * @param <T> the type for parent, children, value and the return value. * @return the value(s) that failed to be added. */ public static <T extends Metadata> Collection<T> addChildValue(MetadataParent<T> parent, Collection<T> children, T value) { if (value == null) { throw new IllegalArgumentException("The added child value is null"); } T my = parent.getChild(value.getName()); if (my != null) { Collection<T> returnList = null; if (my instanceof MetadataParent && value instanceof MetadataParent) { //they are both a path, let's try to merge as much as possible. Collection<T> subValues = ((MetadataParent)value).getChildren(); Collection<T> leftOvers = ((MetadataParent)my).addChildren(subValues); if (leftOvers != null && !leftOvers.isEmpty()) { //some of the children failed to be merged, return them to sender. Metadata treeNode = null; if (value instanceof MetadataValue) { LinkedList<MetadataValue> list = (LinkedList<MetadataValue>)leftOvers; treeNode = new TreeNodeMetadataValue(value.getName(), value.getDescription(), list, value.isExposedToEnvironment()); } else if (value instanceof MetadataDefinition) { LinkedList<MetadataDefinition> list = (LinkedList<MetadataDefinition>)leftOvers; treeNode = new TreeNodeMetadataDefinition(value.getName(), value.getDescription(), list, false); } returnList = new LinkedList<T>(); returnList.add((T)treeNode); return returnList; } } else { //one or both of them is not a parent, so we fail. returnList = new LinkedList<T>(); returnList.add(value); } return returnList; } else { children.add(value); value.setParent(parent); return null; } } /** * Adds the values as children to the parent. Help utility for those who implement {@link * com.sonyericsson.hudson.plugins.metadata.model.MetadataParent# * addChild(com.sonyericsson.hudson.plugins.metadata.model.Metadata)} * * @param parent the parent to add the values to * @param children the direct list of the parents children. * @param values the values to add. * @param <T> the type for parent, children, values and the return value. * @return the values that failed to be added. */ public static <T extends Metadata> Collection<T> addChildValues(MetadataParent parent, Collection<T> children, Collection<T> values) { List<T> leftovers = new LinkedList<T>(); for (T value : values) { Collection<T> returned = addChildValue(parent, children, value); if (returned != null) { leftovers.addAll(returned); } } if (leftovers.isEmpty()) { return null; } else { return leftovers; } } /** * Utility method for {@link com.sonyericsson.hudson.plugins.metadata.model.MetadataParent#getChild(String)}. * * @param values the list of children. * @param name the name to search. * @param <T> the type for values, name and the return value. * @return the child if found or null if not. */ public static <T extends Metadata> T getChildValue(Collection<T> values, String name) { for (T value : values) { if (value.getName().equalsIgnoreCase(name)) { return value; } } return null; } /** * Utility method for {@link com.sonyericsson.hudson.plugins.metadata.model.MetadataParent#indexOf(String)} }. * * @param children the list of children. * @param name the name to search. * @param <T> the type for values, name and the return value. * @return the index of the child if found or -1 if not. */ public static <T extends Metadata> int getChildIndex(List<T> children, String name) { for (int i = 0; i < children.size(); i++) { if (children.get(i).getName().equalsIgnoreCase(name)) { return i; } } return -1; } /** * Converts the container into a JSON object. This processing is different from {@link * com.sonyericsson.hudson.plugins.metadata.model.values.MetadataValue#toJson()} because it will only convert the * children not the entire object, since a container (like * {@link com.sonyericsson.hudson.plugins.metadata.model.MetadataJobProperty}) * in essence doesn't have a name. * * @param container the container * @return the JSON representation. */ public static JSON toJson(MetadataContainer<MetadataValue> container) { return JsonUtils.toJson(container.getChildren()); } /** * Gets the child names of the given parent. * * @param parent the parent to get the child names from. * @return the child names. */ public static Collection<String> getChildNames(MetadataParent<MetadataValue> parent) { Collection<String> childNames = new LinkedList<String>(); Collection<MetadataValue> children = parent.getChildren(); if (children != null) { for (MetadataValue value : children) { childNames.add(value.getName()); } } return childNames; } }