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}