1 // 2 // XPathTable.java 3 // fourjay 4 // 5 // Created by Steven R. Loomis on 20/10/2005. 6 // Copyright 2005-2014 IBM. All rights reserved. 7 // 8 // 9 10 package org.unicode.cldr.web; 11 12 import java.sql.Connection; 13 import java.sql.PreparedStatement; 14 import java.sql.ResultSet; 15 import java.sql.SQLException; 16 import java.sql.Statement; 17 import java.util.Collections; 18 import java.util.HashSet; 19 import java.util.Hashtable; 20 import java.util.Map; 21 import java.util.Set; 22 23 import org.unicode.cldr.icu.LDMLConstants; 24 import org.unicode.cldr.util.CLDRConfig; 25 import org.unicode.cldr.util.CLDRConfig.Environment; 26 import org.unicode.cldr.util.LDMLUtilities; 27 import org.unicode.cldr.util.PrettyPath; 28 import org.unicode.cldr.util.StringId; 29 import org.unicode.cldr.util.XMLSource; 30 import org.unicode.cldr.util.XPathParts; 31 32 import com.ibm.icu.dev.util.ElapsedTimer; 33 import com.ibm.icu.impl.Utility; 34 35 /** 36 * This class maps between full and partial xpaths, and the small integers (xpids) which 37 * are actually stored in the database. It keeps an in-memory cache which is 38 * populated as ids are requested. 39 * 40 * 41 * Definitions: 42 * xpath: an XPath, such as "//ldml/shoeSize" 43 * xpid / int: an integer 'token' value, such as 123. 44 * This is old and deprecated. 45 * Specific to this instance of SurveyTool. 46 * This is usually what is meant by "int xpath" in code or in the database. 47 * strid / hex: a hexadecimal "hash" of the full xpath such as "b1dfb436c841a73". 48 * This is the preferred method of condensing of xpaths. 49 * Note that you can't calculate the xpath from this without a look-up table. 50 * "long" StringID: this is the "long" form of the hex id. Not used within the SurveyTool, some CLDR tools use it. 51 */ 52 public class XPathTable { 53 public static final String CLDR_XPATHS = "cldr_xpaths"; 54 55 private PrettyPath ppath = new PrettyPath(); 56 57 private static final boolean DEBUG = false; 58 59 /** 60 * Called by SM to create the reg 61 * 62 * @param ourConn 63 * the conn to use 64 */ createTable(Connection ourConn)65 public static XPathTable createTable(Connection ourConn) throws SQLException { 66 try { 67 boolean isNew = !DBUtils.hasTable(ourConn, CLDR_XPATHS); 68 XPathTable reg = new XPathTable(); 69 if (isNew) { 70 reg.setupDB(); 71 } 72 reg.loadXPaths(ourConn); 73 74 return reg; 75 } finally { 76 DBUtils.closeDBConnection(ourConn); 77 } 78 } 79 loadXPaths(Connection conn)80 private void loadXPaths(Connection conn) throws SQLException { 81 if (stringToId.size() != 0) { // Only load the entire stringToId map 82 // once. 83 return; 84 } 85 ElapsedTimer et = new ElapsedTimer("XPathTable: load all xpaths"); 86 int ixpaths = 0; 87 PreparedStatement queryStmt = DBUtils.prepareForwardReadOnly(conn, "SELECT id,xpath FROM " + CLDR_XPATHS); 88 // First, try to query it back from the DB. 89 ResultSet rs = queryStmt.executeQuery(); 90 while (rs.next()) { 91 int id = rs.getInt(1); 92 String xpath = Utility.unescape(rs.getString(2)); 93 setById(id, xpath); 94 stat_dbFetch++; 95 ixpaths++; 96 } 97 queryStmt.close(); 98 final boolean hushMessages = CLDRConfig.getInstance().getEnvironment() == Environment.UNITTEST; 99 if (!hushMessages) System.err.println(et + ": " + ixpaths + " loaded"); 100 } 101 102 /** 103 * Called by SM to shutdown 104 * 105 * @deprecated unneeded 106 */ 107 @Deprecated shutdownDB()108 public void shutdownDB() throws SQLException { 109 110 } 111 112 /** 113 * internal - called to setup db 114 */ setupDB()115 private void setupDB() throws SQLException { 116 String sql = null; 117 Connection conn = null; 118 try { 119 conn = DBUtils.getInstance().getDBConnection(); 120 SurveyLog.debug("XPathTable DB: initializing... conn: " + conn + ", db:" + CLDR_XPATHS + ", id:" 121 + DBUtils.DB_SQL_IDENTITY); 122 Statement s = conn.createStatement(); 123 if (s == null) { 124 throw new InternalError("S is null"); 125 } 126 String xpathindex = "xpath"; 127 String uniqueness = ", " + "unique(xpath)"; 128 if (DBUtils.db_Mysql) { 129 uniqueness = ""; 130 xpathindex = "xpath(768)"; 131 } 132 sql = ("create table " + CLDR_XPATHS + "(id INT NOT NULL " + DBUtils.DB_SQL_IDENTITY + ", " + "xpath " 133 + DBUtils.DB_SQL_VARCHARXPATH + DBUtils.DB_SQL_MB4 + " NOT NULL" + uniqueness + ") " + DBUtils.DB_SQL_ENGINE_INNO + DBUtils.DB_SQL_MB4); 134 s.execute(sql); 135 sql = ("CREATE INDEX " + CLDR_XPATHS + "_id on " + CLDR_XPATHS + "(id)"); 136 s.execute(sql); 137 sql = ("CREATE INDEX " + CLDR_XPATHS + "_xpath on " + CLDR_XPATHS + " (" + xpathindex + ")"); 138 s.execute(sql); 139 sql = null; 140 s.close(); 141 conn.commit(); 142 } finally { 143 DBUtils.close(conn); 144 if (sql != null) { 145 SurveyLog.logger.warning("Last SQL: " + sql); 146 } 147 } 148 } 149 150 public Hashtable<String, Integer> stringToId = new Hashtable<>(4096); // public for statistics only 151 public Hashtable<Long, String> sidToString = new Hashtable<>(4096); // public for statistics only 152 statistics()153 public String statistics() { 154 return "DB: " + stat_dbAdd + "add/" + stat_dbFetch + "fetch/" 155 + (stat_dbAdd + stat_dbFetch) + "total." + "-" + idStats(); 156 } 157 158 private static int stat_dbAdd = 0; 159 private static int stat_dbFetch = 0; 160 XPathTable()161 public XPathTable() { 162 } 163 164 /** 165 * SpecialTable implementation 166 */ 167 IntHash<String> xptHash = new IntHash<>(); 168 idToString_put(int id, String str)169 String idToString_put(int id, String str) { 170 return xptHash.put(id, str); 171 } 172 idToString_get(int id)173 String idToString_get(int id) { 174 return xptHash.get(id); 175 } 176 idStats()177 String idStats() { 178 return xptHash.stats(); 179 } 180 181 /** END specialtable implementation */ 182 183 /** 184 * Loads all xpath-id mappings from the database. If there are any xpaths in 185 * the specified XMLSource which are not already in the database, they will 186 * be created here. 187 */ loadXPaths(XMLSource source)188 public synchronized void loadXPaths(XMLSource source) { 189 // Get list of xpaths that aren't already loaded. 190 Set<String> unloadedXpaths = new HashSet<>(); 191 for (String xpath : source) { 192 unloadedXpaths.add(xpath); 193 } 194 unloadedXpaths.removeAll(stringToId.keySet()); 195 196 Connection conn = null; 197 PreparedStatement queryStmt = null; 198 try { 199 conn = DBUtils.getInstance().getDBConnection(); 200 if (!DEBUG) { 201 addXpaths(unloadedXpaths, conn); 202 } else { 203 // Debug: add paths one by one. 204 for (final String path : unloadedXpaths) { 205 try { 206 addXpaths(Collections.singleton(path), conn); 207 } catch (SQLException se) { 208 SurveyLog.logException(se, "While loading xpath: " + Utility.escape(path)); 209 throw se; 210 } 211 } 212 } 213 } catch (SQLException sqe) { 214 SurveyLog.logException(sqe, "loadXPaths(" + source.getLocaleID() + ")"); 215 SurveyMain.busted("loadXPaths(" + source.getLocaleID() + ")", sqe); 216 } finally { 217 DBUtils.close(queryStmt, conn); 218 } 219 } 220 221 /** 222 * Add a set of xpaths to the database. 223 * 224 * @param xpaths 225 * @param conn 226 * @throws SQLException 227 */ addXpaths(Set<String> xpaths, Connection conn)228 private synchronized void addXpaths(Set<String> xpaths, Connection conn) throws SQLException { 229 if (xpaths.size() == 0) 230 return; 231 232 PreparedStatement queryStmt = null; 233 PreparedStatement insertStmt = null; 234 // Insert new xpaths. 235 insertStmt = conn.prepareStatement("INSERT INTO " + CLDR_XPATHS + " (xpath) " + " values (" 236 + " ?)"); 237 for (String xpath : xpaths) { 238 insertStmt.setString(1, Utility.escape(xpath)); 239 insertStmt.addBatch(); 240 stat_dbAdd++; 241 } 242 insertStmt.executeBatch(); 243 conn.commit(); 244 insertStmt.close(); 245 246 // PreparedStatement.getGeneratedKeys() only returns the ID of the 247 // last INSERT statement, so we have to improvise here by performing 248 // another SELECT to get the newly-inserted IDs. 249 queryStmt = conn.prepareStatement("SELECT id,xpath FROM " + CLDR_XPATHS + " ORDER BY id DESC"); 250 queryStmt.setMaxRows(xpaths.size()); 251 queryStmt.setFetchSize(xpaths.size()); 252 ResultSet rs = queryStmt.executeQuery(); 253 while (rs.next()) { 254 int id = rs.getInt(1); 255 String xpath = Utility.unescape(rs.getString(2)); 256 setById(id, xpath); 257 } 258 queryStmt.close(); 259 } 260 261 /** 262 * @return the xpath's id (as an Integer) 263 */ addXpath(String xpath, boolean addIfNotFound, Connection inConn)264 private synchronized Integer addXpath(String xpath, boolean addIfNotFound, Connection inConn) { 265 Integer nid = stringToId.get(xpath); // double check 266 if (nid != null) { 267 return nid; 268 } 269 270 Connection conn = null; 271 PreparedStatement queryStmt = null; 272 PreparedStatement insertStmt = null; 273 try { 274 if (inConn != null) { 275 conn = inConn; 276 } else { 277 conn = DBUtils.getInstance().getDBConnection(); 278 } 279 queryStmt = conn.prepareStatement("SELECT id FROM " + CLDR_XPATHS + " " + " where XPATH=" 280 + " ? "); 281 queryStmt.setString(1, Utility.escape(xpath)); 282 // First, try to query it back from the DB. 283 ResultSet rs = queryStmt.executeQuery(); 284 if (!rs.next()) { 285 if (!addIfNotFound) { 286 return -1; 287 } else { 288 // add it 289 insertStmt = conn.prepareStatement("INSERT INTO " + CLDR_XPATHS + " (xpath ) " + " values (" 290 + " ?)", Statement.RETURN_GENERATED_KEYS); 291 292 insertStmt.setString(1, Utility.escape(xpath)); 293 insertStmt.execute(); 294 conn.commit(); 295 rs = insertStmt.getGeneratedKeys(); 296 if (!rs.next()) { 297 SurveyLog.errln("Couldn't retrieve newly added xpath " + xpath); 298 } else { 299 stat_dbAdd++; 300 } 301 } 302 } else { 303 stat_dbFetch++; 304 } 305 306 int id = rs.getInt(1); 307 nid = Integer.valueOf(id); 308 setById(id, xpath); 309 // logger.info("Mapped " + id + " back to " + xpath); 310 rs.close(); 311 return nid; 312 } catch (SQLException sqe) { 313 SurveyLog.logger.warning("xpath [" + xpath + "] len " + xpath.length()); 314 SurveyLog.logger.severe("XPathTable: Failed in addXPath(" + xpath + "): " + DBUtils.unchainSqlException(sqe)); 315 SurveyMain.busted("XPathTable: Failed in addXPath(" + xpath + "): " + DBUtils.unchainSqlException(sqe)); 316 } finally { 317 if (inConn != null) { 318 conn = null; // don't close 319 } 320 DBUtils.close(insertStmt, queryStmt, conn); 321 } 322 return null; // an exception occured. 323 } 324 325 // /** 326 // * needs a new name.. This uses the string pool and also adds it to the 327 // * table 328 // */ 329 // final String poolx(String x) { 330 // if (x == null) { 331 // return null; 332 // } 333 // 334 // String y = (String) xstringHash.get(x); 335 // if (y == null) { 336 // xstringHash.put(x, x); 337 // 338 // // addXpath(x); 339 // 340 // return x; 341 // } else { 342 // return y; 343 // } 344 // } 345 346 /** 347 * API for get by ID 348 */ getById(int id)349 public final String getById(int id) { 350 if (id == -1) { 351 return null; 352 } 353 String s = idToString_get(id); 354 if (s != null) { 355 return s; 356 } 357 throw new RuntimeException(id + " not found. Make sure loadXpaths() was called first!"); 358 } 359 360 /** 361 * Adds an xpathid-xpath value pair to the XPathTable. This method is used 362 * by classes to cache the values obtained by using their own queries. 363 * 364 * @param id 365 * @param xpath 366 */ setById(int id, String xpath)367 public final void setById(int id, String xpath) { 368 stringToId.put(idToString_put(id, xpath), id); 369 sidToString.put(getStringID(xpath), xpath); 370 } 371 372 /** 373 * get an xpath id by value, add it if not found 374 * 375 * @param xpath 376 * string string to add 377 * @return the id for the specified path 378 */ getByXpath(String xpath)379 public final int getByXpath(String xpath) { 380 Integer nid = stringToId.get(xpath); 381 if (nid != null) { 382 return nid.intValue(); 383 } else { 384 return addXpath(xpath, true, null).intValue(); 385 } 386 } 387 388 /** 389 * Look up xpath id by value. Return -1 if not found 390 * 391 * @param xpath 392 * @return id, or -1 if not found 393 */ peekByXpath(String xpath)394 public final int peekByXpath(String xpath) { 395 Integer nid = stringToId.get(xpath); 396 if (nid != null) { 397 return nid.intValue(); 398 } else { 399 return addXpath(xpath, false, null).intValue(); 400 } 401 } 402 403 /** 404 * get an xpath id by value, add it if not found 405 * 406 * @param xpath 407 * string string to add 408 * @return the id for the specified path 409 */ getByXpath(String xpath, Connection conn)410 public final int getByXpath(String xpath, Connection conn) { 411 Integer nid = stringToId.get(xpath); 412 if (nid != null) { 413 return nid.intValue(); 414 } else { 415 return addXpath(xpath, true, conn).intValue(); 416 } 417 } 418 419 /** 420 * Look up xpath id by value. Return -1 if not found 421 * 422 * @param xpath 423 * @return id, or -1 if not found 424 */ peekByXpath(String xpath, Connection conn)425 public final int peekByXpath(String xpath, Connection conn) { 426 Integer nid = stringToId.get(xpath); 427 if (nid != null) { 428 return nid.intValue(); 429 } else { 430 return addXpath(xpath, false, conn).intValue(); 431 } 432 } 433 434 /** 435 * 436 * @param path 437 * @param xpp 438 * @return 439 */ altFromPathToTinyXpath(String path)440 public String altFromPathToTinyXpath(String path) { 441 return whatFromPathToTinyXpath(path, LDMLConstants.ALT); 442 } 443 444 /** 445 * 446 * @param path 447 * @return 448 * 449 * Called by handlePathValue and makeProposedFile 450 */ removeAlt(String path)451 public static String removeAlt(String path) { 452 XPathParts xpp = XPathParts.getFrozenInstance(path).cloneAsThawed(); // not frozen, for removeAttribute 453 xpp.removeAttribute(-1, LDMLConstants.ALT); 454 return xpp.toString(); 455 } 456 457 /** 458 * remove the 'draft=' and 'alt=*proposed' from the XPath. Makes the path 459 * almost distinguishing, except that certain attributes, such as numbers=, 460 * will be left. 461 * 462 * @param path 463 * @return 464 */ removeDraftAltProposed(String path)465 public static String removeDraftAltProposed(String path) { 466 XPathParts xpp = XPathParts.getFrozenInstance(path).cloneAsThawed(); // not frozen, for removeAttribute 467 Map<String, String> lastAtts = xpp.getAttributes(-1); 468 469 // Remove alt proposed, but leave the type 470 String oldAlt = lastAtts.get(LDMLConstants.ALT); 471 if (oldAlt != null) { 472 String newAlt = LDMLUtilities.parseAlt(oldAlt)[0]; // #0 : altType 473 if (newAlt == null) { 474 xpp.removeAttribute(-1, LDMLConstants.ALT); // alt dropped out existence 475 } else { 476 xpp.putAttributeValue(-1, LDMLConstants.ALT, newAlt); 477 } 478 } 479 480 // always remove draft 481 xpp.removeAttribute(-1, LDMLConstants.DRAFT); 482 483 return xpp.toString(); 484 } 485 getUndistinguishingElementsFor(String path)486 public Map<String, String> getUndistinguishingElementsFor(String path) { 487 return XPathParts.getFrozenInstance(path).getSpecialNondistinguishingAttributes(); 488 } 489 490 /** 491 * 492 * @param path 493 * @return 494 * 495 * Called by handlePathValue and makeProposedFile 496 */ getAlt(String path)497 public static String getAlt(String path) { 498 XPathParts xpp = XPathParts.getFrozenInstance(path); 499 return xpp.getAttributeValue(-1, LDMLConstants.ALT); 500 } 501 xpathToBaseXpathId(String xpath)502 public final int xpathToBaseXpathId(String xpath) { 503 return getByXpath(xpathToBaseXpath(xpath)); 504 } 505 506 /** 507 * note does not remove draft. expects a dpath. 508 * 509 * @param xpath 510 * 511 * This is NOT the same as the two-parameter xpathToBaseXpath elsewhere in this file 512 */ xpathToBaseXpath(String xpath)513 public static String xpathToBaseXpath(String xpath) { 514 XPathParts xpp = XPathParts.getFrozenInstance(xpath); 515 Map<String, String> lastAtts = xpp.getAttributes(-1); 516 String oldAlt = lastAtts.get(LDMLConstants.ALT); 517 if (oldAlt == null) { 518 return xpath; // no change 519 } 520 521 String newAlt = LDMLUtilities.parseAlt(oldAlt)[0]; // #0 : altType 522 if (newAlt == null) { 523 xpp = xpp.cloneAsThawed(); 524 xpp.removeAttribute(-1, LDMLConstants.ALT); // alt dropped out existence 525 } else if (newAlt.equals(oldAlt)) { 526 return xpath; // No change 527 } else { 528 xpp = xpp.cloneAsThawed(); 529 xpp.putAttributeValue(-1, LDMLConstants.ALT, newAlt); 530 } 531 String newXpath = xpp.toString(); 532 // SurveyLog.logger.warning("xp2Bxp: " + xpath + " --> " + newXpath); 533 return newXpath; 534 } 535 536 /** 537 * Modify the given XPathParts by possibly changing or removing its ALT attribute. 538 * 539 * @param xpp the XPathParts, whose contents get changed here and used/modified by the caller 540 * 541 * Called only from submit.jsp 542 */ xPathPartsToBase(XPathParts xpp)543 public static void xPathPartsToBase(XPathParts xpp) { 544 Map<String, String> lastAtts = xpp.getAttributes(-1); 545 String oldAlt = lastAtts.get(LDMLConstants.ALT); 546 if (oldAlt == null) { 547 return; // no change 548 } 549 String newAlt = LDMLUtilities.parseAlt(oldAlt)[0]; // #0 : altType 550 if (newAlt == null) { 551 xpp.removeAttribute(-1, LDMLConstants.ALT); // alt dropped out existence 552 } else if (newAlt.equals(oldAlt)) { 553 return; // No change 554 } else { 555 xpp.putAttributeValue(-1, LDMLConstants.ALT, newAlt); 556 } 557 } 558 559 /** 560 * Get the type attribute for the given path and LDMLConstants 561 * 562 * @param path 563 * @param what LDMLConstants.ALT 564 * @return the type as a string 565 */ whatFromPathToTinyXpath(String path, String what)566 private String whatFromPathToTinyXpath(String path, String what) { 567 XPathParts xpp = XPathParts.getFrozenInstance(path).cloneAsThawed(); // not frozen, for removeAttribute 568 Map<String, String> lastAtts = xpp.getAttributes(-1); 569 String type = lastAtts.get(what); 570 if (type != null) { 571 xpp.removeAttribute(-1, what); 572 } 573 xpp.removeAttribute(-1, LDMLConstants.ALT); 574 xpp.removeAttribute(-1, LDMLConstants.TYPE); 575 xpp.removeAttribute(-1, LDMLConstants.DRAFT); 576 xpp.removeAttribute(-1, LDMLConstants.REFERENCES); 577 // SurveyLog.logger.warning("Type on " + path + " with -1 is " + type ); 578 if ((type == null) && (path.indexOf(what) >= 0)) { 579 try { 580 // less common case - type isn't the last 581 for (int n = -2; (type == null) && ((0 - xpp.size()) < n); n--) { 582 // SurveyLog.logger.warning("Type on n="+n 583 // +", "+path+" with "+n+" is " + type ); 584 lastAtts = xpp.getAttributes(n); 585 if (lastAtts != null) { 586 type = lastAtts.get(what); 587 if (type != null) { 588 xpp.removeAttribute(n, what); 589 } 590 } 591 } 592 } catch (ArrayIndexOutOfBoundsException aioobe) { 593 // means we ran out of elements. 594 } 595 } 596 return type; 597 } 598 599 // proposed-u4-1 600 public static final String PROPOSED_U = LDMLConstants.PROPOSED + "-u"; 601 public static final String PROPOSED_SEP = "-"; 602 public static final String PROPOSED_V = "v"; 603 public static final int NO_XPATH = -1; 604 appendAltProposedPrefix(StringBuilder sb, int userid, Integer voteValue)605 public static final StringBuilder appendAltProposedPrefix(StringBuilder sb, int userid, Integer voteValue) { 606 sb.append(PROPOSED_U); 607 sb.append(userid); 608 if (voteValue != null) { 609 sb.append(PROPOSED_V); 610 sb.append(voteValue); 611 } 612 sb.append(PROPOSED_SEP); 613 return sb; 614 } 615 616 /** 617 * parse an alt-proposed, such as "proposed-u4-1" into a userid (4, in this 618 * case). returns -1 if altProposed is null or in any way malformed. 619 */ altProposedToUserid(String altProposed, Integer voteValue[])620 public static final int altProposedToUserid(String altProposed, Integer voteValue[]) { 621 if ((altProposed == null) || !altProposed.contains(PROPOSED_U)) { 622 return -1; 623 } 624 // skip over 'proposed-u' 625 String idStr = altProposed.substring(altProposed.indexOf(PROPOSED_U) + PROPOSED_U.length()); 626 int dash; 627 if (-1 != (dash = idStr.indexOf(PROPOSED_SEP))) { 628 idStr = idStr.substring(0, dash); 629 } 630 try { 631 return Integer.parseInt(idStr); 632 } catch (Throwable t) { 633 return -1; 634 } 635 } 636 637 // re export PrettyPath API but synchronized 638 /** 639 * Gets sortable form of the pretty path, and caches the mapping for faster 640 * later mapping. 641 * 642 * @param path 643 * @deprecated PrettyPath is deprecated. 644 */ 645 @Deprecated getPrettyPath(String path)646 public String getPrettyPath(String path) { 647 if (path == null) { 648 return null; 649 } 650 synchronized (ppath) { 651 return ppath.getPrettyPath(path); 652 } 653 } 654 655 /** 656 * @deprecated PrettyPath 657 * @param path 658 * @return 659 */ 660 @Deprecated getPrettyPath(int path)661 public String getPrettyPath(int path) { 662 if (path == -1) { 663 return null; 664 } 665 return getPrettyPath(getById(path)); 666 } 667 668 /** 669 * Get original path. ONLY works if getPrettyPath was called with the 670 * original! 671 * 672 * @param prettyPath 673 * @return original path 674 * @deprecated PrettyPath 675 */ 676 @Deprecated getOriginal(String prettyPath)677 public String getOriginal(String prettyPath) { 678 synchronized (ppath) { 679 return ppath.getOriginal(prettyPath); 680 } 681 } 682 683 /** 684 * How much is inside? 685 * 686 * @return Number of xpaths in the table 687 */ count()688 public int count() { 689 return stringToId.size(); 690 } 691 692 /** 693 * xpath to long 694 * @param xpath a string identifying a path, for example "//ldml/numbers/symbols[@numberSystem="sund"]/infinity" 695 * @return a long integer, which is a hash of xpath; for example 2795888612892500012 (decimal) = 6d37a14eec91cee6 (hex) 696 */ getStringID(String xpath)697 public static final long getStringID(String xpath) { 698 return StringId.getId(xpath); 699 } 700 701 /** 702 * xpid to hex 703 * @param baseXpath 704 * @return 705 */ getStringIDString(int baseXpath)706 public String getStringIDString(int baseXpath) { 707 return getStringIDString(getById(baseXpath)); 708 } 709 710 /** 711 * xpath to hex 712 * @param xpath a string identifying a path, for example "//ldml/numbers/symbols[@numberSystem="sund"]/infinity" 713 * @return a sixteen-digit hex string, which is a hash of xpath; for example "6d37a14eec91cee6" 714 */ getStringIDString(String xpath)715 public static final String getStringIDString(String xpath) { 716 return Long.toHexString(getStringID(xpath)); 717 } 718 719 /** 720 * Turn a strid into a xpid (int token) 721 * @param sid 722 * @return 723 */ getXpathIdFromStringId(String sid)724 public final int getXpathIdFromStringId(String sid) { 725 return getByXpath(getByStringID(sid)); 726 } 727 728 /** 729 * Given an XPath stringid, return an integer xpid or NO_XPATH 730 * This function is there to ease transition away from xpids. 731 * @param xpath a StringID (hex) or a decimal id of the form "#1234" 732 * @return the integer xpid or {@link XPathTable#NO_XPATH} 733 */ getXpathIdOrNoneFromStringID(String xpath)734 public int getXpathIdOrNoneFromStringID(String xpath) { 735 int base_xpath; 736 if (xpath == null || xpath.isEmpty()) { 737 base_xpath = XPathTable.NO_XPATH; 738 } else if (xpath.startsWith("#")) { 739 base_xpath = Integer.parseInt(xpath.substring(1)); 740 } else { 741 base_xpath = getXpathIdFromStringId(xpath); 742 } 743 return base_xpath; 744 } 745 getByStringID(String id)746 public String getByStringID(String id) { 747 if (id == null) return null; 748 Long l = Long.parseLong(id, 16); 749 String s = sidToString.get(l); 750 if (s != null) 751 return s; 752 // slow way 753 for (String x : stringToId.keySet()) { 754 if (getStringID(x) == l) { 755 sidToString.put(l, x); 756 return x; 757 } 758 } 759 if (SurveyMain.isUnofficial()) 760 System.err.println("xpt: Couldn't find stringid " + id + " - sid has " + sidToString.size()); 761 // it may be 762 return null; 763 } 764 765 }