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.html;
014
015import static org.apache.juneau.internal.ClassUtils.*;
016
017import java.util.*;
018
019import org.apache.juneau.*;
020import org.apache.juneau.serializer.*;
021import org.apache.juneau.transform.*;
022
023/**
024 * Context object that lives for the duration of a single serialization of {@link HtmlSchemaDocSerializer} and its subclasses.
025 * 
026 * <p>
027 * This class is NOT thread safe.  It is meant to be discarded after one-time use.
028 */
029public class HtmlSchemaDocSerializerSession extends HtmlDocSerializerSession {
030
031   /**
032    * Create a new session using properties specified in the context.
033    * 
034    * @param ctx
035    *    The context creating this session object.
036    *    The context contains all the configuration settings for this object.
037    * @param args
038    *    Runtime arguments.
039    */
040   protected HtmlSchemaDocSerializerSession(HtmlSchemaDocSerializer ctx, SerializerSessionArgs args) {
041      super(ctx, args);
042   }
043
044   @Override /* SerializerSession */
045   protected void doSerialize(SerializerPipe out, Object o) throws Exception {
046      ObjectMap schema = getSchema(getClassMetaForObject(o), "root", null);
047      super.doSerialize(out, schema);
048   }
049
050   /*
051    * Creates a schema representation of the specified class type.
052    * 
053    * @param eType The class type to get the schema of.
054    * @param ctx Serialize context used to prevent infinite loops.
055    * @param attrName The name of the current attribute.
056    * @return A schema representation of the specified class.
057    * @throws SerializeException If a problem occurred trying to convert the output.
058    */
059   @SuppressWarnings({ "unchecked", "rawtypes" })
060   private ObjectMap getSchema(ClassMeta<?> eType, String attrName, String[] pNames) throws Exception {
061
062      ObjectMap out = new ObjectMap();
063
064      ClassMeta<?> aType;        // The actual type (will be null if recursion occurs)
065      ClassMeta<?> sType;        // The serialized type
066
067      aType = push(attrName, eType, null);
068
069      sType = eType.getSerializedClassMeta(this);
070      String type = null;
071
072      if (sType.isEnum() || sType.isCharSequence() || sType.isChar())
073         type = "string";
074      else if (sType.isNumber())
075         type = "number";
076      else if (sType.isBoolean())
077         type = "boolean";
078      else if (sType.isMapOrBean())
079         type = "object";
080      else if (sType.isCollectionOrArray())
081         type = "array";
082      else
083         type = "any";
084
085      out.put("type", type);
086      out.put("class", eType.toString());
087      PojoSwap t = eType.getPojoSwap(this);
088      if (t != null)
089         out.put("transform", t);
090
091      if (aType != null) {
092         if (sType.isEnum())
093            out.put("enum", getEnumStrings((Class<Enum<?>>)sType.getInnerClass()));
094         else if (sType.isCollectionOrArray()) {
095            ClassMeta componentType = sType.getElementType();
096            if (sType.isCollection() && isParentClass(Set.class, sType.getInnerClass()))
097               out.put("uniqueItems", true);
098            out.put("items", getSchema(componentType, "items", pNames));
099         } else if (sType.isBean()) {
100            ObjectMap properties = new ObjectMap();
101            BeanMeta bm = getBeanMeta(sType.getInnerClass());
102            if (pNames != null)
103               bm = new BeanMetaFiltered(bm, pNames);
104            for (Iterator<BeanPropertyMeta> i = bm.getPropertyMetas().iterator(); i.hasNext();) {
105               BeanPropertyMeta p = i.next();
106               properties.put(p.getName(), getSchema(p.getClassMeta(), p.getName(), p.getProperties()));
107            }
108            out.put("properties", properties);
109         }
110      }
111      pop();
112      return out;
113   }
114
115   @SuppressWarnings({ "unchecked", "rawtypes" })
116   private static List<String> getEnumStrings(Class<? extends Enum> c) {
117      List<String> l = new LinkedList<>();
118      for (Object e : EnumSet.allOf(c))
119         l.add(e.toString());
120      return l;
121   }
122}