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