View Javadoc
1   /*
2    * Licensed to The Apereo Foundation under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional
4    * information regarding copyright ownership.
5    *
6    *
7    * The Apereo Foundation licenses this file to you under the Educational
8    * Community License, Version 2.0 (the "License"); you may not use this file
9    * except in compliance with the License. You may obtain a copy of the License
10   * at:
11   *
12   *   http://opensource.org/licenses/ecl2.txt
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
17   * License for the specific language governing permissions and limitations under
18   * the License.
19   *
20   */
21  
22  package org.opencastproject.util;
23  
24  import static org.opencastproject.util.data.Monadics.mlist;
25  
26  import org.opencastproject.util.data.Collections;
27  import org.opencastproject.util.data.Function;
28  import org.opencastproject.util.data.Function2;
29  import org.opencastproject.util.data.Monadics;
30  import org.opencastproject.util.data.Option;
31  import org.opencastproject.util.data.Prelude;
32  
33  import org.json.simple.JSONArray;
34  import org.json.simple.JSONObject;
35  
36  import java.util.ArrayList;
37  import java.util.Date;
38  import java.util.List;
39  
40  /** JSON builder based on json-simple. */
41  public final class Jsons {
42    private Jsons() {
43    }
44  
45    /** Check if a value is not {@link #ZERO_VAL}. */
46    public static final Function<Val, Boolean> notZero = new Function<Val, Boolean>() {
47      @Override public Boolean apply(Val val) {
48        return !ZERO_VAL.equals(val);
49      }
50    };
51  
52    /** Get the value from a property. */
53    public static final Function<Prop, Val> getVal = new Function<Prop, Val>() {
54      @Override public Val apply(Prop prop) {
55        return prop.getVal();
56      }
57    };
58  
59    /** JSON null. */
60    public static final Val NULL = new Val() {
61    };
62  
63    /** Identity for {@link Val values}. */
64    public static final Val ZERO_VAL = new Val() {
65    };
66  
67    /** Identity for {@link Obj objects}. */
68    public static final Obj ZERO_OBJ = obj();
69  
70    /** Identity for {@link Arr arrays}. */
71    public static final Arr ZERO_ARR = arr();
72  
73    public static final class Prop {
74      private final String name;
75      private final Val val;
76  
77      private Prop(String name, Val val) {
78        this.name = name;
79        this.val = val;
80      }
81  
82      public String getName() {
83        return name;
84      }
85  
86      public Val getVal() {
87        return val;
88      }
89    }
90  
91    // sum type
92    public abstract static class Val {
93    }
94  
95    private static final class SVal extends Val {
96      private final Object val;
97  
98      private SVal(Object val) {
99        this.val = val;
100     }
101 
102     public Object getVal() {
103       return val;
104     }
105   }
106 
107   public static final class Obj extends Val {
108     private final List<Prop> props;
109 
110     private Obj(List<Prop> props) {
111       this.props = props;
112     }
113 
114     public List<Prop> getProps() {
115       return props;
116     }
117 
118     public Obj append(Obj o) {
119       if (!ZERO_OBJ.equals(o))
120         return new Obj(Collections.<Prop, List>concat(props, o.getProps()));
121       else
122         return o;
123     }
124 
125     public String toJson() {
126       return Jsons.toJson(this);
127     }
128   }
129 
130   public static final class Arr extends Val {
131     private final List<Val> vals;
132 
133     public Arr(List<Val> vals) {
134       this.vals = vals;
135     }
136 
137     public List<Val> getVals() {
138       return vals;
139     }
140 
141     public String toJson() {
142       return Jsons.toJson(this);
143     }
144   }
145 
146   //
147 
148   public static String toJson(Obj obj) {
149     return toJsonSimple(obj).toString();
150   }
151 
152   public static String toJson(Arr arr) {
153     return toJsonSimple(arr).toString();
154   }
155 
156   private static JSONObject toJsonSimple(Obj obj) {
157     return mlist(obj.getProps()).foldl(new JSONObject(), new Function2<JSONObject, Prop, JSONObject>() {
158       @Override public JSONObject apply(JSONObject jo, Prop prop) {
159         jo.put(prop.getName(), toJsonSimple(prop.getVal()));
160         return jo;
161       }
162     });
163   }
164 
165   private static JSONArray toJsonSimple(Arr arr) {
166     return mlist(arr.getVals()).foldl(new JSONArray(), new Function2<JSONArray, Val, JSONArray>() {
167       @Override public JSONArray apply(JSONArray ja, Val val) {
168         ja.add(toJsonSimple(val));
169         return ja;
170       }
171     });
172   }
173 
174   private static Object toJsonSimple(Val val) {
175     if (val instanceof SVal) {
176       return ((SVal) val).getVal();
177     }
178     if (val instanceof Obj) {
179       return toJsonSimple((Obj) val);
180     }
181     if (val instanceof Arr) {
182       return toJsonSimple((Arr) val);
183     }
184     if (val.equals(NULL)) {
185       return null;
186     }
187     return Prelude.unexhaustiveMatch();
188   }
189 
190   /** Create an object. */
191   public static Obj obj(Prop... ps) {
192     return new Obj(mlist(ps).filter(notZero.o(getVal)).value());
193   }
194 
195   /** Create an array. */
196   public static Arr arr(Val... vs) {
197     return new Arr(mlist(vs).filter(notZero).value());
198   }
199 
200   /** Create an array. */
201   public static Arr arr(List<Val> vs) {
202     return new Arr(mlist(vs).filter(notZero).value());
203   }
204 
205   /** Create an array. */
206   public static Arr arr(Monadics.ListMonadic<Val> vs) {
207     return new Arr(vs.filter(notZero).value());
208   }
209 
210   public static Val v(Number v) {
211     return new SVal(v);
212   }
213 
214   public static Val v(String v) {
215     return new SVal(v);
216   }
217 
218   public static final Function<String, Val> stringVal = new Function<String, Val>() {
219     @Override public Val apply(String s) {
220       return v(s);
221     }
222   };
223 
224   public static Val v(Boolean v) {
225     return new SVal(v);
226   }
227 
228   public static Val v(Date v) {
229     return new SVal(DateTimeSupport.toUTC(v.getTime()));
230   }
231 
232   /** Create a property. */
233   public static Prop p(String key, Val val) {
234     return new Prop(key, val);
235   }
236 
237   /** Create a property. Passing none is like setting {@link #ZERO_VAL} which erases the property. */
238   public static Prop p(String key, Option<Val> val) {
239     return new Prop(key, val.getOrElse(ZERO_VAL));
240   }
241 
242   /** Create a property. Convenience. */
243   public static Prop p(String key, Number value) {
244     return new Prop(key, v(value));
245   }
246 
247   /** Create a property. Convenience. */
248   public static Prop p(String key, String value) {
249     return new Prop(key, v(value));
250   }
251 
252   /** Create a property. Convenience. */
253   public static Prop p(String key, Boolean value) {
254     return new Prop(key, v(value));
255   }
256 
257   /** Merge a list of objects into one (last one wins). */
258   public static Obj append(Obj... os) {
259     final List<Prop> props = mlist(os).foldl(new ArrayList<Prop>(), new Function2<ArrayList<Prop>, Obj, ArrayList<Prop>>() {
260       @Override public ArrayList<Prop> apply(ArrayList<Prop> props, Obj obj) {
261         props.addAll(obj.getProps());
262         return props;
263       }
264     });
265     return new Obj(props);
266   }
267 
268   /** Append a list of arrays into one. */
269   public static Arr append(Arr... as) {
270     final List<Val> vals = mlist(as).foldl(new ArrayList<Val>(), new Function2<ArrayList<Val>, Arr, ArrayList<Val>>() {
271       @Override public ArrayList<Val> apply(ArrayList<Val> vals, Arr arr) {
272         vals.addAll(arr.getVals());
273         return vals;
274       }
275     });
276     return new Arr(vals);
277   }
278 }