< prev index next >

src/java.prefs/windows/classes/java/util/prefs/WindowsPreferences.java

Print this page




  45  */
  46 
  47 class WindowsPreferences extends AbstractPreferences {
  48 
  49     static {
  50         PrivilegedAction<Void> load = () -> {
  51             System.loadLibrary("prefs");
  52             return null;
  53         };
  54         AccessController.doPrivileged(load);
  55     }
  56 
  57     /**
  58      * Logger for error messages
  59      */
  60     private static PlatformLogger logger;
  61 
  62     /**
  63      * Windows registry path to <tt>Preferences</tt>'s root nodes.
  64      */
  65     private static final byte[] WINDOWS_ROOT_PATH
  66                                = stringToByteArray("Software\\JavaSoft\\Prefs");
  67 
  68     /**
  69      * Windows handles to <tt>HKEY_CURRENT_USER</tt> and
  70      * <tt>HKEY_LOCAL_MACHINE</tt> hives.
  71      */
  72     private static final int HKEY_CURRENT_USER = 0x80000001;
  73     private static final int HKEY_LOCAL_MACHINE = 0x80000002;
  74 
  75     /**
  76      * Mount point for <tt>Preferences</tt>'  user root.
  77      */
  78     private static final int USER_ROOT_NATIVE_HANDLE = HKEY_CURRENT_USER;
  79 
  80     /**
  81      * Mount point for <tt>Preferences</tt>'  system root.
  82      */
  83     private static final int SYSTEM_ROOT_NATIVE_HANDLE = HKEY_LOCAL_MACHINE;
  84 
  85     /**
  86      * Maximum byte-encoded path length for Windows native functions,


 387         return result;
 388     }
 389 
 390     /**
 391      * Constructs a <tt>WindowsPreferences</tt> node, creating underlying
 392      * Windows registry node and all its Windows parents, if they are not yet
 393      * created.
 394      * Logs a warning message, if Windows Registry is unavailable.
 395      */
 396     private WindowsPreferences(WindowsPreferences parent, String name) {
 397         super(parent, name);
 398         int parentNativeHandle = parent.openKey(KEY_CREATE_SUB_KEY, KEY_READ);
 399         if (parentNativeHandle == NULL_NATIVE_HANDLE) {
 400             // if here, openKey failed and logged
 401             isBackingStoreAvailable = false;
 402             return;
 403         }
 404         int[] result =
 405                WindowsRegCreateKeyEx1(parentNativeHandle, toWindowsName(name));
 406         if (result[ERROR_CODE] != ERROR_SUCCESS) {
 407             logger().warning("Could not create windows registry "
 408             + "node " + byteArrayToString(windowsAbsolutePath()) +
 409             " at root 0x" + Integer.toHexString(rootNativeHandle()) +
 410             ". Windows RegCreateKeyEx(...) returned error code " +
 411             result[ERROR_CODE] + ".");
 412             isBackingStoreAvailable = false;
 413             return;
 414         }
 415         newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY);
 416         closeKey(parentNativeHandle);
 417         closeKey(result[NATIVE_HANDLE]);
 418     }
 419 
 420     /**
 421      * Constructs a root node creating the underlying
 422      * Windows registry node and all of its parents, if they have not yet been
 423      * created.
 424      * Logs a warning message, if Windows Registry is unavailable.
 425      * @param rootNativeHandle Native handle to one of Windows top level keys.
 426      * @param rootDirectory Path to root directory, as a byte-encoded string.
 427      */
 428     private  WindowsPreferences(int rootNativeHandle, byte[] rootDirectory) {
 429         super(null,"");
 430         int[] result =
 431                 WindowsRegCreateKeyEx1(rootNativeHandle, rootDirectory);
 432         if (result[ERROR_CODE] != ERROR_SUCCESS) {
 433             logger().warning("Could not open/create prefs root node " +
 434             byteArrayToString(windowsAbsolutePath()) + " at root 0x" +
 435             Integer.toHexString(rootNativeHandle()) +
 436             ". Windows RegCreateKeyEx(...) returned error code " +
 437             result[ERROR_CODE] + ".");
 438             isBackingStoreAvailable = false;
 439             return;
 440         }
 441         // Check if a new node
 442         newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY);
 443         closeKey(result[NATIVE_HANDLE]);
 444     }
 445 
 446     /**
 447      * Returns Windows absolute path of the current node as a byte array.
 448      * Java "/" separator is transformed into Windows "\".
 449      * @see Preferences#absolutePath()
 450      */
 451     private byte[] windowsAbsolutePath() {
 452         ByteArrayOutputStream bstream = new ByteArrayOutputStream();
 453         bstream.write(WINDOWS_ROOT_PATH, 0, WINDOWS_ROOT_PATH.length-1);
 454         StringTokenizer tokenizer = new StringTokenizer(absolutePath(),"/");
 455         while (tokenizer.hasMoreTokens()) {
 456             bstream.write((byte)'\\');
 457             String nextName = tokenizer.nextToken();
 458             byte[] windowsNextName = toWindowsName(nextName);
 459             bstream.write(windowsNextName, 0, windowsNextName.length-1);
 460         }
 461         bstream.write(0);
 462         return bstream.toByteArray();
 463     }
 464 
 465     /**
 466      * Opens current node's underlying Windows registry key using a
 467      * given security mask.
 468      * @param securityMask Windows security mask.
 469      * @return Windows registry key's handle.
 470      * @see #openKey(byte[], int)
 471      * @see #openKey(int, byte[], int)
 472      * @see #closeKey(int)
 473      */
 474     private int openKey(int securityMask) {


 495      * @param windowsAbsolutePath Windows absolute path of the
 496      *        key as a byte-encoded string.
 497      * @param mask1 Preferred Windows security mask.
 498      * @param mask2 Alternate Windows security mask.
 499      * @return Windows registry key's handle.
 500      * @see #openKey(int)
 501      * @see #openKey(int, byte[],int)
 502      * @see #closeKey(int)
 503      */
 504     private int openKey(byte[] windowsAbsolutePath, int mask1, int mask2) {
 505         /*  Check if key's path is short enough be opened at once
 506             otherwise use a path-splitting procedure */
 507         if (windowsAbsolutePath.length <= MAX_WINDOWS_PATH_LENGTH + 1) {
 508              int[] result = WindowsRegOpenKey1(rootNativeHandle(),
 509                                                windowsAbsolutePath, mask1);
 510              if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1)
 511                  result = WindowsRegOpenKey1(rootNativeHandle(),
 512                                              windowsAbsolutePath, mask2);
 513 
 514              if (result[ERROR_CODE] != ERROR_SUCCESS) {
 515                 logger().warning("Could not open windows "
 516                 + "registry node " + byteArrayToString(windowsAbsolutePath()) +
 517                 " at root 0x" + Integer.toHexString(rootNativeHandle()) +

 518                 ". Windows RegOpenKey(...) returned error code " +
 519                 result[ERROR_CODE] + ".");
 520                 result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE;
 521                 if (result[ERROR_CODE] == ERROR_ACCESS_DENIED) {
 522                     throw new SecurityException("Could not open windows "
 523                 + "registry node " + byteArrayToString(windowsAbsolutePath()) +
 524                 " at root 0x" + Integer.toHexString(rootNativeHandle()) +


 525                 ": Access denied");
 526                 }
 527              }
 528              return result[NATIVE_HANDLE];
 529         } else {
 530             return openKey(rootNativeHandle(), windowsAbsolutePath, mask1, mask2);
 531         }
 532     }
 533 
 534      /**
 535      * Opens Windows registry key at a given relative path
 536      * with respect to a given Windows registry key.
 537      * @param windowsAbsolutePath Windows relative path of the
 538      *        key as a byte-encoded string.
 539      * @param nativeHandle handle to the base Windows key.
 540      * @param mask1 Preferred Windows security mask.
 541      * @param mask2 Alternate Windows security mask.
 542      * @return Windows registry key's handle.
 543      * @see #openKey(int)
 544      * @see #openKey(byte[],int)
 545      * @see #closeKey(int)
 546      */
 547     private int openKey(int nativeHandle, byte[] windowsRelativePath,
 548                         int mask1, int mask2) {
 549     /* If the path is short enough open at once. Otherwise split the path */
 550         if (windowsRelativePath.length <= MAX_WINDOWS_PATH_LENGTH + 1 ) {
 551              int[] result = WindowsRegOpenKey1(nativeHandle,
 552                                                windowsRelativePath, mask1);
 553              if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1)
 554                  result = WindowsRegOpenKey1(nativeHandle,
 555                                              windowsRelativePath, mask2);
 556 
 557              if (result[ERROR_CODE] != ERROR_SUCCESS) {
 558                 logger().warning("Could not open windows "
 559                 + "registry node " + byteArrayToString(windowsAbsolutePath()) +
 560                 " at root 0x" + Integer.toHexString(nativeHandle) +
 561                 ". Windows RegOpenKey(...) returned error code " +
 562                 result[ERROR_CODE] + ".");
 563                 result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE;
 564              }
 565              return result[NATIVE_HANDLE];
 566         } else {
 567             int separatorPosition = -1;
 568             // Be greedy - open the longest possible path
 569             for (int i = MAX_WINDOWS_PATH_LENGTH; i > 0; i--) {
 570                 if (windowsRelativePath[i] == ((byte)'\\')) {
 571                     separatorPosition = i;
 572                     break;
 573                 }
 574             }
 575             // Split the path and do the recursion
 576             byte[] nextRelativeRoot = new byte[separatorPosition+1];
 577             System.arraycopy(windowsRelativePath, 0, nextRelativeRoot,0,
 578                                                       separatorPosition);
 579             nextRelativeRoot[separatorPosition] = 0;


 587                 return NULL_NATIVE_HANDLE;
 588             }
 589             int result = openKey(nextNativeHandle, nextRelativePath,
 590                                  mask1,mask2);
 591             closeKey(nextNativeHandle);
 592             return result;
 593         }
 594     }
 595 
 596      /**
 597      * Closes Windows registry key.
 598      * Logs a warning if Windows registry is unavailable.
 599      * @param key's Windows registry handle.
 600      * @see #openKey(int)
 601      * @see #openKey(byte[],int)
 602      * @see #openKey(int, byte[],int)
 603     */
 604     private void closeKey(int nativeHandle) {
 605         int result = WindowsRegCloseKey(nativeHandle);
 606         if (result != ERROR_SUCCESS) {
 607             logger().warning("Could not close windows "
 608             + "registry node " + byteArrayToString(windowsAbsolutePath()) +
 609             " at root 0x" + Integer.toHexString(rootNativeHandle()) +
 610             ". Windows RegCloseKey(...) returned error code " + result + ".");


 611         }
 612     }
 613 
 614      /**
 615      * Implements <tt>AbstractPreferences</tt> <tt>putSpi()</tt> method.
 616      * Puts name-value pair into the underlying Windows registry node.
 617      * Logs a warning, if Windows registry is unavailable.
 618      * @see #getSpi(String)
 619      */
 620     protected void putSpi(String javaName, String value) {
 621         int nativeHandle = openKey(KEY_SET_VALUE);
 622         if (nativeHandle == NULL_NATIVE_HANDLE) {
 623             isBackingStoreAvailable = false;
 624             return;
 625         }
 626         int result = WindowsRegSetValueEx1(nativeHandle,
 627                 toWindowsName(javaName), toWindowsValueString(value));
 628         if (result != ERROR_SUCCESS) {
 629             logger().warning("Could not assign value to key " +
 630             byteArrayToString(toWindowsName(javaName))+ " at Windows registry node "
 631            + byteArrayToString(windowsAbsolutePath()) + " at root 0x"
 632            + Integer.toHexString(rootNativeHandle()) +
 633            ". Windows RegSetValueEx(...) returned error code " + result + ".");



 634             isBackingStoreAvailable = false;
 635         }
 636         closeKey(nativeHandle);
 637     }
 638 
 639     /**
 640      * Implements <tt>AbstractPreferences</tt> <tt>getSpi()</tt> method.
 641      * Gets a string value from the underlying Windows registry node.
 642      * Logs a warning, if Windows registry is unavailable.
 643      * @see #putSpi(String, String)
 644      */
 645     protected String getSpi(String javaName) {
 646         int nativeHandle = openKey(KEY_QUERY_VALUE);
 647         if (nativeHandle == NULL_NATIVE_HANDLE) {
 648             return null;
 649         }
 650         Object resultObject = WindowsRegQueryValueEx(nativeHandle,
 651                 toWindowsName(javaName));
 652         if (resultObject == null) {
 653             closeKey(nativeHandle);


 655         }
 656         closeKey(nativeHandle);
 657         return toJavaValueString((byte[]) resultObject);
 658     }
 659 
 660     /**
 661      * Implements <tt>AbstractPreferences</tt> <tt>removeSpi()</tt> method.
 662      * Deletes a string name-value pair from the underlying Windows registry
 663      * node, if this value still exists.
 664      * Logs a warning, if Windows registry is unavailable or key has already
 665      * been deleted.
 666      */
 667     protected void removeSpi(String key) {
 668         int nativeHandle = openKey(KEY_SET_VALUE);
 669         if (nativeHandle == NULL_NATIVE_HANDLE) {
 670         return;
 671         }
 672         int result =
 673             WindowsRegDeleteValue(nativeHandle, toWindowsName(key));
 674         if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
 675             logger().warning("Could not delete windows registry "
 676             + "value " + byteArrayToString(windowsAbsolutePath())+ "\\" +
 677             toWindowsName(key) + " at root 0x" +
 678             Integer.toHexString(rootNativeHandle()) +
 679             ". Windows RegDeleteValue(...) returned error code " +
 680             result + ".");
 681             isBackingStoreAvailable = false;
 682         }
 683         closeKey(nativeHandle);
 684     }
 685 
 686     /**
 687      * Implements <tt>AbstractPreferences</tt> <tt>keysSpi()</tt> method.
 688      * Gets value names from the underlying Windows registry node.
 689      * Throws a BackingStoreException and logs a warning, if
 690      * Windows registry is unavailable.
 691      */
 692     protected String[] keysSpi() throws BackingStoreException{
 693         // Find out the number of values
 694         int nativeHandle = openKey(KEY_QUERY_VALUE);
 695         if (nativeHandle == NULL_NATIVE_HANDLE) {
 696             throw new BackingStoreException("Could not open windows"
 697             + "registry node " + byteArrayToString(windowsAbsolutePath()) +
 698             " at root 0x" + Integer.toHexString(rootNativeHandle()) + ".");


 699         }
 700         int[] result =  WindowsRegQueryInfoKey1(nativeHandle);
 701         if (result[ERROR_CODE] != ERROR_SUCCESS) {
 702             String info = "Could not query windows"
 703             + "registry node " + byteArrayToString(windowsAbsolutePath()) +
 704             " at root 0x" + Integer.toHexString(rootNativeHandle()) +

 705             ". Windows RegQueryInfoKeyEx(...) returned error code " +
 706             result[ERROR_CODE] + ".";
 707             logger().warning(info);
 708             throw new BackingStoreException(info);
 709         }
 710         int maxValueNameLength = result[MAX_VALUE_NAME_LENGTH];
 711         int valuesNumber = result[VALUES_NUMBER];
 712         if (valuesNumber == 0) {
 713             closeKey(nativeHandle);
 714             return new String[0];
 715        }
 716        // Get the values
 717        String[] valueNames = new String[valuesNumber];
 718        for (int i = 0; i < valuesNumber; i++) {
 719             byte[] windowsName = WindowsRegEnumValue1(nativeHandle, i,
 720                                                         maxValueNameLength+1);
 721             if (windowsName == null) {
 722                 String info =
 723                 "Could not enumerate value #" + i + "  of windows node " +
 724                 byteArrayToString(windowsAbsolutePath()) + " at root 0x" +
 725                 Integer.toHexString(rootNativeHandle()) + ".";
 726                 logger().warning(info);
 727                 throw new BackingStoreException(info);
 728             }
 729             valueNames[i] = toJavaName(windowsName);
 730         }
 731         closeKey(nativeHandle);
 732         return valueNames;
 733     }
 734 
 735     /**
 736      * Implements <tt>AbstractPreferences</tt> <tt>childrenNamesSpi()</tt> method.
 737      * Calls Windows registry to retrive children of this node.
 738      * Throws a BackingStoreException and logs a warning message,
 739      * if Windows registry is not available.
 740      */
 741     protected String[] childrenNamesSpi() throws BackingStoreException {
 742         // Open key
 743         int nativeHandle = openKey(KEY_ENUMERATE_SUB_KEYS| KEY_QUERY_VALUE);
 744         if (nativeHandle == NULL_NATIVE_HANDLE) {
 745             throw new BackingStoreException("Could not open windows"
 746             + "registry node " + byteArrayToString(windowsAbsolutePath()) +
 747             " at root 0x" + Integer.toHexString(rootNativeHandle()) + ".");


 748         }
 749         // Get number of children
 750         int[] result =  WindowsRegQueryInfoKey1(nativeHandle);
 751         if (result[ERROR_CODE] != ERROR_SUCCESS) {
 752             String info = "Could not query windows"
 753             + "registry node " + byteArrayToString(windowsAbsolutePath()) +
 754             " at root 0x" + Integer.toHexString(rootNativeHandle()) +
 755             ". Windows RegQueryInfoKeyEx(...) returned error code " +
 756             result[ERROR_CODE] + ".";
 757             logger().warning(info);
 758             throw new BackingStoreException(info);
 759         }
 760         int maxKeyLength = result[MAX_KEY_LENGTH];
 761         int subKeysNumber = result[SUBKEYS_NUMBER];
 762         if (subKeysNumber == 0) {
 763             closeKey(nativeHandle);
 764             return new String[0];
 765         }
 766         String[] subkeys = new String[subKeysNumber];
 767         String[] children = new String[subKeysNumber];
 768         // Get children
 769         for (int i = 0; i < subKeysNumber; i++) {
 770             byte[] windowsName = WindowsRegEnumKeyEx1(nativeHandle, i,
 771                                                                 maxKeyLength+1);
 772             if (windowsName == null) {
 773                 String info =


 785     }
 786 
 787     /**
 788      * Implements <tt>Preferences</tt> <tt>flush()</tt> method.
 789      * Flushes Windows registry changes to disk.
 790      * Throws a BackingStoreException and logs a warning message if Windows
 791      * registry is not available.
 792      */
 793     public void flush() throws BackingStoreException{
 794 
 795         if (isRemoved()) {
 796             parent.flush();
 797             return;
 798         }
 799         if (!isBackingStoreAvailable) {
 800             throw new BackingStoreException(
 801                                        "flush(): Backing store not available.");
 802         }
 803         int nativeHandle = openKey(KEY_READ);
 804         if (nativeHandle == NULL_NATIVE_HANDLE) {
 805             throw new BackingStoreException("Could not open windows"
 806             + "registry node " + byteArrayToString(windowsAbsolutePath()) +
 807             " at root 0x" + Integer.toHexString(rootNativeHandle()) + ".");


 808         }
 809         int result = WindowsRegFlushKey1(nativeHandle);
 810         if (result != ERROR_SUCCESS) {
 811             String info = "Could not flush windows "
 812             + "registry node " + byteArrayToString(windowsAbsolutePath())
 813             + " at root 0x" + Integer.toHexString(rootNativeHandle()) +
 814             ". Windows RegFlushKey(...) returned error code " + result + ".";


 815             logger().warning(info);
 816             throw new BackingStoreException(info);
 817         }
 818         closeKey(nativeHandle);
 819     }
 820 
 821 
 822     /**
 823      * Implements <tt>Preferences</tt> <tt>sync()</tt> method.
 824      * Flushes Windows registry changes to disk. Equivalent to flush().
 825      * @see flush()
 826      */
 827     public void sync() throws BackingStoreException{
 828         if (isRemoved())
 829             throw new IllegalStateException("Node has been removed");
 830         flush();
 831     }
 832 
 833     /**
 834      * Implements <tt>AbstractPreferences</tt> <tt>childSpi()</tt> method.
 835      * Constructs a child node with a
 836      * given name and creates its underlying Windows registry node,
 837      * if it does not exist.
 838      * Logs a warning message, if Windows Registry is unavailable.
 839      */
 840     protected AbstractPreferences childSpi(String name) {
 841             return new WindowsPreferences(this, name);
 842     }
 843 
 844     /**
 845      * Implements <tt>AbstractPreferences</tt> <tt>removeNodeSpi()</tt> method.
 846      * Deletes underlying Windows registry node.
 847      * Throws a BackingStoreException and logs a warning, if Windows registry
 848      * is not available.
 849      */
 850     public void removeNodeSpi() throws BackingStoreException {
 851         int parentNativeHandle =
 852                          ((WindowsPreferences)parent()).openKey(DELETE);
 853         if (parentNativeHandle == NULL_NATIVE_HANDLE) {
 854             throw new BackingStoreException("Could not open parent windows"
 855             + "registry node of " + byteArrayToString(windowsAbsolutePath()) +
 856             " at root 0x" + Integer.toHexString(rootNativeHandle()) + ".");


 857         }
 858         int result =
 859                 WindowsRegDeleteKey(parentNativeHandle, toWindowsName(name()));
 860         if (result != ERROR_SUCCESS) {
 861             String info = "Could not delete windows "
 862             + "registry node " + byteArrayToString(windowsAbsolutePath()) +
 863             " at root 0x" + Integer.toHexString(rootNativeHandle()) +
 864             ". Windows RegDeleteKeyEx(...) returned error code " +
 865             result + ".";
 866             logger().warning(info);
 867             throw new BackingStoreException(info);
 868         }
 869         closeKey(parentNativeHandle);
 870     }
 871 
 872     /**
 873      * Converts value's or node's name from its byte array representation to
 874      * java string. Two encodings, simple and altBase64 are used. See
 875      * {@link #toWindowsName(String) toWindowsName()} for a detailed
 876      * description of encoding conventions.
 877      * @param windowsNameArray Null-terminated byte array.
 878      */
 879     private static String toJavaName(byte[] windowsNameArray) {
 880         String windowsName = byteArrayToString(windowsNameArray);
 881         // check if Alt64
 882         if ((windowsName.length()>1) &&
 883                                    (windowsName.substring(0,2).equals("/!"))) {
 884             return toJavaAlt64Name(windowsName);
 885         }
 886         StringBuffer javaName = new StringBuffer();
 887         char ch;
 888         // Decode from simple encoding
 889         for (int i = 0; i < windowsName.length(); i++){
 890             if ((ch = windowsName.charAt(i)) == '/') {
 891                 char next = ' ';
 892                 if ((windowsName.length() > i + 1) &&
 893                    ((next = windowsName.charAt(i+1)) >= 'A') && (next <= 'Z')) {

 894                 ch = next;
 895                 i++;
 896                 } else  if ((windowsName.length() > i + 1) && (next == '/')) {

 897                 ch = '\\';
 898                 i++;
 899                 }
 900             } else if (ch == '\\') {
 901                 ch = '/';
 902             }
 903             javaName.append(ch);
 904         }
 905         return javaName.toString();
 906     }
 907 
 908     /**
 909      * Converts value's or node's name from its Windows representation to java
 910      * string, using altBase64 encoding. See
 911      * {@link #toWindowsName(String) toWindowsName()} for a detailed
 912      * description of encoding conventions.
 913      */
 914 
 915     private static String toJavaAlt64Name(String windowsName) {
 916         byte[] byteBuffer =
 917                           Base64.altBase64ToByteArray(windowsName.substring(2));
 918         StringBuffer result = new StringBuffer();
 919         for (int i = 0; i < byteBuffer.length; i++) {
 920             int firstbyte = (byteBuffer[i++] & 0xff);
 921             int secondbyte =  (byteBuffer[i] & 0xff);
 922             result.append((char)((firstbyte << 8) + secondbyte));
 923         }
 924         return result.toString();
 925     }
 926 
 927     /**
 928      * Converts value's or node's name to its Windows representation
 929      * as a byte-encoded string.
 930      * Two encodings, simple and altBase64 are used.
 931      * <p>
 932      * <i>Simple</i> encoding is used, if java string does not contain
 933      * any characters less, than 0x0020, or greater, than 0x007f.
 934      * Simple encoding adds "/" character to capital letters, i.e.
 935      * "A" is encoded as "/A". Character '\' is encoded as '//',
 936      * '/' is encoded as '\'.
 937      * The constructed string is converted to byte array by truncating the
 938      * highest byte and adding the terminating <tt>null</tt> character.
 939      * <p>
 940      * <i>altBase64</i>  encoding is used, if java string does contain at least
 941      * one character less, than 0x0020, or greater, than 0x007f.
 942      * This encoding is marked by setting first two bytes of the
 943      * Windows string to '/!'. The java name is then encoded using
 944      * byteArrayToAltBase64() method from
 945      * Base64 class.
 946      */
 947     private static byte[] toWindowsName(String javaName) {
 948         StringBuffer windowsName = new StringBuffer();
 949         for (int i = 0; i < javaName.length(); i++) {
 950             char ch =javaName.charAt(i);
 951             if ((ch < 0x0020)||(ch > 0x007f)) {
 952                 // If a non-trivial character encountered, use altBase64
 953                 return toWindowsAlt64Name(javaName);
 954             }
 955             if (ch == '\\') {
 956                 windowsName.append("//");
 957             } else if (ch == '/') {
 958                 windowsName.append('\\');
 959             } else if ((ch >= 'A') && (ch <='Z')) {
 960                 windowsName.append("/" + ch);
 961             } else {
 962                 windowsName.append(ch);
 963             }
 964         }
 965         return stringToByteArray(windowsName.toString());
 966     }
 967 
 968     /**
 969      * Converts value's or node's name to its Windows representation
 970      * as a byte-encoded string, using altBase64 encoding. See
 971      * {@link #toWindowsName(String) toWindowsName()} for a detailed
 972      * description of encoding conventions.
 973      */
 974     private static byte[] toWindowsAlt64Name(String javaName) {
 975         byte[] javaNameArray = new byte[2*javaName.length()];
 976         // Convert to byte pairs
 977         int counter = 0;
 978         for (int i = 0; i < javaName.length();i++) {
 979                 int ch = javaName.charAt(i);
 980                 javaNameArray[counter++] = (byte)(ch >>> 8);
 981                 javaNameArray[counter++] = (byte)ch;
 982         }
 983 
 984         return stringToByteArray(
 985                            "/!" + Base64.byteArrayToAltBase64(javaNameArray));
 986     }
 987 
 988     /**
 989      * Converts value string from its Windows representation
 990      * to java string.  See
 991      * {@link #toWindowsValueString(String) toWindowsValueString()} for the
 992      * description of the encoding algorithm.
 993      */
 994      private static String toJavaValueString(byte[] windowsNameArray) {
 995         // Use modified native2ascii algorithm
 996         String windowsName = byteArrayToString(windowsNameArray);
 997         StringBuffer javaName = new StringBuffer();
 998         char ch;
 999         for (int i = 0; i < windowsName.length(); i++){
1000             if ((ch = windowsName.charAt(i)) == '/') {
1001                 char next = ' ';
1002 
1003                 if (windowsName.length() > i + 1 &&
1004                                     (next = windowsName.charAt(i + 1)) == 'u') {
1005                     if (windowsName.length() < i + 6){
1006                         break;
1007                     } else {
1008                         ch = (char)Integer.parseInt
1009                                       (windowsName.substring(i + 2, i + 6), 16);
1010                         i += 5;
1011                     }
1012                 } else
1013                 if ((windowsName.length() > i + 1) &&
1014                           ((windowsName.charAt(i+1)) >= 'A') && (next <= 'Z')) {

1015                 ch = next;
1016                 i++;
1017                 } else  if ((windowsName.length() > i + 1) &&
1018                                                (next == '/')) {
1019                 ch = '\\';
1020                 i++;
1021                 }
1022             } else if (ch == '\\') {
1023                 ch = '/';
1024             }
1025             javaName.append(ch);
1026         }
1027         return javaName.toString();
1028     }
1029 
1030     /**
1031      * Converts value string to it Windows representation.
1032      * as a byte-encoded string.
1033      * Encoding algorithm adds "/" character to capital letters, i.e.
1034      * "A" is encoded as "/A". Character '\' is encoded as '//',
1035      * '/' is encoded as  '\'.
1036      * Then encoding scheme similar to jdk's native2ascii converter is used
1037      * to convert java string to a byte array of ASCII characters.
1038      */
1039     private static byte[] toWindowsValueString(String javaName) {
1040         StringBuffer windowsName = new StringBuffer();
1041         for (int i = 0; i < javaName.length(); i++) {
1042             char ch =javaName.charAt(i);
1043             if ((ch < 0x0020)||(ch > 0x007f)){
1044                 // write \udddd
1045                 windowsName.append("/u");
1046                 String hex = Integer.toHexString(javaName.charAt(i));
1047                 StringBuffer hex4 = new StringBuffer(hex);
1048                 hex4.reverse();
1049                 int len = 4 - hex4.length();
1050                 for (int j = 0; j < len; j++){
1051                     hex4.append('0');
1052                 }
1053                 for (int j = 0; j < 4; j++){
1054                     windowsName.append(hex4.charAt(3 - j));
1055                 }
1056             } else if (ch == '\\') {
1057                 windowsName.append("//");
1058             } else if (ch == '/') {
1059                 windowsName.append('\\');
1060             } else if ((ch >= 'A') && (ch <='Z')) {
1061                 windowsName.append("/" + ch);
1062             } else {
1063                 windowsName.append(ch);
1064             }
1065         }
1066         return stringToByteArray(windowsName.toString());
1067     }
1068 
1069     /**
1070      * Returns native handle for the top Windows node for this node.
1071      */
1072     private int rootNativeHandle() {
1073         return (isUserNode()? USER_ROOT_NATIVE_HANDLE :
1074                               SYSTEM_ROOT_NATIVE_HANDLE);

1075     }
1076 
1077     /**
1078      * Returns this java string as a null-terminated byte array
1079      */
1080     private static byte[] stringToByteArray(String str) {
1081         byte[] result = new byte[str.length()+1];
1082         for (int i = 0; i < str.length(); i++) {
1083             result[i] = (byte) str.charAt(i);
1084         }
1085         result[str.length()] = 0;
1086         return result;
1087     }
1088 
1089     /**
1090      * Converts a null-terminated byte array to java string
1091      */
1092     private static String byteArrayToString(byte[] array) {
1093         StringBuffer result = new StringBuffer();
1094         for (int i = 0; i < array.length - 1; i++) {
1095             result.append((char)array[i]);
1096         }
1097         return result.toString();
1098     }
1099 
1100    /**
1101     * Empty, never used implementation  of AbstractPreferences.flushSpi().
1102     */
1103     protected void flushSpi() throws BackingStoreException {
1104         // assert false;
1105     }
1106 
1107    /**
1108     * Empty, never used implementation  of AbstractPreferences.flushSpi().
1109     */
1110     protected void syncSpi() throws BackingStoreException {
1111         // assert false;
1112     }
1113 


  45  */
  46 
  47 class WindowsPreferences extends AbstractPreferences {
  48 
  49     static {
  50         PrivilegedAction<Void> load = () -> {
  51             System.loadLibrary("prefs");
  52             return null;
  53         };
  54         AccessController.doPrivileged(load);
  55     }
  56 
  57     /**
  58      * Logger for error messages
  59      */
  60     private static PlatformLogger logger;
  61 
  62     /**
  63      * Windows registry path to <tt>Preferences</tt>'s root nodes.
  64      */
  65     private static final byte[] WINDOWS_ROOT_PATH =
  66         stringToByteArray("Software\\JavaSoft\\Prefs");
  67 
  68     /**
  69      * Windows handles to <tt>HKEY_CURRENT_USER</tt> and
  70      * <tt>HKEY_LOCAL_MACHINE</tt> hives.
  71      */
  72     private static final int HKEY_CURRENT_USER = 0x80000001;
  73     private static final int HKEY_LOCAL_MACHINE = 0x80000002;
  74 
  75     /**
  76      * Mount point for <tt>Preferences</tt>'  user root.
  77      */
  78     private static final int USER_ROOT_NATIVE_HANDLE = HKEY_CURRENT_USER;
  79 
  80     /**
  81      * Mount point for <tt>Preferences</tt>'  system root.
  82      */
  83     private static final int SYSTEM_ROOT_NATIVE_HANDLE = HKEY_LOCAL_MACHINE;
  84 
  85     /**
  86      * Maximum byte-encoded path length for Windows native functions,


 387         return result;
 388     }
 389 
 390     /**
 391      * Constructs a <tt>WindowsPreferences</tt> node, creating underlying
 392      * Windows registry node and all its Windows parents, if they are not yet
 393      * created.
 394      * Logs a warning message, if Windows Registry is unavailable.
 395      */
 396     private WindowsPreferences(WindowsPreferences parent, String name) {
 397         super(parent, name);
 398         int parentNativeHandle = parent.openKey(KEY_CREATE_SUB_KEY, KEY_READ);
 399         if (parentNativeHandle == NULL_NATIVE_HANDLE) {
 400             // if here, openKey failed and logged
 401             isBackingStoreAvailable = false;
 402             return;
 403         }
 404         int[] result =
 405                WindowsRegCreateKeyEx1(parentNativeHandle, toWindowsName(name));
 406         if (result[ERROR_CODE] != ERROR_SUCCESS) {
 407             logger().warning("Could not create windows registry node " +
 408                     byteArrayToString(windowsAbsolutePath()) +
 409                     " at root 0x" + Integer.toHexString(rootNativeHandle()) +
 410                     ". Windows RegCreateKeyEx(...) returned error code " +
 411                     result[ERROR_CODE] + ".");
 412             isBackingStoreAvailable = false;
 413             return;
 414         }
 415         newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY);
 416         closeKey(parentNativeHandle);
 417         closeKey(result[NATIVE_HANDLE]);
 418     }
 419 
 420     /**
 421      * Constructs a root node creating the underlying
 422      * Windows registry node and all of its parents, if they have not yet been
 423      * created.
 424      * Logs a warning message, if Windows Registry is unavailable.
 425      * @param rootNativeHandle Native handle to one of Windows top level keys.
 426      * @param rootDirectory Path to root directory, as a byte-encoded string.
 427      */
 428     private  WindowsPreferences(int rootNativeHandle, byte[] rootDirectory) {
 429         super(null, "");
 430         int[] result =
 431                 WindowsRegCreateKeyEx1(rootNativeHandle, rootDirectory);
 432         if (result[ERROR_CODE] != ERROR_SUCCESS) {
 433             logger().warning("Could not open/create prefs root node " +
 434                     byteArrayToString(windowsAbsolutePath()) +
 435                     " at root 0x" + Integer.toHexString(rootNativeHandle()) +
 436                     ". Windows RegCreateKeyEx(...) returned error code " +
 437                     result[ERROR_CODE] + ".");
 438             isBackingStoreAvailable = false;
 439             return;
 440         }
 441         // Check if a new node
 442         newNode = (result[DISPOSITION] == REG_CREATED_NEW_KEY);
 443         closeKey(result[NATIVE_HANDLE]);
 444     }
 445 
 446     /**
 447      * Returns Windows absolute path of the current node as a byte array.
 448      * Java "/" separator is transformed into Windows "\".
 449      * @see Preferences#absolutePath()
 450      */
 451     private byte[] windowsAbsolutePath() {
 452         ByteArrayOutputStream bstream = new ByteArrayOutputStream();
 453         bstream.write(WINDOWS_ROOT_PATH, 0, WINDOWS_ROOT_PATH.length-1);
 454         StringTokenizer tokenizer = new StringTokenizer(absolutePath(), "/");
 455         while (tokenizer.hasMoreTokens()) {
 456             bstream.write((byte)'\\');
 457             String nextName = tokenizer.nextToken();
 458             byte[] windowsNextName = toWindowsName(nextName);
 459             bstream.write(windowsNextName, 0, windowsNextName.length-1);
 460         }
 461         bstream.write(0);
 462         return bstream.toByteArray();
 463     }
 464 
 465     /**
 466      * Opens current node's underlying Windows registry key using a
 467      * given security mask.
 468      * @param securityMask Windows security mask.
 469      * @return Windows registry key's handle.
 470      * @see #openKey(byte[], int)
 471      * @see #openKey(int, byte[], int)
 472      * @see #closeKey(int)
 473      */
 474     private int openKey(int securityMask) {


 495      * @param windowsAbsolutePath Windows absolute path of the
 496      *        key as a byte-encoded string.
 497      * @param mask1 Preferred Windows security mask.
 498      * @param mask2 Alternate Windows security mask.
 499      * @return Windows registry key's handle.
 500      * @see #openKey(int)
 501      * @see #openKey(int, byte[],int)
 502      * @see #closeKey(int)
 503      */
 504     private int openKey(byte[] windowsAbsolutePath, int mask1, int mask2) {
 505         /*  Check if key's path is short enough be opened at once
 506             otherwise use a path-splitting procedure */
 507         if (windowsAbsolutePath.length <= MAX_WINDOWS_PATH_LENGTH + 1) {
 508             int[] result = WindowsRegOpenKey1(rootNativeHandle(),
 509                                               windowsAbsolutePath, mask1);
 510             if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1)
 511                 result = WindowsRegOpenKey1(rootNativeHandle(),
 512                                             windowsAbsolutePath, mask2);
 513 
 514             if (result[ERROR_CODE] != ERROR_SUCCESS) {
 515                 logger().warning("Could not open windows registry node " +
 516                         byteArrayToString(windowsAbsolutePath()) +
 517                         " at root 0x" +
 518                         Integer.toHexString(rootNativeHandle()) +
 519                         ". Windows RegOpenKey(...) returned error code " +
 520                         result[ERROR_CODE] + ".");
 521                 result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE;
 522                 if (result[ERROR_CODE] == ERROR_ACCESS_DENIED) {
 523                     throw new SecurityException(
 524                             "Could not open windows registry node " +
 525                             byteArrayToString(windowsAbsolutePath()) +
 526                             " at root 0x" +
 527                             Integer.toHexString(rootNativeHandle()) +
 528                             ": Access denied");
 529                 }
 530             }
 531             return result[NATIVE_HANDLE];
 532         } else {
 533             return openKey(rootNativeHandle(), windowsAbsolutePath, mask1, mask2);
 534         }
 535     }
 536 
 537      /**
 538      * Opens Windows registry key at a given relative path
 539      * with respect to a given Windows registry key.
 540      * @param windowsAbsolutePath Windows relative path of the
 541      *        key as a byte-encoded string.
 542      * @param nativeHandle handle to the base Windows key.
 543      * @param mask1 Preferred Windows security mask.
 544      * @param mask2 Alternate Windows security mask.
 545      * @return Windows registry key's handle.
 546      * @see #openKey(int)
 547      * @see #openKey(byte[],int)
 548      * @see #closeKey(int)
 549      */
 550     private int openKey(int nativeHandle, byte[] windowsRelativePath,
 551                         int mask1, int mask2) {
 552     /* If the path is short enough open at once. Otherwise split the path */
 553         if (windowsRelativePath.length <= MAX_WINDOWS_PATH_LENGTH + 1 ) {
 554             int[] result = WindowsRegOpenKey1(nativeHandle,
 555                                               windowsRelativePath, mask1);
 556             if (result[ERROR_CODE] == ERROR_ACCESS_DENIED && mask2 != mask1)
 557                 result = WindowsRegOpenKey1(nativeHandle,
 558                                             windowsRelativePath, mask2);
 559 
 560             if (result[ERROR_CODE] != ERROR_SUCCESS) {
 561                 logger().warning("Could not open windows registry node " +
 562                         byteArrayToString(windowsAbsolutePath()) +
 563                         " at root 0x" + Integer.toHexString(nativeHandle) +
 564                         ". Windows RegOpenKey(...) returned error code " +
 565                         result[ERROR_CODE] + ".");
 566                 result[NATIVE_HANDLE] = NULL_NATIVE_HANDLE;
 567             }
 568             return result[NATIVE_HANDLE];
 569         } else {
 570             int separatorPosition = -1;
 571             // Be greedy - open the longest possible path
 572             for (int i = MAX_WINDOWS_PATH_LENGTH; i > 0; i--) {
 573                 if (windowsRelativePath[i] == ((byte)'\\')) {
 574                     separatorPosition = i;
 575                     break;
 576                 }
 577             }
 578             // Split the path and do the recursion
 579             byte[] nextRelativeRoot = new byte[separatorPosition+1];
 580             System.arraycopy(windowsRelativePath, 0, nextRelativeRoot,0,
 581                                                       separatorPosition);
 582             nextRelativeRoot[separatorPosition] = 0;


 590                 return NULL_NATIVE_HANDLE;
 591             }
 592             int result = openKey(nextNativeHandle, nextRelativePath,
 593                                  mask1,mask2);
 594             closeKey(nextNativeHandle);
 595             return result;
 596         }
 597     }
 598 
 599      /**
 600      * Closes Windows registry key.
 601      * Logs a warning if Windows registry is unavailable.
 602      * @param key's Windows registry handle.
 603      * @see #openKey(int)
 604      * @see #openKey(byte[],int)
 605      * @see #openKey(int, byte[],int)
 606     */
 607     private void closeKey(int nativeHandle) {
 608         int result = WindowsRegCloseKey(nativeHandle);
 609         if (result != ERROR_SUCCESS) {
 610             logger().warning("Could not close windows registry node " +
 611                     byteArrayToString(windowsAbsolutePath()) +
 612                     " at root 0x" +
 613                     Integer.toHexString(rootNativeHandle()) +
 614                     ". Windows RegCloseKey(...) returned error code " +
 615                     result + ".");
 616         }
 617     }
 618 
 619      /**
 620      * Implements <tt>AbstractPreferences</tt> <tt>putSpi()</tt> method.
 621      * Puts name-value pair into the underlying Windows registry node.
 622      * Logs a warning, if Windows registry is unavailable.
 623      * @see #getSpi(String)
 624      */
 625     protected void putSpi(String javaName, String value) {
 626         int nativeHandle = openKey(KEY_SET_VALUE);
 627         if (nativeHandle == NULL_NATIVE_HANDLE) {
 628             isBackingStoreAvailable = false;
 629             return;
 630         }
 631         int result = WindowsRegSetValueEx1(nativeHandle,
 632                 toWindowsName(javaName), toWindowsValueString(value));
 633         if (result != ERROR_SUCCESS) {
 634             logger().warning("Could not assign value to key " +
 635                     byteArrayToString(toWindowsName(javaName)) +
 636                     " at Windows registry node " +
 637                     byteArrayToString(windowsAbsolutePath()) +
 638                     " at root 0x" +
 639                     Integer.toHexString(rootNativeHandle()) +
 640                     ". Windows RegSetValueEx(...) returned error code " +
 641                     result + ".");
 642             isBackingStoreAvailable = false;
 643         }
 644         closeKey(nativeHandle);
 645     }
 646 
 647     /**
 648      * Implements <tt>AbstractPreferences</tt> <tt>getSpi()</tt> method.
 649      * Gets a string value from the underlying Windows registry node.
 650      * Logs a warning, if Windows registry is unavailable.
 651      * @see #putSpi(String, String)
 652      */
 653     protected String getSpi(String javaName) {
 654         int nativeHandle = openKey(KEY_QUERY_VALUE);
 655         if (nativeHandle == NULL_NATIVE_HANDLE) {
 656             return null;
 657         }
 658         Object resultObject = WindowsRegQueryValueEx(nativeHandle,
 659                 toWindowsName(javaName));
 660         if (resultObject == null) {
 661             closeKey(nativeHandle);


 663         }
 664         closeKey(nativeHandle);
 665         return toJavaValueString((byte[]) resultObject);
 666     }
 667 
 668     /**
 669      * Implements <tt>AbstractPreferences</tt> <tt>removeSpi()</tt> method.
 670      * Deletes a string name-value pair from the underlying Windows registry
 671      * node, if this value still exists.
 672      * Logs a warning, if Windows registry is unavailable or key has already
 673      * been deleted.
 674      */
 675     protected void removeSpi(String key) {
 676         int nativeHandle = openKey(KEY_SET_VALUE);
 677         if (nativeHandle == NULL_NATIVE_HANDLE) {
 678         return;
 679         }
 680         int result =
 681             WindowsRegDeleteValue(nativeHandle, toWindowsName(key));
 682         if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
 683             logger().warning("Could not delete windows registry value " +
 684                     byteArrayToString(windowsAbsolutePath()) + "\\" +
 685                     toWindowsName(key) + " at root 0x" +
 686                     Integer.toHexString(rootNativeHandle()) +
 687                     ". Windows RegDeleteValue(...) returned error code " +
 688                     result + ".");
 689             isBackingStoreAvailable = false;
 690         }
 691         closeKey(nativeHandle);
 692     }
 693 
 694     /**
 695      * Implements <tt>AbstractPreferences</tt> <tt>keysSpi()</tt> method.
 696      * Gets value names from the underlying Windows registry node.
 697      * Throws a BackingStoreException and logs a warning, if
 698      * Windows registry is unavailable.
 699      */
 700     protected String[] keysSpi() throws BackingStoreException{
 701         // Find out the number of values
 702         int nativeHandle = openKey(KEY_QUERY_VALUE);
 703         if (nativeHandle == NULL_NATIVE_HANDLE) {
 704             throw new BackingStoreException(
 705                     "Could not open windows registry node " +
 706                     byteArrayToString(windowsAbsolutePath()) +
 707                     " at root 0x" +
 708                     Integer.toHexString(rootNativeHandle()) + ".");
 709         }
 710         int[] result =  WindowsRegQueryInfoKey1(nativeHandle);
 711         if (result[ERROR_CODE] != ERROR_SUCCESS) {
 712             String info = "Could not query windows registry node " +
 713                     byteArrayToString(windowsAbsolutePath()) +
 714                     " at root 0x" +
 715                     Integer.toHexString(rootNativeHandle()) +
 716                     ". Windows RegQueryInfoKeyEx(...) returned error code " +
 717                     result[ERROR_CODE] + ".";
 718             logger().warning(info);
 719             throw new BackingStoreException(info);
 720         }
 721         int maxValueNameLength = result[MAX_VALUE_NAME_LENGTH];
 722         int valuesNumber = result[VALUES_NUMBER];
 723         if (valuesNumber == 0) {
 724             closeKey(nativeHandle);
 725             return new String[0];
 726         }
 727         // Get the values
 728         String[] valueNames = new String[valuesNumber];
 729         for (int i = 0; i < valuesNumber; i++) {
 730             byte[] windowsName = WindowsRegEnumValue1(nativeHandle, i,
 731                                                       maxValueNameLength+1);
 732             if (windowsName == null) {
 733                 String info =
 734                     "Could not enumerate value #" + i + "  of windows node " +
 735                     byteArrayToString(windowsAbsolutePath()) + " at root 0x" +
 736                     Integer.toHexString(rootNativeHandle()) + ".";
 737                 logger().warning(info);
 738                 throw new BackingStoreException(info);
 739             }
 740             valueNames[i] = toJavaName(windowsName);
 741         }
 742         closeKey(nativeHandle);
 743         return valueNames;
 744     }
 745 
 746     /**
 747      * Implements <tt>AbstractPreferences</tt> <tt>childrenNamesSpi()</tt> method.
 748      * Calls Windows registry to retrive children of this node.
 749      * Throws a BackingStoreException and logs a warning message,
 750      * if Windows registry is not available.
 751      */
 752     protected String[] childrenNamesSpi() throws BackingStoreException {
 753         // Open key
 754         int nativeHandle = openKey(KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE);
 755         if (nativeHandle == NULL_NATIVE_HANDLE) {
 756             throw new BackingStoreException(
 757                     "Could not open windows registry node " +
 758                     byteArrayToString(windowsAbsolutePath()) +
 759                     " at root 0x" +
 760                     Integer.toHexString(rootNativeHandle()) + ".");
 761         }
 762         // Get number of children
 763         int[] result =  WindowsRegQueryInfoKey1(nativeHandle);
 764         if (result[ERROR_CODE] != ERROR_SUCCESS) {
 765             String info = "Could not query windows registry node " + 
 766                     byteArrayToString(windowsAbsolutePath()) +
 767                     " at root 0x" + Integer.toHexString(rootNativeHandle()) +
 768                     ". Windows RegQueryInfoKeyEx(...) returned error code " +
 769                     result[ERROR_CODE] + ".";
 770             logger().warning(info);
 771             throw new BackingStoreException(info);
 772         }
 773         int maxKeyLength = result[MAX_KEY_LENGTH];
 774         int subKeysNumber = result[SUBKEYS_NUMBER];
 775         if (subKeysNumber == 0) {
 776             closeKey(nativeHandle);
 777             return new String[0];
 778         }
 779         String[] subkeys = new String[subKeysNumber];
 780         String[] children = new String[subKeysNumber];
 781         // Get children
 782         for (int i = 0; i < subKeysNumber; i++) {
 783             byte[] windowsName = WindowsRegEnumKeyEx1(nativeHandle, i,
 784                                                       maxKeyLength+1);
 785             if (windowsName == null) {
 786                 String info =


 798     }
 799 
 800     /**
 801      * Implements <tt>Preferences</tt> <tt>flush()</tt> method.
 802      * Flushes Windows registry changes to disk.
 803      * Throws a BackingStoreException and logs a warning message if Windows
 804      * registry is not available.
 805      */
 806     public void flush() throws BackingStoreException{
 807 
 808         if (isRemoved()) {
 809             parent.flush();
 810             return;
 811         }
 812         if (!isBackingStoreAvailable) {
 813             throw new BackingStoreException(
 814                     "flush(): Backing store not available.");
 815         }
 816         int nativeHandle = openKey(KEY_READ);
 817         if (nativeHandle == NULL_NATIVE_HANDLE) {
 818             throw new BackingStoreException(
 819                     "Could not open windows registry node " +
 820                     byteArrayToString(windowsAbsolutePath()) +
 821                     " at root 0x" +
 822                     Integer.toHexString(rootNativeHandle()) + ".");
 823         }
 824         int result = WindowsRegFlushKey1(nativeHandle);
 825         if (result != ERROR_SUCCESS) {
 826             String info = "Could not flush windows registry node " +
 827                     byteArrayToString(windowsAbsolutePath()) +
 828                     " at root 0x" +
 829                     Integer.toHexString(rootNativeHandle()) +
 830                     ". Windows RegFlushKey(...) returned error code " +
 831                     result + ".";
 832             logger().warning(info);
 833             throw new BackingStoreException(info);
 834         }
 835         closeKey(nativeHandle);
 836     }
 837 
 838 
 839     /**
 840      * Implements <tt>Preferences</tt> <tt>sync()</tt> method.
 841      * Flushes Windows registry changes to disk. Equivalent to flush().
 842      * @see flush()
 843      */
 844     public void sync() throws BackingStoreException{
 845         if (isRemoved())
 846             throw new IllegalStateException("Node has been removed");
 847         flush();
 848     }
 849 
 850     /**
 851      * Implements <tt>AbstractPreferences</tt> <tt>childSpi()</tt> method.
 852      * Constructs a child node with a
 853      * given name and creates its underlying Windows registry node,
 854      * if it does not exist.
 855      * Logs a warning message, if Windows Registry is unavailable.
 856      */
 857     protected AbstractPreferences childSpi(String name) {
 858         return new WindowsPreferences(this, name);
 859     }
 860 
 861     /**
 862      * Implements <tt>AbstractPreferences</tt> <tt>removeNodeSpi()</tt> method.
 863      * Deletes underlying Windows registry node.
 864      * Throws a BackingStoreException and logs a warning, if Windows registry
 865      * is not available.
 866      */
 867     public void removeNodeSpi() throws BackingStoreException {
 868         int parentNativeHandle =
 869                 ((WindowsPreferences)parent()).openKey(DELETE);
 870         if (parentNativeHandle == NULL_NATIVE_HANDLE) {
 871             throw new BackingStoreException(
 872                     "Could not open parent windows registry node of " +
 873                     byteArrayToString(windowsAbsolutePath()) +
 874                     " at root 0x" +
 875                     Integer.toHexString(rootNativeHandle()) + ".");
 876         }
 877         int result =
 878                 WindowsRegDeleteKey(parentNativeHandle, toWindowsName(name()));
 879         if (result != ERROR_SUCCESS) {
 880             String info = "Could not delete windows registry node " +
 881                     byteArrayToString(windowsAbsolutePath()) +
 882                     " at root 0x" + Integer.toHexString(rootNativeHandle()) +
 883                     ". Windows RegDeleteKeyEx(...) returned error code " +
 884                     result + ".";
 885             logger().warning(info);
 886             throw new BackingStoreException(info);
 887         }
 888         closeKey(parentNativeHandle);
 889     }
 890 
 891     /**
 892      * Converts value's or node's name from its byte array representation to
 893      * java string. Two encodings, simple and altBase64 are used. See
 894      * {@link #toWindowsName(String) toWindowsName()} for a detailed
 895      * description of encoding conventions.
 896      * @param windowsNameArray Null-terminated byte array.
 897      */
 898     private static String toJavaName(byte[] windowsNameArray) {
 899         String windowsName = byteArrayToString(windowsNameArray);
 900         // check if Alt64
 901         if ((windowsName.length() > 1) &&
 902                 (windowsName.substring(0, 2).equals("/!"))) {
 903             return toJavaAlt64Name(windowsName);
 904         }
 905         StringBuilder javaName = new StringBuilder();
 906         char ch;
 907         // Decode from simple encoding
 908         for (int i = 0; i < windowsName.length(); i++) {
 909             if ((ch = windowsName.charAt(i)) == '/') {
 910                 char next = ' ';
 911                 if ((windowsName.length() > i + 1) &&
 912                         ((next = windowsName.charAt(i+1)) >= 'A') &&
 913                         (next <= 'Z')) {
 914                     ch = next;
 915                     i++;
 916                 } else if ((windowsName.length() > i + 1) &&
 917                            (next == '/')) {
 918                     ch = '\\';
 919                     i++;
 920                 }
 921             } else if (ch == '\\') {
 922                 ch = '/';
 923             }
 924             javaName.append(ch);
 925         }
 926         return javaName.toString();
 927     }
 928 
 929     /**
 930      * Converts value's or node's name from its Windows representation to java
 931      * string, using altBase64 encoding. See
 932      * {@link #toWindowsName(String) toWindowsName()} for a detailed
 933      * description of encoding conventions.
 934      */
 935 
 936     private static String toJavaAlt64Name(String windowsName) {
 937         byte[] byteBuffer =
 938                 Base64.altBase64ToByteArray(windowsName.substring(2));
 939         StringBuilder result = new StringBuilder();
 940         for (int i = 0; i < byteBuffer.length; i++) {
 941             int firstbyte = (byteBuffer[i++] & 0xff);
 942             int secondbyte =  (byteBuffer[i] & 0xff);
 943             result.append((char)((firstbyte << 8) + secondbyte));
 944         }
 945         return result.toString();
 946     }
 947 
 948     /**
 949      * Converts value's or node's name to its Windows representation
 950      * as a byte-encoded string.
 951      * Two encodings, simple and altBase64 are used.
 952      * <p>
 953      * <i>Simple</i> encoding is used, if java string does not contain
 954      * any characters less, than 0x0020, or greater, than 0x007f.
 955      * Simple encoding adds "/" character to capital letters, i.e.
 956      * "A" is encoded as "/A". Character '\' is encoded as '//',
 957      * '/' is encoded as '\'.
 958      * The constructed string is converted to byte array by truncating the
 959      * highest byte and adding the terminating <tt>null</tt> character.
 960      * <p>
 961      * <i>altBase64</i>  encoding is used, if java string does contain at least
 962      * one character less, than 0x0020, or greater, than 0x007f.
 963      * This encoding is marked by setting first two bytes of the
 964      * Windows string to '/!'. The java name is then encoded using
 965      * byteArrayToAltBase64() method from
 966      * Base64 class.
 967      */
 968     private static byte[] toWindowsName(String javaName) {
 969         StringBuilder windowsName = new StringBuilder();
 970         for (int i = 0; i < javaName.length(); i++) {
 971             char ch = javaName.charAt(i);
 972             if ((ch < 0x0020) || (ch > 0x007f)) {
 973                 // If a non-trivial character encountered, use altBase64
 974                 return toWindowsAlt64Name(javaName);
 975             }
 976             if (ch == '\\') {
 977                 windowsName.append("//");
 978             } else if (ch == '/') {
 979                 windowsName.append('\\');
 980             } else if ((ch >= 'A') && (ch <='Z')) {
 981                 windowsName.append('/').append(ch);
 982             } else {
 983                 windowsName.append(ch);
 984             }
 985         }
 986         return stringToByteArray(windowsName.toString());
 987     }
 988 
 989     /**
 990      * Converts value's or node's name to its Windows representation
 991      * as a byte-encoded string, using altBase64 encoding. See
 992      * {@link #toWindowsName(String) toWindowsName()} for a detailed
 993      * description of encoding conventions.
 994      */
 995     private static byte[] toWindowsAlt64Name(String javaName) {
 996         byte[] javaNameArray = new byte[2*javaName.length()];
 997         // Convert to byte pairs
 998         int counter = 0;
 999         for (int i = 0; i < javaName.length();i++) {
1000             int ch = javaName.charAt(i);
1001             javaNameArray[counter++] = (byte)(ch >>> 8);
1002             javaNameArray[counter++] = (byte)ch;
1003         }
1004 
1005         return stringToByteArray("/!" +
1006                 Base64.byteArrayToAltBase64(javaNameArray));
1007     }
1008 
1009     /**
1010      * Converts value string from its Windows representation
1011      * to java string.  See
1012      * {@link #toWindowsValueString(String) toWindowsValueString()} for the
1013      * description of the encoding algorithm.
1014      */
1015      private static String toJavaValueString(byte[] windowsNameArray) {
1016         // Use modified native2ascii algorithm
1017         String windowsName = byteArrayToString(windowsNameArray);
1018         StringBuilder javaName = new StringBuilder();
1019         char ch;
1020         for (int i = 0; i < windowsName.length(); i++){
1021             if ((ch = windowsName.charAt(i)) == '/') {
1022                 char next = ' ';
1023 
1024                 if (windowsName.length() > i + 1 &&
1025                         (next = windowsName.charAt(i + 1)) == 'u') {
1026                     if (windowsName.length() < i + 6) {
1027                         break;
1028                     } else {
1029                         ch = (char)Integer.parseInt(
1030                                 windowsName.substring(i + 2, i + 6), 16);
1031                         i += 5;
1032                     }
1033                 } else
1034                 if ((windowsName.length() > i + 1) &&
1035                         ((windowsName.charAt(i+1)) >= 'A') &&
1036                         (next <= 'Z')) {
1037                     ch = next;
1038                     i++;
1039                 } else if ((windowsName.length() > i + 1) &&
1040                         (next == '/')) {
1041                     ch = '\\';
1042                     i++;
1043                 }
1044             } else if (ch == '\\') {
1045                 ch = '/';
1046             }
1047             javaName.append(ch);
1048         }
1049         return javaName.toString();
1050     }
1051 
1052     /**
1053      * Converts value string to it Windows representation.
1054      * as a byte-encoded string.
1055      * Encoding algorithm adds "/" character to capital letters, i.e.
1056      * "A" is encoded as "/A". Character '\' is encoded as '//',
1057      * '/' is encoded as  '\'.
1058      * Then encoding scheme similar to jdk's native2ascii converter is used
1059      * to convert java string to a byte array of ASCII characters.
1060      */
1061     private static byte[] toWindowsValueString(String javaName) {
1062         StringBuilder windowsName = new StringBuilder();
1063         for (int i = 0; i < javaName.length(); i++) {
1064             char ch = javaName.charAt(i);
1065             if ((ch < 0x0020) || (ch > 0x007f)){
1066                 // write \udddd
1067                 windowsName.append("/u");
1068                 String hex = Integer.toHexString(javaName.charAt(i));
1069                 StringBuilder hex4 = new StringBuilder(hex);
1070                 hex4.reverse();
1071                 int len = 4 - hex4.length();
1072                 for (int j = 0; j < len; j++){
1073                     hex4.append('0');
1074                 }
1075                 for (int j = 0; j < 4; j++){
1076                     windowsName.append(hex4.charAt(3 - j));
1077                 }
1078             } else if (ch == '\\') {
1079                 windowsName.append("//");
1080             } else if (ch == '/') {
1081                 windowsName.append('\\');
1082             } else if ((ch >= 'A') && (ch <='Z')) {
1083                 windowsName.append('/').append(ch);
1084             } else {
1085                 windowsName.append(ch);
1086             }
1087         }
1088         return stringToByteArray(windowsName.toString());
1089     }
1090 
1091     /**
1092      * Returns native handle for the top Windows node for this node.
1093      */
1094     private int rootNativeHandle() {
1095         return (isUserNode()
1096                 ? USER_ROOT_NATIVE_HANDLE
1097                 : SYSTEM_ROOT_NATIVE_HANDLE);
1098     }
1099 
1100     /**
1101      * Returns this java string as a null-terminated byte array
1102      */
1103     private static byte[] stringToByteArray(String str) {
1104         byte[] result = new byte[str.length()+1];
1105         for (int i = 0; i < str.length(); i++) {
1106             result[i] = (byte) str.charAt(i);
1107         }
1108         result[str.length()] = 0;
1109         return result;
1110     }
1111 
1112     /**
1113      * Converts a null-terminated byte array to java string
1114      */
1115     private static String byteArrayToString(byte[] array) {
1116         StringBuilder result = new StringBuilder();
1117         for (int i = 0; i < array.length - 1; i++) {
1118             result.append((char)array[i]);
1119         }
1120         return result.toString();
1121     }
1122 
1123    /**
1124     * Empty, never used implementation  of AbstractPreferences.flushSpi().
1125     */
1126     protected void flushSpi() throws BackingStoreException {
1127         // assert false;
1128     }
1129 
1130    /**
1131     * Empty, never used implementation  of AbstractPreferences.flushSpi().
1132     */
1133     protected void syncSpi() throws BackingStoreException {
1134         // assert false;
1135     }
1136 
< prev index next >