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}