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