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

org.apache.openjpa.kernel.ResultShape Maven / Gradle / Ivy

There is a newer version: 4.0.1
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.openjpa.kernel;

import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Describes the shape of a query result.
 * 
* A shape is described as a Java class by the generic type argument T. A shape may contain zero or more shapes. * A shape is categorized as follows: *
  • A primitive shape can not have child shapes e.g. Foo or float. *
  • A compound shape has zero or more child shapes e.g. Foo{} or Foo{String, int} or * Foo{String,Bar{Double},int}. *
  • A nesting shape has one or more compound child shape(s). * For example, Foo{String,Bar{Double},int}. On the other hand, Foo{String, int} is a compound shape but is not * nesting because all its child shapes are primitive. *
    * A primitive category shape is declared during construction and immutable. * The category of a non-primitive shape is mutable. *
    * Notice that all nested shapes are compound shapes but not all compound shapes are nesting. *
    * A compound shape can add other primitive shapes or nest other shapes to any arbitrary depth. * However, a shape does not allow recursive nesting of shapes. *
    * Usage: * The purpose of a shape is to populate an instance of T from an array of input values where each * array element is further specified with a type and an alias. FillStrategy determines how a shape * populates an instance of T by consuming the input array element values. * The input data is presented as an Object[] with a parallel array of types because the primitive * types (short, float etc.) are not preserved in the input array. For certain FillStrategy such as * MAP or BEAN, the alias of the input array element are used to identify the Map key or setter * methods respectively. * * @author Pinaki Poddar * * @since 2.0.0 * */ @SuppressWarnings("serial") public class ResultShape implements Serializable { private final Class cls; // the type of value this shape represents or populates private final boolean isPrimitive; // flags this shape as primitive private boolean isNesting; // flags this shape as nesting private String alias; private final FillStrategy strategy; // the strategy to populate this shape private final List> children; // children of this shape. null for primitives private Set> parents; // the shapes that have nested this shape /** * Construct a non-primitive shape with ASSIGN or ARRAY fill strategy. */ public ResultShape(Class cls) { this(cls, false); } /** * Construct a primitive or non-primitive shape with ASSIGN or ARRAY fill strategy. * If the shape is declared as primitive then the given class can not be an array. */ public ResultShape(Class cls, boolean primitive) { this(cls, cls.isArray() ? new FillStrategy.Array(cls) : new FillStrategy.Assign(), primitive); if (cls.isArray() && primitive) throw new IllegalArgumentException(cls.getSimpleName() + " can not be primitive shape"); } /** * * Construct a non-primitive shape with the given fill strategy. */ public ResultShape(Class cls, FillStrategy strategy) { this(cls, strategy, false); } /** * Construct a shape with the given fill strategy. * */ public ResultShape(Class cls, FillStrategy strategy, boolean primitive) { if (cls == null) throw new NullPointerException(); this.cls = cls; this.strategy = strategy; isPrimitive = primitive; children = isPrimitive ? null : new ArrayList>(); } // /** // * Construct a shape with the MAP fill strategy to invoke the given method. // * // */ // public ResultShape(Class cls, Method putMethod) { // if (cls == null) throw new NullPointerException(); // this.cls = cls; // this.strategy = new FillStrategy.Map(putMethod); // isPrimitive = true; // children = new ArrayList>(); // } // // /** // * Construct a shape with the CONSTRUCTOR fill strategy to invoke the given constructor. // * // */ // public ResultShape(Class cls, Constructor cons) { // if (cls == null) throw new NullPointerException(); // this.cls = cls; // this.strategy = new FillStrategy.NewInstance(cons); // isPrimitive = false; // children = new ArrayList>(); // } /** * Gets the type of instance populated by this shape. */ public Class getType() { return cls; } public FillStrategy getStrategy() { return strategy; } public ResultShape setAlias(String alias) { this.alias = alias; return this; } public String getAlias() { return alias; } /** * Gets the list of classes to compose this shape and all its children. * For example, a shape Foo{String,Bar{int, Date}, Double} will return * {String, int, Date, Double} */ public List> getCompositeTypes() { List> result = new ArrayList>(); if (isPrimitive() || children.isEmpty()) { result.add(cls); } else { for (ResultShape child : children) { result.addAll(child.getCompositeTypes()); } } return result; } /** * Gets the list of classes to compose this shape only i.e. without expanding the children's shape. * For example, a shape Foo{String,Bar{int, Date}, Double} will return {String, Bar, Double} */ public List> getTypes() { List> result = new ArrayList>(); if (children.isEmpty()) { result.add(cls); } else { for (ResultShape child : children) { result.add(child.getType()); } } return result; } /** * Creates a new shape of type X with the given class arguments and nests * the new shape within this shape. * * @return newly created nested shape */ public ResultShape nest(Class cls, FillStrategy strategy, Class... classes) { assertNotPrimitive(); ResultShape child = new ResultShape(cls, strategy, true); this.nest(child.add(classes)); return child; } /** * Nest the given shape. * * @param shape The given shape can not be a parent of this shape * to prohibit recursive nesting. * * @return this shape itself */ public ResultShape nest(ResultShape shape) { assertNotPrimitive(); if (shape.isParent(this)) throw new IllegalArgumentException(this + " can not nest recursive " + shape); children.add(shape); shape.addParent(this); isNesting |= !shape.isPrimitive(); return this; } /** * Adds the given shape as one of the parents of this shape. * */ private void addParent(ResultShape p) { if (parents == null) parents = new HashSet>(); parents.add(p); } /** * Adds the given classes as child shapes of this shape. * The child shapes are primitive shapes. */ public ResultShape add(Class... classes) { assertNotPrimitive(); for (Class c : classes) { children.add(new ResultShape(c, true)); } return this; } /** * Gets all the child shapes. */ public List> getChildren() { return Collections.unmodifiableList(children); } /** * Affirms if this shape can have child shapes. */ public boolean isCompound() { return !isPrimitive; } /** * Affirms if this shape can not have any child shape. * A primitive shape uses ASSIGN strategy. */ public boolean isPrimitive() { return isPrimitive; } /** * Affirms if at least one child shape of this shape is a compound shape. */ public boolean isNesting() { return isNesting; } /** * Affirms if this shape is nested within other shapes. */ public boolean isNested() { return parents != null; } /** * Affirms if the given shape is a parent (or grandparent) of this shape. */ public boolean isParent(ResultShape p) { if (p.getParents().contains(this)) return true; if (children != null) { for (ResultShape child : children) { if (child.isParent(p)) return true; } } return false; } void assertNotPrimitive() { if (isPrimitive) throw new UnsupportedOperationException("Can not add/nest shape to primitive shape " + this); } /** * Gets the immediate parents of this shape. */ public Set> getParents() { return parents == null ? Collections.EMPTY_SET : Collections.unmodifiableSet(parents); } /** * Total number of arguments required to populate the shape and all its child shapes. */ public int argLength() { if (isPrimitive() || children.isEmpty()) return 1; int l = 0; for (ResultShape child : children) { l += child.argLength(); } return l; } /** * Number of arguments to populate this shape only. */ public int length() { if (isPrimitive() || children.isEmpty()) return 1; return children.size(); } // ====================================================================================== // Data Population Routines // ====================================================================================== /** * Fill this shape and its children with the given array element values. * The parallel arrays contain the actual values, the types of these values and aliases. * The type and alias information are used for packing Map or invoking constructor. * The type can be different from what can be determined from array elements because * of boxing of primitive types. * The actual constructor argument types are sourced from types[] array. */ public T pack(Object[] values, Class[] types, String[] aliases) { if (values.length < argLength()) // input can be longer than required throw new IndexOutOfBoundsException(values.length + " values are less than " + argLength() + " argumenets required to pack " + this); Object[] args = new Object[length()]; Class[] argTypes = new Class[length()]; String[] argAliases = new String[length()]; if (isPrimitive() || children.isEmpty()) { args[0] = values[0]; argTypes[0] = types[0]; argAliases[0] = aliases[0]; } else { // pack each children int start = 0; int i = 0; for (ResultShape rs : children) { int finish = start + rs.argLength(); args[i] = rs.pack(chop(values, start, finish), chop(types, start, finish), chop(aliases, start, finish)); argTypes[i] = rs.getType(); argAliases[0] = rs.getAlias(); start = finish; i++; } } return (T)strategy.fill(args, argTypes, argAliases); } /** * Chop an array from start to finish. */ X[] chop(X[] values, int start, int finish) { X[] result = (X[])Array.newInstance(values.getClass().getComponentType(), finish-start); System.arraycopy(values, start, result, 0, finish-start); return result; } /** * Gets a human-readable representation of this shape. * */ public String toString() { StringBuilder buf = new StringBuilder(cls.getSimpleName()); if (isPrimitive() || children.isEmpty()) return buf.toString(); int i = 0; for (ResultShape child : children) { buf.append(i++ == 0 ? "{" : ", "); buf.append(child); } if (!children.isEmpty()) buf.append("}"); return buf.toString(); } }




  • © 2015 - 2024 Weber Informatics LLC | Privacy Policy