Home | History | Annotate | Download | only in jni
      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 
     17 package org.clearsilver.jni;
     18 
     19 import org.clearsilver.CSFileLoader;
     20 import org.clearsilver.HDF;
     21 
     22 import java.io.IOException;
     23 import java.util.Calendar;
     24 import java.util.Date;
     25 import 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  */
     31 public 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