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.dto.cognos;
014
015import java.util.*;
016
017import org.apache.juneau.*;
018import org.apache.juneau.annotation.*;
019import org.apache.juneau.xml.annotation.*;
020
021/**
022 * Represents a Cognos dataset.
023 *
024 * <p>
025 * When serialized to XML, creates the following construct (example pulled from <code>AddressBookResource</code>):
026 * <p class='bcode w800'>
027 *    <xt>&lt;?xml</xt> <xa>version</xa>=<xs>'1.0'</xs> <xa>encoding</xa>=<xs>'UTF-8'</xs><xt>?&gt;</xt>
028 *    <xt>&lt;c:dataset <xa>xmlns:c</xa>=<xs>'http://developer.cognos.com/schemas/xmldata/1/'</xs>&gt;</xt>
029 *       <xt>&lt;c:metadata&gt;</xt>
030 *          <xt>&lt;c:item</xt> <xa>name</xa>=<xs>'name'</xs> <xa>type</xa>=<xs>'xs:String'</xs>
031 *             <xa>length</xa>=<xs>'255'</xs><xt>/&gt;</xt>
032 *          <xt>&lt;c:item</xt> <xa>name</xa>=<xs>'age'</xs> <xa>type</xa>=<xs>'xs:int'</xs><xt>/&gt;</xt>
033 *          <xt>&lt;c:item</xt> <xa>name</xa>=<xs>'numAddresses'</xs> <xa>type</xa>=<xs>'xs:int'</xs><xt>/&gt;</xt>
034 *       <xt>&lt;/c:metadata&gt;</xt>
035 *       <xt>&lt;c:data&gt;</xt>
036 *          <xt>&lt;c:row&gt;</xt>
037 *             <xt>&lt;c:value&gt;</xt>Barack Obama<xt>&lt;/c:value&gt;</xt>
038 *             <xt>&lt;c:value&gt;</xt>52<xt>&lt;/c:value&gt;</xt>
039 *             <xt>&lt;c:value&gt;</xt>2<xt>&lt;/c:value&gt;</xt>
040 *          <xt>&lt;/c:row&gt;</xt>
041 *          <xt>&lt;c:row&gt;</xt>
042 *             <xt>&lt;c:value&gt;</xt>George Walker Bush<xt>&lt;/c:value&gt;</xt>
043 *             <xt>&lt;c:value&gt;</xt>67<xt>&lt;/c:value&gt;</xt>
044 *             <xt>&lt;c:value&gt;</xt>2<xt>&lt;/c:value&gt;</xt>
045 *          <xt>&lt;/c:row&gt;</xt>
046 *       <xt>&lt;/c:data&gt;</xt>
047 *    <xt>&lt;/c:dataset&gt;</xt>
048 * </p>
049 *
050 * <p>
051 * Only 2-dimensional POJOs (arrays or collections of maps or beans) can be serialized to Cognos.
052 *
053 * <h5 class='section'>Example:</h5>
054 *
055 * The construct shown above is a serialized <code>AddressBook</code> object which is a subclass of
056 * <code>LinkedList&lt;Person&gt;</code>.
057 * The code for generating the XML is as follows...
058 * <p class='bcode w800'>
059 *    Column[] items = {
060 *       <jk>new</jk> Column(<js>"name"</js>, <js>"xs:String"</js>, 255),
061 *       <jk>new</jk> Column(<js>"age"</js>, <js>"xs:int"</js>),
062 *       <jk>new</jk> Column(<js>"numAddresses"</js>, <js>"xs:int"</js>)
063 *          .addPojoSwap(
064 *             <jk>new</jk> PojoSwap&lt;Person,Integer&gt;() {
065 *                <ja>@Override</ja>
066 *                <jk>public</jk> Integer swap(Person p) {
067 *                   <jk>return</jk> p.<jf>addresses</jf>.size();
068 *                }
069 *             }
070 *          )
071 *    };
072 *
073 *    DataSet ds = <jk>new</jk> DataSet(items, <jsf>addressBook</jsf>, BeanContext.<jsf>DEFAULT</jsf>);
074 *
075 *    String xml = XmlSerializer.<jsf>DEFAULT_SQ</jsf>.serialize(ds);
076 * </p>
077 */
078@SuppressWarnings("unchecked")
079@Bean(typeName="dataset", properties="metadata,data")
080public class DataSet {
081
082   private Column[] metaData;
083   private List<Row> data;
084
085   /** Bean constructor. */
086   public DataSet() {}
087
088   /**
089    * Constructor.
090    *
091    * @param columns The meta-data that represents the columns in the dataset.
092    * @param o
093    *    The POJO being serialized to Cognos.
094    *    Must be an array/collection of beans/maps.
095    * @param session The bean session used to convert POJOs to strings.
096    * @throws Exception An error occurred trying to serialize the POJO.
097    */
098   public DataSet(Column[] columns, Object o, BeanSession session) throws Exception {
099      metaData = columns;
100      data = new LinkedList<>();
101      if (o != null) {
102         if (o.getClass().isArray())
103            o = Arrays.asList((Object[])o);
104         if (o instanceof Collection) {
105            Collection<?> c = (Collection<?>)o;
106            for (Object o2 : c) {
107               Row r = new Row();
108               Map<?,?> m = null;
109               if (o2 instanceof Map)
110                  m = (Map<?,?>)o2;
111               else
112                  m = session.toBeanMap(o2);
113               for (Column col : columns) {
114                  Object v;
115                  if (col.pojoSwap != null)
116                     v = col.pojoSwap.swap(session, o2);
117                  else
118                     v = m.get(col.getName());
119                  r.add(v == null ? null : v.toString());
120               }
121               data.add(r);
122            }
123         }
124      }
125   }
126
127   /**
128    * Represents a row of data.
129    *
130    * <p>
131    * When serialized to XML, creates the following construct (example pulled from <code>AddressBookResource</code>):
132    * <p class='bcode w800'>
133    *    <xt>&lt;row&gt;</xt>
134    *       <xt>&lt;value&gt;</xt>Barack Obama<xt>&lt;/value&gt;</xt>
135    *       <xt>&lt;value&gt;</xt>52<xt>&lt;/value&gt;</xt>
136    *       <xt>&lt;value&gt;</xt>2<xt>&lt;/value&gt;</xt>
137    *    <xt>&lt;/row&gt;</xt>
138    * </p>
139    */
140   @Bean(typeName="row")
141   public static final class Row {
142      private List<String> values = new LinkedList<>();
143
144      void add(String value) {
145         values.add(value);
146      }
147
148      /**
149       * Returns the values in this row.
150       *
151       * @return The values in this row.
152       */
153      @Xml(format=XmlFormat.COLLAPSED, childName="value")
154      public List<String> getValues() {
155         return values;
156      }
157   }
158
159
160   //-----------------------------------------------------------------------------------------------------------------
161   // Bean properties
162   //-----------------------------------------------------------------------------------------------------------------
163
164   /**
165    * Bean property getter:  <property>metadata</property>.
166    *
167    * @return The value of the <property>metadata</property> property on this bean, or <jk>null</jk> if it is not set.
168    */
169   @BeanProperty("metadata")
170   public Column[] getMetaData() {
171      return metaData;
172   }
173
174   /**
175    * Bean property setter:  <property>metadata</property>.
176    *
177    * @param metaData The new value for the <property>metadata</property> property on this bean.
178    * @return This object (for method chaining).
179    */
180   @BeanProperty("metadata")
181   public DataSet setMetaData(Column[] metaData) {
182      this.metaData = metaData;
183      return this;
184   }
185
186   /**
187    * Bean property getter:  <property>data</property>.
188    *
189    * @return The value of the <property>data</property> property on this bean, or <jk>null</jk> if it is not set.
190    */
191   @BeanProperty("data")
192   public List<Row> getData() {
193      return data;
194   }
195
196   /**
197    * Bean property setter:  <property>data</property>.
198    *
199    * @param data The new value for the <property>data</property> property on this bean.
200    * @return This object (for method chaining).
201    */
202   @BeanProperty("data")
203   public DataSet setData(List<Row> data) {
204      this.data = data;
205      return this;
206   }
207}