JniHdf.java revision 56ed4167b942ec265f9cee70ac4d71d10b3835ce
1/*
2 * Copyright (C) 2010 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.clearsilver.jni;
18
19import org.clearsilver.CSFileLoader;
20import org.clearsilver.HDF;
21
22import java.io.IOException;
23import java.util.Calendar;
24import java.util.Date;
25import java.util.TimeZone;
26
27/**
28 * This class is a wrapper around the HDF C API.  Many features of the C API
29 * are not yet exposed through this wrapper.
30 */
31public class JniHdf implements HDF {
32
33  long hdfptr;  // stores the C HDF* pointer
34  JniHdf root; // If this is a child HDF node, points at the root node of
35               // the tree.  For root nodes this is null.  A child node needs
36               // to hold a reference on the root to prevent the root from
37               // being GC-ed.
38
39  static {
40    JNI.loadLibrary();
41  }
42
43  static JniHdf cast(HDF hdf) {
44    if (!(hdf instanceof JniHdf)) {
45      throw new IllegalArgumentException("HDF object not of type JniHdf.  "
46          + "Make sure you use the same ClearsilverFactory to construct all "
47          + "related HDF and CS objects.");
48    }
49    return (JniHdf)hdf;
50  }
51
52  /**
53   * Default public constructor.
54   */
55  public JniHdf() {
56    hdfptr = _init();
57    root = null;
58  }
59
60  protected JniHdf(long hdfptr, JniHdf parent) {
61    this.hdfptr = hdfptr;
62    this.root = (parent.root != null) ? parent.root : parent;
63  }
64
65  /** Constructs an HDF child node.  Used by other methods in this class when
66   * a child node needs to be constructed.
67   */
68  protected JniHdf newHdf(long hdfptr, HDF parent) {
69    return new JniHdf(hdfptr, cast(parent));
70  }
71
72  /** Clean up allocated memory if neccesary. close() allows application
73   *  to force clean up.
74   */
75  public void close() {
76    // Only root nodes have ownership of the C HDF pointer, so only a root
77    // node needs to dealloc hdfptr.dir
78    if (root == null) {
79      if (hdfptr != 0) {
80        _dealloc(hdfptr);
81        hdfptr = 0;
82      }
83    }
84  }
85
86  /** Call close() just in case when deallocating Java object.
87   */
88  protected void finalize() throws Throwable {
89    close();
90    super.finalize();
91  }
92
93  /** Loads the contents of the specified HDF file from disk into the current
94   *  HDF object.  The loaded contents are merged with the existing contents.
95   *  @param filename the name of file to read in and parse.
96   *  @throws java.io.FileNotFoundException if the specified file does not
97   *  exist.
98   *  @throws IOException other problems reading the file.
99   */
100  public boolean readFile(String filename) throws IOException {
101    if (hdfptr == 0) {
102      throw new NullPointerException("HDF is closed.");
103    }
104    return _readFile(hdfptr, filename, fileLoader != null);
105  }
106
107  protected String fileLoad(String filename) throws IOException {
108    if (hdfptr == 0) {
109      throw new NullPointerException("HDF is closed.");
110    }
111    CSFileLoader aFileLoader = fileLoader;
112    if (aFileLoader == null) {
113      throw new NullPointerException("No fileLoader specified.");
114    } else {
115      String result = aFileLoader.load(this, filename);
116      if (result == null) {
117        throw new NullPointerException("CSFileLoader.load() returned null");
118      }
119      return result;
120    }
121  }
122
123  // The optional CS file loader to use to read in files
124  private CSFileLoader fileLoader = null;
125
126  /**
127   * Get the file loader in use, if any.
128   * @return the file loader in use.
129   */
130  public CSFileLoader getFileLoader() {
131    return fileLoader;
132  }
133
134  /**
135   * Set the CS file loader to use
136   * @param fileLoader the file loader that should be used.
137   */
138  public void setFileLoader(CSFileLoader fileLoader) {
139    this.fileLoader = fileLoader;
140  }
141
142  /** Serializes HDF contents to a file (readable by readFile)
143   */
144  public boolean writeFile(String filename) throws IOException {
145    if (hdfptr == 0) {
146      throw new NullPointerException("HDF is closed.");
147    }
148    return _writeFile(hdfptr, filename);
149  }
150
151  /** Parses/loads the contents of the given string as HDF into the current
152   *  HDF object.  The loaded contents are merged with the existing contents.
153   */
154  public boolean readString(String data) {
155    if (hdfptr == 0) {
156      throw new NullPointerException("HDF is closed.");
157    }
158    return _readString(hdfptr, data);
159  }
160
161  /** Serializes HDF contents to a string (readable by readString)
162   */
163  public String writeString() {
164    if (hdfptr == 0) {
165      throw new NullPointerException("HDF is closed.");
166    }
167    return _writeString(hdfptr);
168  }
169
170  /** Retrieves the integer value at the specified path in this HDF node's
171   *  subtree.  If the value does not exist, or cannot be converted to an
172   *  integer, default_value will be returned. */
173  public int getIntValue(String hdfname, int default_value) {
174    if (hdfptr == 0) {
175      throw new NullPointerException("HDF is closed.");
176    }
177    return _getIntValue(hdfptr,hdfname,default_value);
178  }
179
180  /** Retrieves the value at the specified path in this HDF node's subtree.
181   */
182  public String getValue(String hdfname, String default_value) {
183    if (hdfptr == 0) {
184      throw new NullPointerException("HDF is closed.");
185    }
186    return _getValue(hdfptr,hdfname,default_value);
187  }
188
189  /** Sets the value at the specified path in this HDF node's subtree. */
190  public void setValue(String hdfname, String value) {
191    if (hdfptr == 0) {
192      throw new NullPointerException("HDF is closed.");
193    }
194    _setValue(hdfptr,hdfname,value);
195  }
196
197  /** Remove the specified subtree. */
198  public void removeTree(String hdfname) {
199    if (hdfptr == 0) {
200      throw new NullPointerException("HDF is closed.");
201    }
202    _removeTree(hdfptr,hdfname);
203  }
204
205  /** Links the src hdf name to the dest. */
206  public void setSymLink(String hdf_name_src, String hdf_name_dest) {
207    if (hdfptr == 0) {
208      throw new NullPointerException("HDF is closed.");
209    }
210    _setSymLink(hdfptr,hdf_name_src,hdf_name_dest);
211  }
212
213  /** Export a date to a clearsilver tree using a specified timezone */
214  public void exportDate(String hdfname, TimeZone timeZone, Date date) {
215    if (hdfptr == 0) {
216      throw new NullPointerException("HDF is closed.");
217    }
218
219    Calendar cal = Calendar.getInstance(timeZone);
220    cal.setTime(date);
221
222    String sec = Integer.toString(cal.get(Calendar.SECOND));
223    setValue(hdfname + ".sec", sec.length() == 1 ? "0" + sec : sec);
224
225    String min = Integer.toString(cal.get(Calendar.MINUTE));
226    setValue(hdfname + ".min", min.length() == 1 ? "0" + min : min);
227
228    setValue(hdfname + ".24hour",
229        Integer.toString(cal.get(Calendar.HOUR_OF_DAY)));
230    // java.util.Calendar uses represents 12 o'clock as 0
231    setValue(hdfname + ".hour",
232        Integer.toString(
233            cal.get(Calendar.HOUR) == 0 ? 12 : cal.get(Calendar.HOUR)));
234    setValue(hdfname + ".am",
235        cal.get(Calendar.AM_PM) == Calendar.AM ? "1" : "0");
236    setValue(hdfname + ".mday",
237        Integer.toString(cal.get(Calendar.DAY_OF_MONTH)));
238    setValue(hdfname + ".mon",
239        Integer.toString(cal.get(Calendar.MONTH)+1));
240    setValue(hdfname + ".year",
241        Integer.toString(cal.get(Calendar.YEAR)));
242    setValue(hdfname + ".2yr",
243        Integer.toString(cal.get(Calendar.YEAR)).substring(2));
244
245    // Java DAY_OF_WEEK puts Sunday .. Saturday as 1 .. 7 respectively
246    // See http://java.sun.com/j2se/1.5.0/docs/api/java/util/Calendar.html#DAY_OF_WEEK
247    // However, C and Python export Sun .. Sat as 0 .. 6, because
248    // POSIX localtime_r produces wday 0 .. 6.  So, adjust.
249    setValue(hdfname + ".wday",
250        Integer.toString(cal.get(Calendar.DAY_OF_WEEK) - 1));
251
252    boolean tzNegative = timeZone.getRawOffset() < 0;
253    int tzAbsolute = java.lang.Math.abs(timeZone.getRawOffset()/1000);
254    String tzHour = Integer.toString(tzAbsolute/3600);
255    String tzMin = Integer.toString(tzAbsolute/60 - (tzAbsolute/3600)*60);
256    String tzString = (tzNegative ? "-" : "+")
257        + (tzHour.length() == 1 ? "0" + tzHour : tzHour)
258        + (tzMin.length() == 1 ? "0" + tzMin : tzMin);
259    setValue(hdfname + ".tzoffset", tzString);
260  }
261
262  /** Export a date to a clearsilver tree using a specified timezone */
263  public void exportDate(String hdfname, String tz, int tt) {
264    if (hdfptr == 0) {
265      throw new NullPointerException("HDF is closed.");
266    }
267
268    TimeZone timeZone = TimeZone.getTimeZone(tz);
269
270    if (timeZone == null) {
271      throw new RuntimeException("Unknown timezone: " + tz);
272    }
273
274    Date date = new Date((long)tt * 1000);
275
276    exportDate(hdfname, timeZone, date);
277  }
278
279  /** Retrieves the HDF object that is the root of the subtree at hdfpath, or
280   *  null if no object exists at that path. */
281  public JniHdf getObj(String hdfpath) {
282    if (hdfptr == 0) {
283      throw new NullPointerException("HDF is closed.");
284    }
285    long obj_ptr = _getObj(hdfptr, hdfpath);
286    if ( obj_ptr == 0 ) {
287      return null;
288    }
289    return newHdf(obj_ptr, this);
290  }
291
292  /** Retrieves the HDF for the first child of the root of the subtree
293   *  at hdfpath, or null if no child exists of that path or if the
294   *  path doesn't exist. */
295  public JniHdf getChild(String hdfpath) {
296    if (hdfptr == 0) {
297      throw new NullPointerException("HDF is closed.");
298    }
299    long obj_ptr = _getChild(hdfptr, hdfpath);
300    if ( obj_ptr == 0 ) {
301      return null;
302    }
303    return newHdf(obj_ptr, this);
304  }
305
306  /** Return the root of the tree where the current node lies.  If the
307   *  current node is the root, return this. */
308  public JniHdf getRootObj() {
309    return root != null ? root : this;
310  }
311
312  public boolean belongsToSameRoot(HDF hdf) {
313    JniHdf jniHdf = cast(hdf);
314    return this.getRootObj() == jniHdf.getRootObj();
315  }
316
317  /** Retrieves the HDF object that is the root of the subtree at
318   *  hdfpath, create the subtree if it doesn't exist */
319  public JniHdf getOrCreateObj(String hdfpath) {
320    if (hdfptr == 0) {
321      throw new NullPointerException("HDF is closed.");
322    }
323    long obj_ptr = _getObj(hdfptr, hdfpath);
324    if ( obj_ptr == 0 ) {
325      // Create a node
326      _setValue(hdfptr, hdfpath, "");
327      obj_ptr = _getObj( hdfptr, hdfpath );
328      if ( obj_ptr == 0 ) {
329        return null;
330      }
331    }
332    return newHdf(obj_ptr, this);
333  }
334
335  /** Returns the name of this HDF node.   The root node has no name, so
336   *  calling this on the root node will return null. */
337  public String objName() {
338    if (hdfptr == 0) {
339      throw new NullPointerException("HDF is closed.");
340    }
341    return _objName(hdfptr);
342  }
343
344  /** Returns the value of this HDF node, or null if this node has no value.
345   *  Every node in the tree can have a value, a child, and a next peer. */
346  public String objValue() {
347    if (hdfptr == 0) {
348      throw new NullPointerException("HDF is closed.");
349    }
350    return _objValue(hdfptr);
351  }
352
353  /** Returns the child of this HDF node, or null if there is no child.
354   *  Use this in conjunction with objNext to walk the HDF tree.  Every node
355   *  in the tree can have a value, a child, and a next peer.
356   */
357  public JniHdf objChild() {
358    if (hdfptr == 0) {
359      throw new NullPointerException("HDF is closed.");
360    }
361    long child_ptr = _objChild(hdfptr);
362    if ( child_ptr == 0 ) {
363      return null;
364    }
365    return newHdf(child_ptr, this);
366  }
367
368  /** Returns the next sibling of this HDF node, or null if there is no next
369   *  sibling.  Use this in conjunction with objChild to walk the HDF tree.
370   *  Every node in the tree can have a value, a child, and a next peer.
371   */
372  public JniHdf objNext() {
373    if (hdfptr == 0) {
374      throw new NullPointerException("HDF is closed.");
375    }
376    long next_ptr = _objNext(hdfptr);
377    if ( next_ptr == 0 ) {
378      return null;
379    }
380    return newHdf(next_ptr, this);
381  }
382
383  public void copy(String hdfpath, HDF src) {
384    JniHdf source = cast(src);
385    if (hdfptr == 0 || source.hdfptr == 0) {
386      throw new NullPointerException("HDF is closed.");
387    }
388    _copy(hdfptr, hdfpath, source.hdfptr);
389  }
390
391  /**
392   * Generates a string representing the content of the HDF tree rooted at
393   * this node.
394   */
395  public String dump() {
396    if (hdfptr == 0) {
397      throw new NullPointerException("HDF is closed.");
398    }
399    return _dump(hdfptr);
400  }
401
402  private static native long _init();
403  private static native void _dealloc(long ptr);
404  private native boolean _readFile(long ptr, String filename, boolean use_cb)
405      throws IOException;
406  private static native boolean _writeFile(long ptr, String filename);
407  private static native boolean _readString(long ptr, String data);
408  private static native String _writeString(long ptr);
409  private static native int _getIntValue(long ptr, String hdfname,
410      int default_value);
411  private static native String _getValue(long ptr, String hdfname,
412      String default_value);
413  private static native void _setValue(long ptr, String hdfname,
414      String hdf_value);
415  private static native void _removeTree(long ptr, String hdfname);
416  private static native void _setSymLink(long ptr, String hdf_name_src,
417      String hdf_name_dest);
418  private static native long _getObj(long ptr, String hdfpath);
419  private static native long _getChild(long ptr, String hdfpath);
420  private static native long _objChild(long ptr);
421  private static native long _objNext(long ptr);
422  private static native String _objName(long ptr);
423  private static native String _objValue(long ptr);
424  private static native void _copy(long destptr, String hdfpath, long srcptr);
425
426  private static native String _dump(long ptr);
427}
428