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.marshall; 018 019import static org.apache.juneau.commons.utils.ClassUtils.*; 020import static org.apache.juneau.commons.utils.Shorts.*; 021 022import java.lang.reflect.*; 023import java.util.*; 024 025/** 026 * Represents a map of dictionary type names to bean classes that make up a bean dictionary. 027 * 028 * <p> 029 * In general, this approach for defining dictionary names for classes is used when it's not possible to use the 030 * {@link Marshalled#typeName() @Marshalled(typeName)} annotation. 031 * 032 * <h5 class='section'>Example:</h5> 033 * <p class='bjava'> 034 * <jc>// A bean dictionary map consisting of classes without @Marshalled(typeName) annotations</jc> 035 * <jc>// that require type names to be explicitly specified.</jc> 036 * <jk>public class</jk> MyBeanDictionaryMap <jk>extends</jk> BeanDictionaryMap { 037 * 038 * <jc>// Must provide a no-arg constructor!</jc> 039 * <jk>public</jk> MyBeanDictionaryMap() { 040 * append(<js>"MyBean"</js>, MyBean.<jk>class</jk>); 041 * append(<js>"MyBeanArray"</js>, MyBean[].<jk>class</jk>); 042 * append(<js>"StringArray"</js>, String[].<jk>class</jk>); 043 * append(<js>"String2dArray"</js>, String[][].<jk>class</jk>); 044 * append(<js>"IntArray"</js>, <jk>int</jk>[].<jk>class</jk>); 045 * append(<js>"Int2dArray"</js>, <jk>int</jk>[][].<jk>class</jk>); 046 * append(<js>"LinkedList"</js>, LinkedList.<jk>class</jk>); 047 * append(<js>"TreeMap"</js>, TreeMap.<jk>class</jk>); 048 * append(<js>"LinkedListOfInts"</js>, LinkedList.<jk>class</jk>, Integer.<jk>class</jk>); 049 * append(<js>"LinkedListOfR1"</js>, LinkedList.<jk>class</jk>, R1.<jk>class</jk>); 050 * append(<js>"LinkedListOfCalendar"</js>, LinkedList.<jk>class</jk>, Calendar.<jk>class</jk>); 051 * } 052 * } 053 * 054 * <jc>// Use it in a parser.</jc> 055 * ReaderParser <jv>parser</jv> = JsonParser 056 * .<jsm>create</jsm>() 057 * .dictionary(MyBeanDictionaryMap.<jk>class</jk>) 058 * .build(); 059 * </p> 060 * 061 * <p> 062 * Subclasses must implement a public no-arg constructor so that it can be instantiated by the bean context code. 063 * 064 * 065 * @serial exclude 066 */ 067@SuppressWarnings({ 068 "rawtypes" // Raw types necessary for bean dictionary Map operations 069}) 070public class BeanDictionaryMap extends LinkedHashMap<String,Object> { 071 private static final long serialVersionUID = 1L; 072 073 /** 074 * Constructor. 075 */ 076 protected BeanDictionaryMap() {} 077 078 private void assertValidParameter(Object o) { 079 if (nn(o)) { 080 if (o instanceof Class) 081 return; 082 if (isArray(o)) { 083 for (var i = 0; i < Array.getLength(o); i++) 084 assertValidParameter(Array.get(o, i)); 085 return; 086 } 087 } 088 throw brex("Invalid object type passed to BeanDictionaryMap: '%s'. Only objects of type Class or Object[] containing Class or Object[] objects can be used.", cn(o)); 089 } 090 091 /** 092 * Add a dictionary name mapping for the specified class. 093 * 094 * @param typeName The dictionary name of the class. 095 * @param c The class represented by the dictionary name. 096 * @return This object. 097 */ 098 protected BeanDictionaryMap append(String typeName, Class<?> c) { 099 put(typeName, c); 100 return this; 101 } 102 103 /** 104 * Add a dictionary name mapping for the specified collection class with the specified entry class. 105 * 106 * @param typeName The dictionary name of the class. 107 * @param collectionClass The collection implementation class. 108 * @param entryClass The entry class. Must not be <jk>null</jk>. 109 * @return This object. 110 */ 111 protected BeanDictionaryMap append(String typeName, Class<? extends Collection> collectionClass, Object entryClass) { 112 assertValidParameter(entryClass); 113 put(typeName, a(collectionClass, entryClass)); 114 return this; 115 } 116 117 /** 118 * Add a dictionary name mapping for the specified map class with the specified key and value classes. 119 * 120 * @param typeName The dictionary name of the class. 121 * @param mapClass The map implementation class. 122 * @param keyClass The key class. Must not be <jk>null</jk>. 123 * @param valueClass The value class. Must not be <jk>null</jk>. 124 * @return This object. 125 */ 126 protected BeanDictionaryMap append(String typeName, Class<? extends Map> mapClass, Object keyClass, Object valueClass) { 127 assertValidParameter(keyClass); 128 assertValidParameter(valueClass); 129 put(typeName, a(mapClass, keyClass, valueClass)); 130 return this; 131 } 132}