001/* 002 * Copyright 2010-2015 Institut Pasteur. 003 * 004 * This file is part of Icy. 005 * 006 * Icy is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * Icy is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with Icy. If not, see <http://www.gnu.org/licenses/>. 018 */ 019package icy.preferences; 020 021import icy.util.ClassUtil; 022import icy.util.StringUtil; 023import icy.util.XMLUtil; 024 025import java.io.File; 026import java.util.ArrayList; 027import java.util.List; 028 029import org.w3c.dom.Document; 030import org.w3c.dom.Element; 031import org.w3c.dom.Node; 032 033/** 034 * @author Stephane 035 */ 036public class XMLPreferences 037{ 038 public static class XMLPreferencesRoot 039 { 040 private final String filename; 041 private Document doc; 042 043 // cached 044 Element element; 045 XMLPreferences preferences; 046 047 public XMLPreferencesRoot(String filename) 048 { 049 this.filename = filename; 050 051 load(); 052 } 053 054 /** 055 * Load preferences from file 056 */ 057 public void load() 058 { 059 load(filename); 060 } 061 062 /** 063 * Load preferences from file 064 */ 065 public void load(String filename) 066 { 067 try 068 { 069 // get document 070 doc = XMLUtil.loadDocument(new File(filename)); 071 } 072 catch (Throwable t) 073 { 074 System.err.println("Error: " + filename + " preferences file is corrupted, cannot recover settings."); 075 // corrupted XML file 076 doc = null; 077 } 078 079 // create it if not existing 080 if (doc == null) 081 doc = XMLUtil.createDocument(false); 082 083 // create root element 084 element = XMLUtil.createRootElement(doc); 085 // create our root XMLPreference object 086 preferences = new XMLPreferences(this, element); 087 preferences.clean(); 088 } 089 090 /** 091 * Save preferences to file 092 */ 093 public void save() 094 { 095 save(filename); 096 } 097 098 /** 099 * Save preferences to file 100 */ 101 public void save(String filename) 102 { 103 if (doc != null) 104 XMLUtil.saveDocument(doc, new File(filename)); 105 } 106 107 /** 108 * @return the element 109 */ 110 public Element getElement() 111 { 112 return element; 113 } 114 115 /** 116 * @return the preferences 117 */ 118 public XMLPreferences getPreferences() 119 { 120 return preferences; 121 } 122 } 123 124 private final static String TYPE_SECTION = "section"; 125 private final static String TYPE_KEY = "key"; 126 127 private final XMLPreferencesRoot root; 128 private final Element currentElement; 129 130 /** 131 * Use:<br> 132 * <code>new XMLPreferencesRoot(filename).getPreferences()</code><br> 133 * to load preferences from file. 134 */ 135 XMLPreferences(XMLPreferencesRoot root, Element element) 136 { 137 super(); 138 139 this.root = root; 140 currentElement = element; 141 } 142 143 public String absolutePath() 144 { 145 String result = "/" + name(); 146 147 synchronized (root) 148 { 149 Element parent = XMLUtil.getParentElement(currentElement); 150 while ((parent != null) && (parent != root.element)) 151 { 152 result = "/" + XMLUtil.getGenericElementName(parent) + result; 153 parent = XMLUtil.getParentElement(parent); 154 } 155 } 156 157 return result; 158 } 159 160 public String name() 161 { 162 synchronized (root) 163 { 164 return XMLUtil.getGenericElementName(currentElement); 165 } 166 } 167 168 public XMLPreferences getParent() 169 { 170 final Element parent; 171 172 synchronized (root) 173 { 174 parent = XMLUtil.getParentElement(currentElement); 175 } 176 177 if (parent != null) 178 return new XMLPreferences(root, parent); 179 180 return null; 181 } 182 183 public ArrayList<XMLPreferences> getChildren() 184 { 185 final ArrayList<XMLPreferences> result = new ArrayList<XMLPreferences>(); 186 final List<Element> elements; 187 188 synchronized (root) 189 { 190 elements = XMLUtil.getGenericElements(currentElement, TYPE_SECTION); 191 } 192 193 for (Element element : elements) 194 result.add(new XMLPreferences(root, element)); 195 196 return result; 197 } 198 199 public ArrayList<String> childrenNames() 200 { 201 final ArrayList<String> result = new ArrayList<String>(); 202 203 synchronized (root) 204 { 205 for (Element element : XMLUtil.getGenericElements(currentElement, TYPE_SECTION)) 206 result.add(XMLUtil.getGenericElementName(element)); 207 } 208 209 return result; 210 } 211 212 private Element getSection(String name) 213 { 214 if (StringUtil.isEmpty(name)) 215 return currentElement; 216 217 Element element; 218 219 // absolute path 220 if (name.startsWith("/")) 221 element = root.element; 222 else 223 { 224 // we test first current node is still existing 225 if (!exists()) 226 return null; 227 228 element = currentElement; 229 } 230 231 synchronized (root) 232 { 233 for (String subName : name.split("/")) 234 if (!subName.isEmpty()) 235 element = XMLUtil.getGenericElement(element, TYPE_SECTION, subName); 236 } 237 238 return element; 239 } 240 241 private Element setSection(String name) 242 { 243 if (StringUtil.isEmpty(name)) 244 return currentElement; 245 246 Element element; 247 248 // absolute path 249 if (name.startsWith("/")) 250 element = root.element; 251 else 252 { 253 // we test first current node is still existing 254 if (!exists()) 255 return null; 256 257 element = currentElement; 258 } 259 260 synchronized (root) 261 { 262 for (String subName : name.split("/")) 263 if (!subName.isEmpty()) 264 element = XMLUtil.setGenericElement(element, TYPE_SECTION, subName); 265 } 266 267 return element; 268 } 269 270 /** 271 * Return XMLPreferences of specified node.<br> 272 */ 273 public XMLPreferences node(String name) 274 { 275 final Element element = setSection(name); 276 277 if (element != null) 278 return new XMLPreferences(root, element); 279 280 return null; 281 } 282 283 /** 284 * Return XMLPreferences of specified node using class name of specified object.<br> 285 * <code>nodeForClass(object) == node(object.getClass().getName())</code><br> 286 * Ex : <code>nodeForClass("text") == node("java.lang.String")</code> 287 */ 288 public XMLPreferences nodeForClass(Object object) 289 { 290 if (object != null) 291 return node(ClassUtil.getPathFromQualifiedName(object.getClass().getName())); 292 293 return null; 294 } 295 296 /** 297 * Return the {@link XMLPreferences} node as an XML node. 298 */ 299 public Element getXMLNode() 300 { 301 return currentElement; 302 } 303 304 /** 305 * Return true if current node is existing 306 */ 307 public boolean exists() 308 { 309 // root element, always exists 310 if (currentElement == root.element) 311 return true; 312 313 synchronized (root) 314 { 315 // try to reach root from current element 316 Element parent = XMLUtil.getParentElement(currentElement); 317 while (parent != null) 318 { 319 // we reached root so the element still exist 320 if (parent == root.element) 321 return true; 322 323 parent = XMLUtil.getParentElement(parent); 324 } 325 } 326 327 // can't reach root, element is no more existing 328 return false; 329 } 330 331 /** 332 * Return true if specified node exists 333 */ 334 public boolean nodeExists(String name) 335 { 336 return getSection(name) != null; 337 } 338 339 /** 340 * Return true if node for specified object exists.<br> 341 * <code>nodeForClassExists(object) == nodeExists(object.getClass().getName())</code><br> 342 * Ex : <code>nodeForClassExists("text") == nodeExists("java.lang.String")</code> 343 */ 344 public boolean nodeForClassExists(Object object) 345 { 346 if (object != null) 347 return nodeExists(ClassUtil.getPathFromQualifiedName(object.getClass().getName())); 348 349 return false; 350 } 351 352 public ArrayList<String> keys() 353 { 354 final ArrayList<String> result = new ArrayList<String>(); 355 356 synchronized (root) 357 { 358 for (Element element : XMLUtil.getGenericElements(currentElement, TYPE_KEY)) 359 result.add(XMLUtil.getGenericElementName(element)); 360 } 361 362 return result; 363 } 364 365 /** 366 * Remove all non element nodes 367 */ 368 public void clean() 369 { 370 synchronized (root) 371 { 372 final List<Node> nodes = XMLUtil.getChildren(currentElement); 373 374 for (Node node : nodes) 375 { 376 final String nodeName = node.getNodeName(); 377 378 if (!(nodeName.equals(TYPE_KEY) || nodeName.equals(TYPE_SECTION))) 379 XMLUtil.removeNode(currentElement, node); 380 } 381 } 382 } 383 384 /** 385 * Remove all direct children of this node 386 */ 387 public void clear() 388 { 389 synchronized (root) 390 { 391 XMLUtil.removeChildren(currentElement, TYPE_KEY); 392 } 393 } 394 395 /** 396 * Remove specified element 397 */ 398 private void remove(Element element) 399 { 400 if (element != null) 401 { 402 synchronized (root) 403 { 404 final Element parent = XMLUtil.getParentElement(element); 405 406 if (parent != null) 407 XMLUtil.removeNode(parent, element); 408 } 409 } 410 } 411 412 /** 413 * Remove current section 414 */ 415 public void remove() 416 { 417 remove(currentElement); 418 } 419 420 /** 421 * Remove specified section 422 */ 423 public void remove(String name) 424 { 425 remove(getSection(name)); 426 } 427 428 /** 429 * Remove all sections 430 */ 431 public void removeChildren() 432 { 433 synchronized (root) 434 { 435 XMLUtil.removeChildren(currentElement, TYPE_SECTION); 436 } 437 } 438 439 public String get(String key, String def) 440 { 441 synchronized (root) 442 { 443 return XMLUtil.getGenericElementValue(currentElement, TYPE_KEY, key, def); 444 } 445 } 446 447 public boolean getBoolean(String key, boolean def) 448 { 449 synchronized (root) 450 { 451 return XMLUtil.getGenericElementBooleanValue(currentElement, TYPE_KEY, key, def); 452 } 453 } 454 455 public byte[] getBytes(String key, byte[] def) 456 { 457 synchronized (root) 458 { 459 return XMLUtil.getGenericElementBytesValue(currentElement, TYPE_KEY, key, def); 460 } 461 } 462 463 public double getDouble(String key, double def) 464 { 465 synchronized (root) 466 { 467 return XMLUtil.getGenericElementDoubleValue(currentElement, TYPE_KEY, key, def); 468 } 469 } 470 471 public float getFloat(String key, float def) 472 { 473 synchronized (root) 474 { 475 return XMLUtil.getGenericElementFloatValue(currentElement, TYPE_KEY, key, def); 476 } 477 } 478 479 public int getInt(String key, int def) 480 { 481 synchronized (root) 482 { 483 return XMLUtil.getGenericElementIntValue(currentElement, TYPE_KEY, key, def); 484 } 485 } 486 487 public long getLong(String key, long def) 488 { 489 synchronized (root) 490 { 491 return XMLUtil.getGenericElementLongValue(currentElement, TYPE_KEY, key, def); 492 } 493 } 494 495 public void put(String key, String value) 496 { 497 synchronized (root) 498 { 499 XMLUtil.setGenericElementValue(currentElement, TYPE_KEY, key, value); 500 } 501 } 502 503 public void putBoolean(String key, boolean value) 504 { 505 synchronized (root) 506 { 507 XMLUtil.setGenericElementBooleanValue(currentElement, TYPE_KEY, key, value); 508 } 509 } 510 511 public void putBytes(String key, byte[] value) 512 { 513 synchronized (root) 514 { 515 XMLUtil.setGenericElementBytesValue(currentElement, TYPE_KEY, key, value.clone()); 516 } 517 } 518 519 public void putDouble(String key, double value) 520 { 521 synchronized (root) 522 { 523 XMLUtil.setGenericElementDoubleValue(currentElement, TYPE_KEY, key, value); 524 } 525 } 526 527 public void putFloat(String key, float value) 528 { 529 synchronized (root) 530 { 531 XMLUtil.setGenericElementFloatValue(currentElement, TYPE_KEY, key, value); 532 } 533 } 534 535 public void putInt(String key, int value) 536 { 537 synchronized (root) 538 { 539 XMLUtil.setGenericElementIntValue(currentElement, TYPE_KEY, key, value); 540 } 541 } 542 543 public void putLong(String key, long value) 544 { 545 synchronized (root) 546 { 547 XMLUtil.setGenericElementLongValue(currentElement, TYPE_KEY, key, value); 548 } 549 } 550}