1/* 2 * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25package com.sun.beans.decoder; 26 27import com.sun.beans.finder.ConstructorFinder; 28 29import java.lang.reflect.Array; 30import java.lang.reflect.Constructor; 31 32import java.util.ArrayList; 33import java.util.List; 34 35/** 36 * This class is intended to handle <new> element. 37 * It describes instantiation of the object. 38 * The {@code class} attribute denotes 39 * the name of the class to instantiate. 40 * The inner elements specifies the arguments of the constructor. 41 * For example:<pre> 42 * <new class="java.lang.Long"> 43 * <string>10</string> 44 * </new></pre> 45 * is equivalent to {@code Long.valueOf("10")} in Java code. 46 * <p>The following attributes are supported: 47 * <dl> 48 * <dt>class 49 * <dd>the type of object for instantiation 50 * <dt>id 51 * <dd>the identifier of the variable that is intended to store the result 52 * </dl> 53 * 54 * @since 1.7 55 * 56 * @author Sergey A. Malenkov 57 */ 58class NewElementHandler extends ElementHandler { 59 private List<Object> arguments = new ArrayList<Object>(); 60 private ValueObject value = ValueObjectImpl.VOID; 61 62 private Class<?> type; 63 64 /** 65 * Parses attributes of the element. 66 * The following attributes are supported: 67 * <dl> 68 * <dt>class 69 * <dd>the type of object for instantiation 70 * <dt>id 71 * <dd>the identifier of the variable that is intended to store the result 72 * </dl> 73 * 74 * @param name the attribute name 75 * @param value the attribute value 76 */ 77 @Override 78 public void addAttribute(String name, String value) { 79 if (name.equals("class")) { // NON-NLS: the attribute name 80 this.type = getOwner().findClass(value); 81 } else { 82 super.addAttribute(name, value); 83 } 84 } 85 86 /** 87 * Adds the argument to the list of arguments 88 * that is used to calculate the value of this element. 89 * 90 * @param argument the value of the element that contained in this one 91 */ 92 @Override 93 protected final void addArgument(Object argument) { 94 if (this.arguments == null) { 95 throw new IllegalStateException("Could not add argument to evaluated element"); 96 } 97 this.arguments.add(argument); 98 } 99 100 /** 101 * Returns the context of the method. 102 * The context of the static method is the class object. 103 * The context of the non-static method is the value of the parent element. 104 * 105 * @return the context of the method 106 */ 107 @Override 108 protected final Object getContextBean() { 109 return (this.type != null) 110 ? this.type 111 : super.getContextBean(); 112 } 113 114 /** 115 * Returns the value of this element. 116 * 117 * @return the value of this element 118 */ 119 @Override 120 protected final ValueObject getValueObject() { 121 if (this.arguments != null) { 122 try { 123 this.value = getValueObject(this.type, this.arguments.toArray()); 124 } 125 catch (Exception exception) { 126 getOwner().handleException(exception); 127 } 128 finally { 129 this.arguments = null; 130 } 131 } 132 return this.value; 133 } 134 135 /** 136 * Calculates the value of this element 137 * using the base class and the array of arguments. 138 * By default, it creates an instance of the base class. 139 * This method should be overridden in those handlers 140 * that extend behavior of this element. 141 * 142 * @param type the base class 143 * @param args the array of arguments 144 * @return the value of this element 145 * @throws Exception if calculation is failed 146 */ 147 ValueObject getValueObject(Class<?> type, Object[] args) throws Exception { 148 if (type == null) { 149 throw new IllegalArgumentException("Class name is not set"); 150 } 151 Class<?>[] types = getArgumentTypes(args); 152 Constructor<?> constructor = ConstructorFinder.findConstructor(type, types); 153 if (constructor.isVarArgs()) { 154 args = getArguments(args, constructor.getParameterTypes()); 155 } 156 return ValueObjectImpl.create(constructor.newInstance(args)); 157 } 158 159 /** 160 * Converts the array of arguments to the array of corresponding classes. 161 * If argument is {@code null} the class is {@code null} too. 162 * 163 * @param arguments the array of arguments 164 * @return the array of corresponding classes 165 */ 166 static Class<?>[] getArgumentTypes(Object[] arguments) { 167 Class<?>[] types = new Class<?>[arguments.length]; 168 for (int i = 0; i < arguments.length; i++) { 169 if (arguments[i] != null) { 170 types[i] = arguments[i].getClass(); 171 } 172 } 173 return types; 174 } 175 176 /** 177 * Resolves variable arguments. 178 * 179 * @param arguments the array of arguments 180 * @param types the array of parameter types 181 * @return the resolved array of arguments 182 */ 183 static Object[] getArguments(Object[] arguments, Class<?>[] types) { 184 int index = types.length - 1; 185 if (types.length == arguments.length) { 186 Object argument = arguments[index]; 187 if (argument == null) { 188 return arguments; 189 } 190 Class<?> type = types[index]; 191 if (type.isAssignableFrom(argument.getClass())) { 192 return arguments; 193 } 194 } 195 int length = arguments.length - index; 196 Class<?> type = types[index].getComponentType(); 197 Object array = Array.newInstance(type, length); 198 System.arraycopy(arguments, index, array, 0, length); 199 200 Object[] args = new Object[types.length]; 201 System.arraycopy(arguments, 0, args, 0, index); 202 args[index] = array; 203 return args; 204 } 205} 206