001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau; 018 019import static org.apache.juneau.common.utils.Utils.*; 020 021import java.lang.reflect.*; 022import java.util.*; 023 024import org.apache.juneau.annotation.*; 025 026/** 027 * Represents a map of dictionary type names to bean classes that make up a bean dictionary. 028 * 029 * <p> 030 * In general, this approach for defining dictionary names for classes is used when it's not possible to use the 031 * {@link Bean#typeName() @Bean(typeName)} annotation. 032 * 033 * <h5 class='section'>Example:</h5> 034 * <p class='bjava'> 035 * <jc>// A bean dictionary map consisting of classes without @Bean(typeName) annotations</jc> 036 * <jc>// that require type names to be explicitly specified.</jc> 037 * <jk>public class</jk> MyBeanDictionaryMap <jk>extends</jk> BeanDictionaryMap { 038 * 039 * <jc>// Must provide a no-arg constructor!</jc> 040 * <jk>public</jk> MyBeanDictionaryMap() { 041 * append(<js>"MyBean"</js>, MyBean.<jk>class</jk>); 042 * append(<js>"MyBeanArray"</js>, MyBean[].<jk>class</jk>); 043 * append(<js>"StringArray"</js>, String[].<jk>class</jk>); 044 * append(<js>"String2dArray"</js>, String[][].<jk>class</jk>); 045 * append(<js>"IntArray"</js>, <jk>int</jk>[].<jk>class</jk>); 046 * append(<js>"Int2dArray"</js>, <jk>int</jk>[][].<jk>class</jk>); 047 * append(<js>"LinkedList"</js>, LinkedList.<jk>class</jk>); 048 * append(<js>"TreeMap"</js>, TreeMap.<jk>class</jk>); 049 * append(<js>"LinkedListOfInts"</js>, LinkedList.<jk>class</jk>, Integer.<jk>class</jk>); 050 * append(<js>"LinkedListOfR1"</js>, LinkedList.<jk>class</jk>, R1.<jk>class</jk>); 051 * append(<js>"LinkedListOfCalendar"</js>, LinkedList.<jk>class</jk>, Calendar.<jk>class</jk>); 052 * } 053 * } 054 * 055 * <jc>// Use it in a parser.</jc> 056 * ReaderParser <jv>parser</jv> = JsonParser 057 * .<jsm>create</jsm>() 058 * .dictionary(MyBeanDictionaryMap.<jk>class</jk>) 059 * .build(); 060 * </p> 061 * 062 * <p> 063 * Subclasses must implement a public no-arg constructor so that it can be instantiated by the bean context code. 064 * 065 * <h5 class='section'>See Also:</h5><ul> 066 * </ul> 067 * 068 * @serial exclude 069 */ 070@SuppressWarnings("rawtypes") 071public class BeanDictionaryMap extends LinkedHashMap<String,Object> { 072 private static final long serialVersionUID = 1L; 073 074 /** 075 * Constructor. 076 */ 077 protected BeanDictionaryMap() {} 078 079 /** 080 * Add a dictionary name mapping for the specified class. 081 * 082 * @param typeName The dictionary name of the class. 083 * @param c The class represented by the dictionary name. 084 * @return This object. 085 */ 086 protected BeanDictionaryMap append(String typeName, Class<?> c) { 087 put(typeName, c); 088 return this; 089 } 090 091 /** 092 * Add a dictionary name mapping for the specified map class with the specified key and value classes. 093 * 094 * @param typeName The dictionary name of the class. 095 * @param mapClass The map implementation class. 096 * @param keyClass The key class. 097 * @param valueClass The value class. 098 * @return This object. 099 */ 100 protected BeanDictionaryMap append(String typeName, Class<? extends Map> mapClass, Object keyClass, Object valueClass) { 101 assertValidParameter(keyClass); 102 assertValidParameter(valueClass); 103 put(typeName, new Object[]{mapClass, keyClass, valueClass}); 104 return this; 105 } 106 107 /** 108 * Add a dictionary name mapping for the specified collection class with the specified entry class. 109 * 110 * @param typeName The dictionary name of the class. 111 * @param collectionClass The collection implementation class. 112 * @param entryClass The entry class. 113 * @return This object. 114 */ 115 protected BeanDictionaryMap append(String typeName, Class<? extends Collection> collectionClass, Object entryClass) { 116 assertValidParameter(entryClass); 117 put(typeName, new Object[]{collectionClass, entryClass}); 118 return this; 119 } 120 121 private void assertValidParameter(Object o) { 122 if (o != null) { 123 if (o instanceof Class) 124 return; 125 if (isArray(o)) { 126 for (int i = 0; i < Array.getLength(o); i++) 127 assertValidParameter(Array.get(o, i)); 128 return; 129 } 130 } 131 throw new BeanRuntimeException("Invalid object type passed to BeanDictionaryMap: ''{0}''. Only objects of type Class or Object[] containing Class or Object[] objects can be used."); 132 } 133}