All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.persistence.queries.AttributeGroup Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2011, 2012 Oracle and/or its affiliates. All rights reserved.
 * This program and the accompanying materials are made available under the 
 * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 
 * which accompanies this distribution. 
 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at 
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * Contributors:
 *     ailitchev - Bug 244124 - Added AttributeGroup base class for nesting 
 ******************************************************************************/
package org.eclipse.persistence.queries;

import java.io.Serializable;
import java.io.StringWriter;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.eclipse.persistence.internal.queries.AttributeItem;
import org.eclipse.persistence.sessions.CopyGroup;

/**
 * Purpose: An AttributeGroup represents a set of mappings and nested
 * AttributeGroups for relationship mappings for an entity type.
 * Responsibilities:
 * 
    *
  • Defines which attributes should be fetched from the database within a * {@link FetchGroup}. *
  • Define which relationship attributes should be populated in a resulting * entity graph within a {@link LoadGroup} *
  • Define which attributes should be copied within a {@link CopyGroup} *
*

* To reference nested attributes a dot ('.') notation is used to reference * related attributes. All attribute names provided are assumed to be correct * until processed against the mappings during usage of the group. * * @see FetchGroup * @see LoadGroup * @see CopyGroup * * @author ailitchev * @since EclipseLink 2.1 */ public class AttributeGroup implements Serializable, Cloneable { /** * Name of the group. This is used in subclasses where the groups are stored * and can be used within a query by name as with FetchGroup. For dynamic * groups the name has no functional value. */ private String name; /** * Specified attributes in the group mapped to their AttributeItems */ protected Map items; public AttributeGroup(String name) { this.name = name; } public AttributeGroup() { this(""); } /** * Subclass may create different types. */ protected AttributeItem newItem(AttributeGroup group, String attrName) { return new AttributeItem(group, attrName); } /** * Subclass may create different types. */ protected AttributeGroup newGroup(String name, AttributeGroup parent) { return new AttributeGroup(name); } public String getName() { return this.name; } public void setName(String name) { this.name = name; } public Set getAttributeNames() { return getItems().keySet(); } // XXX-dclarke: Should this be public? public void setAttributeNames(Set attributeNames) { Iterator it = attributeNames.iterator(); while (it.hasNext()) { this.addAttribute((String) it.next()); } } /** * Indicates whether the group has at least one attribute. */ public boolean hasItems() { return this.items != null && !this.items.isEmpty(); } /** * INTERNAL: */ public Map getItems() { if (this.items == null) { this.items = new HashMap(); } return this.items; } /** * INTERNAL: * Return if the attribute is defined in the group. * Only local attribute names are checked. */ public boolean containsAttributeInternal(String attributeName) { return (this.items != null) && this.items.containsKey(attributeName); } /** * Return if the attribute is defined in the group. */ public boolean containsAttribute(String attributeNameOrPath) { String[] path = convert(attributeNameOrPath); return getItem(path, false) != null; } /** * Add a basic attribute or nested attribute with each String representing * an attribute on the path to what needs to be included in the * AttributeGroup. *

* Example: * group.addAttribute("firstName");
* group.addAttribute("manager.address"); *
* * @param attrPathOrName * A simple attribute, array or attributes forming a path */ public void addAttribute(String attributeNameOrPath) { addAttribute(attributeNameOrPath, null); } /** * Add a basic attribute or nested attribute with each String representing * an attribute on the path to what needs to be included in the * AttributeGroup. *

* Example: * group.addAttribute("firstName", group1);
* group.addAttribute("manager.address", group2); *
* * Note that existing group corresponding to attributeNameOrPath * will be overridden with the passed group. * * @param attrPathOrName * A simple attribute, array or attributes forming a path * @param group - an AttributeGroup to be added. */ public void addAttribute(String attributeNameOrPath, AttributeGroup group) { AttributeItem item = getItem(convert(attributeNameOrPath), true); item.setGroup(group); } /** * Add a set of attributes to the group. */ public void addAttributes(Collection attrOrPaths) { for (String attr : attrOrPaths) { addAttribute(attr); } } /** * Returns AttributeGroup corresponding to the passed (possibly nested) * attribute. */ public AttributeGroup getGroup(String attributeNameOrPath) { AttributeItem item = getItem(convert(attributeNameOrPath), false); if (item != null) { return item.getGroup(); } return null; } /** * INTERNAL: * Lookup the {@link AttributeItem}for the provided attribute name or path. * * @return item or null * @throws IllegalArgumentException if name is not valid attribute name or path */ public AttributeItem getItem(String attributeNameOrPath) { return getItem(convert(attributeNameOrPath), false); } /** * Locate the AttributeGroup where the leaf attribute in the path should be * applied to. * * @param create * indicates if intermediate AttributeGroup required within the * specified path should be created as needed. When checking the * state of the map callers should set this to false to avoid * changing the state unexpectedly */ private AttributeItem getItem(String[] attributePath, boolean create) { AttributeItem item = null; AttributeGroup currentGroup = this; for (int index = 0; index < attributePath.length; index++) { String attrName = attributePath[index]; item = currentGroup.getItems().get(attrName); // Add missing AttributeGroup if (item == null) { // If not creating missing AttributeGroups then return null if (!create) { return null; } item = newItem(currentGroup, attrName); currentGroup.getItems().put(attrName, item); } // Add a AttributeGroup if not at the end of the attributes path if (item.getGroup() == null && index < (attributePath.length - 1)) { if (!create) { return null; } //XXX-dclarke: Converting the attribute[] into a string and then re-parsing it seems odd AttributeGroup newGroup = newGroup(toStringPath(attributePath, index), currentGroup); item.setGroup(newGroup); } currentGroup = item.getGroup(); } return item; } /** * Remove an attribute from the group. */ public void removeAttribute(String attributeNameOrPath) { AttributeItem item = getItem(attributeNameOrPath); if (item != null) { item.getParent().getItems().remove(item.getAttributeName()); } } /** * Return true if this AttributeGroup is a super-set of the passed in * AttributeGroup. */ public boolean isSupersetOf(AttributeGroup anotherGroup) { // TODO: should handle the case when the current group has all // attributes - then its equivalent to null if (anotherGroup == null) { return false; } if (anotherGroup != this) { if (hasItems()) { if (anotherGroup.hasItems()) { Iterator> otherItemEntries = anotherGroup.getItems().entrySet().iterator(); while (otherItemEntries.hasNext()) { Map.Entry otherItemEntry = otherItemEntries.next(); String otherAttributeName = otherItemEntry.getKey(); AttributeItem item = this.items.get(otherAttributeName); if (item == null) { return false; } AttributeGroup group = item.getGroup(); AttributeGroup otherGroup = otherItemEntry.getValue().getGroup(); if (group != null) { if (!group.isSupersetOf(otherGroup)) { return false; } } else { if (otherGroup != null) { return true; } } } return true; } else { return true; } } else { if (anotherGroup.hasItems()) { return false; } else { return true; } } } else { return true; } } /** * Convert a provided name or path which could be a single attributeName, a * single string with dot separated attribute names, or an array of * attribute names defining the path. * * @throws IllegalArgumentException if name is not valid attribute name or path */ protected String[] convert(String... nameOrPath) { if (nameOrPath == null || nameOrPath.length == 0 || (nameOrPath.length == 1 && (nameOrPath[0] == null || nameOrPath[0].length() == 0))) { // TODO - improve error? throw new IllegalArgumentException("Inavlid name or path: " + (nameOrPath.length == 1 ? nameOrPath[0] : null)); } String[] path = nameOrPath; if (nameOrPath.length > 1 || !nameOrPath[0].contains(".")) { path = nameOrPath; } else { if (nameOrPath[0].endsWith(".")) { throw new IllegalArgumentException("Invalid path: " + nameOrPath[0]); } path = nameOrPath[0].split("\\."); } if (path.length == 0) { throw new IllegalArgumentException("Invalid path: " + nameOrPath[0]); } for (int index = 0; index < path.length; index++) { if (path[index] == null || path[index].length() == 0 || !path[index].trim().equals(path[index])) { throw new IllegalArgumentException("Invalid path: " + nameOrPath[0]); } } return path; } @Override public boolean equals(Object obj) { if (this != obj) { if (obj == null) { return false; } AttributeGroup anotherGroup = null; try { anotherGroup = (AttributeGroup) obj; } catch (ClassCastException cce) { return false; } if (hasItems()) { if (anotherGroup.hasItems()) { return getItems().equals(anotherGroup.getItems()); } else { return false; } } else { if (anotherGroup.hasItems()) { return false; } else { return true; } } } else { return true; } } public String toString() { return getClass().getSimpleName() + "(" + getName() + ")" + toStringAdditionalInfo() + "{" + toStringItems() + "}"; } /** * Used by toString to print additional info for derived classes. */ protected String toStringAdditionalInfo() { return ""; } /** * Used by toString to print attribute items. */ protected String toStringItems() { String str = ""; if (this.items != null) { Iterator it = this.items.values().iterator(); boolean isFirst = true; while (it.hasNext()) { if (isFirst) { isFirst = false; } else { str += ", "; } str += it.next().toStringNoClassName(); } } return str; } static protected String toStringPath(String[] attributePath, int position) { StringWriter writer = new StringWriter(); for (int index = 0; index <= position; index++) { writer.write(attributePath[index]); if (index < position) { writer.write("."); } } return writer.toString(); } public boolean isFetchGroup() { return false; } /** * Convert the group to a FetchGroup for usage with queries. */ public FetchGroup toFetchGroup() { if (isFetchGroup()) { return (FetchGroup) this; } FetchGroup fetchGroup = new FetchGroup(getName()); if (this.hasItems()) { Iterator> it = getItems().entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = it.next(); AttributeGroup group = entry.getValue().getGroup(); if (group == null) { fetchGroup.addAttribute(entry.getKey()); } else { fetchGroup.addAttribute(entry.getKey(), group.toFetchGroup()); } } } return fetchGroup; } public boolean isCopyGroup() { return false; } /** * Convert the group to a CopyGroup for usage with the copy() API. */ public CopyGroup toCopyGroup() { if (isCopyGroup()) { return (CopyGroup) this; } CopyGroup copyGroup = new CopyGroup(getName()); copyGroup.cascadeTree(); if (this.hasItems()) { Iterator> it = getItems().entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = it.next(); AttributeGroup group = entry.getValue().getGroup(); if (group == null) { copyGroup.addAttribute(entry.getKey()); } else { copyGroup.addAttribute(entry.getKey(), group.toCopyGroup()); } } } return copyGroup; } public boolean isLoadGroup() { return false; } /** * Convert the group to a LoadGroup for usage with queries. */ public LoadGroup toLoadGroup() { if (this.isLoadGroup()) { return (LoadGroup) this; } LoadGroup loadGroup = new LoadGroup(getName()); if (this.hasItems()) { Iterator> it = getItems().entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = it.next(); AttributeGroup group = entry.getValue().getGroup(); if (group == null) { loadGroup.addAttribute(entry.getKey()); } else { loadGroup.addAttribute(entry.getKey(), group.toLoadGroup()); } } } return loadGroup; } public AttributeGroup clone() { AttributeGroup clone; try { clone = (AttributeGroup) super.clone(); } catch (CloneNotSupportedException ex) { // should never happen throw new InternalError(); } // all attributes and nested groups should be cloned, too clone.items = null; if (hasItems()) { Iterator> it = getItems().entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = it.next(); AttributeGroup group = entry.getValue().getGroup(); if (group != null) { clone.addAttribute(entry.getKey(), group.clone()); } else { clone.addAttribute(entry.getKey()); } } } return clone; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy