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.objecttools;
014
015import static org.apache.juneau.internal.CollectionUtils.*;
016
017import java.lang.reflect.*;
018import java.util.*;
019
020import org.apache.juneau.*;
021import org.apache.juneau.internal.*;
022
023/**
024 * POJO model viewer.
025 *
026 * <p>
027 *    This class is designed to extract properties from collections of maps or beans.
028 * </p>
029 *
030 * <h5 class='section'>Example:</h5>
031 * <p class='bjava'>
032 *    MyBean[] <jv>arrayOfBeans</jv> = ...;
033 *    ObjectViewer <jv>viewer</jv> = ObjectViewer.<jsm>create</jsm>();
034 *
035 *    <jc>// Returns the 'foo' and 'bar' properties extracted into a list of maps.</jc>
036 *    List&lt;Map&gt; <jv>result</jv> = <jv>viewer</jv>.run(<jv>arrayOfBeans</jv>, <js>"foo,bar"</js>);
037 * </p>
038 * <p>
039 *    The tool can be used against the following data types:
040 * </p>
041 * <ul>
042 *    <li>Arrays/collections of maps or beans.
043 *    <li>Singular maps or beans.
044 * </ul>
045 *
046 * <h5 class='section'>See Also:</h5><ul>
047 *    <li class='link'><a class="doclink" href="../../../../index.html#jm.ObjectTools">Overview &gt; juneau-marshall &gt; Object Tools</a>
048
049 * </ul>
050 */
051@SuppressWarnings({"unchecked","rawtypes"})
052public final class ObjectViewer implements ObjectTool<ViewArgs> {
053
054   //-----------------------------------------------------------------------------------------------------------------
055   // Static
056   //-----------------------------------------------------------------------------------------------------------------
057
058   /**
059    * Default reusable searcher.
060    */
061   public static final ObjectViewer DEFAULT = new ObjectViewer();
062
063   /**
064    * Static creator.
065    *
066    * @return A new {@link ObjectViewer} object.
067    */
068   public static ObjectViewer create() {
069      return new ObjectViewer();
070   }
071
072   //-----------------------------------------------------------------------------------------------------------------
073   // Instance
074   //-----------------------------------------------------------------------------------------------------------------
075
076   /**
077    * Runs this viewer on the specified collection or array of objects.
078    *
079    * @param input The input.  Must be an array or collection.
080    * @param args The view args.  See {@link ViewArgs} for format.
081    * @return The extracted properties from the collection of objects.
082    */
083   public List<Map> run(Object input, String args) {
084      return (List<Map>)run(BeanContext.DEFAULT_SESSION, input, ViewArgs.create(args));
085   }
086
087   /**
088    * Runs this viewer on a singleton object.
089    *
090    * @param input The input.  Must be a singleton object.
091    * @param args The view args.  See {@link ViewArgs} for format.
092    * @return The extracted properties from the object.
093    */
094   public Map runSingle(Object input, String args) {
095      return (Map)run(BeanContext.DEFAULT_SESSION, input, ViewArgs.create(args));
096   }
097
098   @Override /* ObjectTool */
099   public Object run(BeanSession session, Object input, ViewArgs args) {
100
101      if (input == null)
102         return null;
103
104      List<String> view = args.getView();
105      ClassMeta type = session.getClassMetaForObject(input);
106
107      if (type.isBeanMap())
108         return new DelegateBeanMap(((BeanMap)input).getBean(), session).filterKeys(view);
109      if (type.isMap())
110         return new DelegateMap((Map)input, session).filterKeys(view);
111      if (type.isBean())
112         return new DelegateBeanMap(input, session).filterKeys(view);
113
114      ArrayList<Object> l = null;
115
116      if (type.isArray()) {
117         int size = Array.getLength(input);
118         l = list(size);
119         for (int i = 0; i < size; i++)
120            l.add(Array.get(input, i));
121      } else if (type.isCollection()) {
122         Collection c = (Collection)input;
123         l = list(c.size());
124         List<Object> l2 = l;
125         c.forEach(x -> l2.add(x));
126      } else {
127         return input;
128      }
129
130      for (ListIterator li = l.listIterator(); li.hasNext();) {
131         Object o = li.next();
132         ClassMeta cm2 = session.getClassMetaForObject(o);
133
134         if (cm2 == null)
135            o = null;
136         else if (cm2.isBeanMap())
137            o = new DelegateBeanMap(((BeanMap)o).getBean(), session).filterKeys(view);
138         else if (cm2.isMap())
139            o = new DelegateMap((Map)o, session).filterKeys(view);
140         else if (cm2.isBean())
141            o = new DelegateBeanMap(o, session).filterKeys(view);
142
143         li.set(o);
144      }
145
146      return l;
147   }
148}