From 8762d54e63d2f6c3609a254e44be070f8c612903 Mon Sep 17 00:00:00 2001 From: Bharadwaj Yadavalli Date: Wed, 9 Jan 2013 11:39:30 -0500 Subject: [PATCH 001/138] 8005689: InterfaceAccessFlagsTest failures in Lambda-JDK tests Fix verifier for new interface access flags Reviewed-by: acorn, kvn --- .../share/vm/classfile/classFileParser.cpp | 40 +++++++++++++++---- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/hotspot/src/share/vm/classfile/classFileParser.cpp b/hotspot/src/share/vm/classfile/classFileParser.cpp index 9dd0640de06..efb6138e583 100644 --- a/hotspot/src/share/vm/classfile/classFileParser.cpp +++ b/hotspot/src/share/vm/classfile/classFileParser.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -3912,7 +3912,10 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, // check that if this class is an interface then it doesn't have static methods if (this_klass->is_interface()) { - check_illegal_static_method(this_klass, CHECK_(nullHandle)); + /* An interface in a JAVA 8 classfile can be static */ + if (_major_version < JAVA_8_VERSION) { + check_illegal_static_method(this_klass, CHECK_(nullHandle)); + } } @@ -4466,6 +4469,7 @@ void ClassFileParser::verify_legal_method_modifiers( const bool is_bridge = (flags & JVM_ACC_BRIDGE) != 0; const bool is_strict = (flags & JVM_ACC_STRICT) != 0; const bool is_synchronized = (flags & JVM_ACC_SYNCHRONIZED) != 0; + const bool is_protected = (flags & JVM_ACC_PROTECTED) != 0; const bool major_gte_15 = _major_version >= JAVA_1_5_VERSION; const bool major_gte_8 = _major_version >= JAVA_8_VERSION; const bool is_initializer = (name == vmSymbols::object_initializer_name()); @@ -4473,11 +4477,33 @@ void ClassFileParser::verify_legal_method_modifiers( bool is_illegal = false; if (is_interface) { - if (!is_public || is_static || is_final || is_native || - ((is_synchronized || is_strict) && major_gte_15 && - (!major_gte_8 || is_abstract)) || - (!major_gte_8 && !is_abstract)) { - is_illegal = true; + if (major_gte_8) { + // Class file version is JAVA_8_VERSION or later Methods of + // interfaces may set any of the flags except ACC_PROTECTED, + // ACC_FINAL, ACC_NATIVE, and ACC_SYNCHRONIZED; they must + // have exactly one of the ACC_PUBLIC or ACC_PRIVATE flags set. + if ((is_public == is_private) || /* Only one of private and public should be true - XNOR */ + (is_native || is_protected || is_final || is_synchronized) || + // If a specific method of a class or interface has its + // ACC_ABSTRACT flag set, it must not have any of its + // ACC_FINAL, ACC_NATIVE, ACC_PRIVATE, ACC_STATIC, + // ACC_STRICT, or ACC_SYNCHRONIZED flags set. No need to + // check for ACC_FINAL, ACC_NATIVE or ACC_SYNCHRONIZED as + // those flags are illegal irrespective of ACC_ABSTRACT being set or not. + (is_abstract && (is_private || is_static || is_strict))) { + is_illegal = true; + } + } else if (major_gte_15) { + // Class file version in the interval [JAVA_1_5_VERSION, JAVA_8_VERSION) + if (!is_public || is_static || is_final || is_synchronized || + is_native || !is_abstract || is_strict) { + is_illegal = true; + } + } else { + // Class file version is pre-JAVA_1_5_VERSION + if (!is_public || is_static || is_final || is_native || !is_abstract) { + is_illegal = true; + } } } else { // not interface if (is_initializer) { From 030fa5107d44b37dce7a61306b186bd9eb6ceb28 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Wed, 9 Jan 2013 14:46:55 -0500 Subject: [PATCH 002/138] 7152671: RFE: Windows decoder should add some std dirs to the symbol search path Added JRE/JDK bin directories to decoder's symbol search path Reviewed-by: dcubed, sla --- hotspot/src/os/windows/vm/decoder_windows.cpp | 78 ++++++++++++++++++- hotspot/src/os/windows/vm/decoder_windows.hpp | 2 + 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/hotspot/src/os/windows/vm/decoder_windows.cpp b/hotspot/src/os/windows/vm/decoder_windows.cpp index 82ba909bc5c..2b4682728a2 100644 --- a/hotspot/src/os/windows/vm/decoder_windows.cpp +++ b/hotspot/src/os/windows/vm/decoder_windows.cpp @@ -49,7 +49,7 @@ void WindowsDecoder::initialize() { pfn_SymSetOptions _pfnSymSetOptions = (pfn_SymSetOptions)::GetProcAddress(handle, "SymSetOptions"); pfn_SymInitialize _pfnSymInitialize = (pfn_SymInitialize)::GetProcAddress(handle, "SymInitialize"); _pfnSymGetSymFromAddr64 = (pfn_SymGetSymFromAddr64)::GetProcAddress(handle, "SymGetSymFromAddr64"); - _pfnUndecorateSymbolName = (pfn_UndecorateSymbolName)GetProcAddress(handle, "UnDecorateSymbolName"); + _pfnUndecorateSymbolName = (pfn_UndecorateSymbolName)::GetProcAddress(handle, "UnDecorateSymbolName"); if (_pfnSymSetOptions == NULL || _pfnSymInitialize == NULL || _pfnSymGetSymFromAddr64 == NULL) { _pfnSymGetSymFromAddr64 = NULL; @@ -60,8 +60,9 @@ void WindowsDecoder::initialize() { return; } - _pfnSymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); - if (!_pfnSymInitialize(GetCurrentProcess(), NULL, TRUE)) { + HANDLE hProcess = ::GetCurrentProcess(); + _pfnSymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_EXACT_SYMBOLS); + if (!_pfnSymInitialize(hProcess, NULL, TRUE)) { _pfnSymGetSymFromAddr64 = NULL; _pfnUndecorateSymbolName = NULL; ::FreeLibrary(handle); @@ -70,6 +71,77 @@ void WindowsDecoder::initialize() { return; } + // set pdb search paths + pfn_SymSetSearchPath _pfn_SymSetSearchPath = + (pfn_SymSetSearchPath)::GetProcAddress(handle, "SymSetSearchPath"); + pfn_SymGetSearchPath _pfn_SymGetSearchPath = + (pfn_SymGetSearchPath)::GetProcAddress(handle, "SymGetSearchPath"); + if (_pfn_SymSetSearchPath != NULL && _pfn_SymGetSearchPath != NULL) { + char paths[MAX_PATH]; + int len = sizeof(paths); + if (!_pfn_SymGetSearchPath(hProcess, paths, len)) { + paths[0] = '\0'; + } else { + // available spaces in path buffer + len -= (int)strlen(paths); + } + + char tmp_path[MAX_PATH]; + DWORD dwSize; + HMODULE hJVM = ::GetModuleHandle("jvm.dll"); + tmp_path[0] = '\0'; + // append the path where jvm.dll is located + if (hJVM != NULL && (dwSize = ::GetModuleFileName(hJVM, tmp_path, sizeof(tmp_path))) > 0) { + while (dwSize > 0 && tmp_path[dwSize] != '\\') { + dwSize --; + } + + tmp_path[dwSize] = '\0'; + + if (dwSize > 0 && len > (int)dwSize + 1) { + strncat(paths, os::path_separator(), 1); + strncat(paths, tmp_path, dwSize); + len -= dwSize + 1; + } + } + + // append $JRE/bin. Arguments::get_java_home actually returns $JRE + // path + char *p = Arguments::get_java_home(); + assert(p != NULL, "empty java home"); + size_t java_home_len = strlen(p); + if (len > (int)java_home_len + 5) { + strncat(paths, os::path_separator(), 1); + strncat(paths, p, java_home_len); + strncat(paths, "\\bin", 4); + len -= (int)(java_home_len + 5); + } + + // append $JDK/bin path if it exists + assert(java_home_len < MAX_PATH, "Invalid path length"); + // assume $JRE is under $JDK, construct $JDK/bin path and + // see if it exists or not + if (strncmp(&p[java_home_len - 3], "jre", 3) == 0) { + strncpy(tmp_path, p, java_home_len - 3); + tmp_path[java_home_len - 3] = '\0'; + strncat(tmp_path, "bin", 3); + + // if the directory exists + DWORD dwAttrib = GetFileAttributes(tmp_path); + if (dwAttrib != INVALID_FILE_ATTRIBUTES && + (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) { + // tmp_path should have the same length as java_home_len, since we only + // replaced 'jre' with 'bin' + if (len > (int)java_home_len + 1) { + strncat(paths, os::path_separator(), 1); + strncat(paths, tmp_path, java_home_len); + } + } + } + + _pfn_SymSetSearchPath(hProcess, paths); + } + // find out if jvm.dll contains private symbols, by decoding // current function and comparing the result address addr = (address)Decoder::demangle; diff --git a/hotspot/src/os/windows/vm/decoder_windows.hpp b/hotspot/src/os/windows/vm/decoder_windows.hpp index b2c2638d8ab..3008ee79136 100644 --- a/hotspot/src/os/windows/vm/decoder_windows.hpp +++ b/hotspot/src/os/windows/vm/decoder_windows.hpp @@ -35,6 +35,8 @@ typedef DWORD (WINAPI *pfn_SymSetOptions)(DWORD); typedef BOOL (WINAPI *pfn_SymInitialize)(HANDLE, PCTSTR, BOOL); typedef BOOL (WINAPI *pfn_SymGetSymFromAddr64)(HANDLE, DWORD64, PDWORD64, PIMAGEHLP_SYMBOL64); typedef DWORD (WINAPI *pfn_UndecorateSymbolName)(const char*, char*, DWORD, DWORD); +typedef BOOL (WINAPI *pfn_SymSetSearchPath)(HANDLE, PCTSTR); +typedef BOOL (WINAPI *pfn_SymGetSearchPath)(HANDLE, PTSTR, int); class WindowsDecoder : public AbstractDecoder { From 472004ca58f6c7189b23d1298adec5550e991fad Mon Sep 17 00:00:00 2001 From: Karen Kinnear Date: Thu, 10 Jan 2013 17:38:20 -0500 Subject: [PATCH 003/138] 7199207: NPG: Crash in PlaceholderTable::verify after StackOverflow Reduce scope of placeholder table entries to improve cleanup Reviewed-by: dholmes, coleenp --- .../src/share/vm/classfile/placeholders.cpp | 12 +- .../src/share/vm/classfile/placeholders.hpp | 10 +- .../share/vm/classfile/systemDictionary.cpp | 174 ++++++------------ hotspot/src/share/vm/utilities/exceptions.hpp | 3 +- 4 files changed, 72 insertions(+), 127 deletions(-) diff --git a/hotspot/src/share/vm/classfile/placeholders.cpp b/hotspot/src/share/vm/classfile/placeholders.cpp index 1babaaf978c..10d7650db58 100644 --- a/hotspot/src/share/vm/classfile/placeholders.cpp +++ b/hotspot/src/share/vm/classfile/placeholders.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -142,7 +142,7 @@ PlaceholderEntry* PlaceholderTable::find_and_add(int index, unsigned int hash, } -// placeholder used to track class loading internal states +// placeholder is used to track class loading internal states // placeholder existence now for loading superclass/superinterface // superthreadQ tracks class circularity, while loading superclass/superinterface // loadInstanceThreadQ tracks load_instance_class calls @@ -153,15 +153,17 @@ PlaceholderEntry* PlaceholderTable::find_and_add(int index, unsigned int hash, // All claimants remove SeenThread after completing action // On removal: if definer and all queues empty, remove entry // Note: you can be in both placeholders and systemDictionary -// see parse_stream for redefine classes // Therefore - must always check SD first // Ignores the case where entry is not found void PlaceholderTable::find_and_remove(int index, unsigned int hash, - Symbol* name, ClassLoaderData* loader_data, Thread* thread) { + Symbol* name, ClassLoaderData* loader_data, + classloadAction action, + Thread* thread) { assert_locked_or_safepoint(SystemDictionary_lock); PlaceholderEntry *probe = get_entry(index, hash, name, loader_data); if (probe != NULL) { - // No other threads using this entry + probe->remove_seen_thread(thread, action); + // If no other threads using this entry, and this thread is not using this entry for other states if ((probe->superThreadQ() == NULL) && (probe->loadInstanceThreadQ() == NULL) && (probe->defineThreadQ() == NULL) && (probe->definer() == NULL)) { remove_entry(index, hash, name, loader_data); diff --git a/hotspot/src/share/vm/classfile/placeholders.hpp b/hotspot/src/share/vm/classfile/placeholders.hpp index af4518a462f..ca0d85af0fb 100644 --- a/hotspot/src/share/vm/classfile/placeholders.hpp +++ b/hotspot/src/share/vm/classfile/placeholders.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -82,7 +82,7 @@ public: }; // find_and_add returns probe pointer - old or new - // If no entry exists, add a placeholder entry and push SeenThread + // If no entry exists, add a placeholder entry and push SeenThread for classloadAction // If entry exists, reuse entry and push SeenThread for classloadAction PlaceholderEntry* find_and_add(int index, unsigned int hash, Symbol* name, ClassLoaderData* loader_data, @@ -92,9 +92,11 @@ public: void remove_entry(int index, unsigned int hash, Symbol* name, ClassLoaderData* loader_data); -// Remove placeholder information + // find_and_remove first removes SeenThread for classloadAction + // If all queues are empty and definer is null, remove the PlacheholderEntry completely void find_and_remove(int index, unsigned int hash, - Symbol* name, ClassLoaderData* loader_data, Thread* thread); + Symbol* name, ClassLoaderData* loader_data, + classloadAction action, Thread* thread); // GC support. void classes_do(KlassClosure* f); diff --git a/hotspot/src/share/vm/classfile/systemDictionary.cpp b/hotspot/src/share/vm/classfile/systemDictionary.cpp index a493e73d84d..54601625056 100644 --- a/hotspot/src/share/vm/classfile/systemDictionary.cpp +++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -172,7 +172,7 @@ Klass* SystemDictionary::handle_resolution_exception(Symbol* class_name, Handle assert(klass_h() == NULL, "Should not have result with exception pending"); Handle e(THREAD, PENDING_EXCEPTION); CLEAR_PENDING_EXCEPTION; - THROW_MSG_CAUSE_0(vmSymbols::java_lang_NoClassDefFoundError(), class_name->as_C_string(), e); + THROW_MSG_CAUSE_NULL(vmSymbols::java_lang_NoClassDefFoundError(), class_name->as_C_string(), e); } else { return NULL; } @@ -181,9 +181,9 @@ Klass* SystemDictionary::handle_resolution_exception(Symbol* class_name, Handle if (klass_h() == NULL) { ResourceMark rm(THREAD); if (throw_error) { - THROW_MSG_0(vmSymbols::java_lang_NoClassDefFoundError(), class_name->as_C_string()); + THROW_MSG_NULL(vmSymbols::java_lang_NoClassDefFoundError(), class_name->as_C_string()); } else { - THROW_MSG_0(vmSymbols::java_lang_ClassNotFoundException(), class_name->as_C_string()); + THROW_MSG_NULL(vmSymbols::java_lang_ClassNotFoundException(), class_name->as_C_string()); } } return (Klass*)klass_h(); @@ -343,29 +343,29 @@ Klass* SystemDictionary::resolve_super_or_fail(Symbol* child_name, } if (throw_circularity_error) { ResourceMark rm(THREAD); - THROW_MSG_0(vmSymbols::java_lang_ClassCircularityError(), child_name->as_C_string()); + THROW_MSG_NULL(vmSymbols::java_lang_ClassCircularityError(), child_name->as_C_string()); } // java.lang.Object should have been found above assert(class_name != NULL, "null super class for resolving"); // Resolve the super class or interface, check results on return - Klass* superk = NULL; - superk = SystemDictionary::resolve_or_null(class_name, + Klass* superk = SystemDictionary::resolve_or_null(class_name, class_loader, protection_domain, THREAD); KlassHandle superk_h(THREAD, superk); - // Note: clean up of placeholders currently in callers of - // resolve_super_or_fail - either at update_dictionary time - // or on error + // Clean up of placeholders moved so that each classloadAction registrar self-cleans up + // It is no longer necessary to keep the placeholder table alive until update_dictionary + // or error. GC used to walk the placeholder table as strong roots. + // The instanceKlass is kept alive because the class loader is on the stack, + // which keeps the loader_data alive, as well as all instanceKlasses in + // the loader_data. parseClassFile adds the instanceKlass to loader_data. { - MutexLocker mu(SystemDictionary_lock, THREAD); - PlaceholderEntry* probe = placeholders()->get_entry(p_index, p_hash, child_name, loader_data); - if (probe != NULL) { - probe->remove_seen_thread(THREAD, PlaceholderTable::LOAD_SUPER); - } + MutexLocker mu(SystemDictionary_lock, THREAD); + placeholders()->find_and_remove(p_index, p_hash, child_name, loader_data, PlaceholderTable::LOAD_SUPER, THREAD); + SystemDictionary_lock->notify_all(); } if (HAS_PENDING_EXCEPTION || superk_h() == NULL) { // can null superk @@ -430,8 +430,8 @@ void SystemDictionary::validate_protection_domain(instanceKlassHandle klass, // We're using a No_Safepoint_Verifier to catch any place where we // might potentially do a GC at all. - // SystemDictionary::do_unloading() asserts that classes are only - // unloaded at a safepoint. + // Dictionary::do_unloading() asserts that classes in SD are only + // unloaded at a safepoint. Anonymous classes are not in SD. No_Safepoint_Verifier nosafepoint; dictionary()->add_protection_domain(d_index, d_hash, klass, loader_data, protection_domain, THREAD); @@ -486,7 +486,6 @@ void SystemDictionary::double_lock_wait(Handle lockObject, TRAPS) { // super class loading here. // This also is critical in cases where the original thread gets stalled // even in non-circularity situations. -// Note: only one thread can define the class, but multiple can resolve // Note: must call resolve_super_or_fail even if null super - // to force placeholder entry creation for this class for circularity detection // Caller must check for pending exception @@ -518,14 +517,6 @@ instanceKlassHandle SystemDictionary::handle_parallel_super_load( protection_domain, true, CHECK_(nh)); - // We don't redefine the class, so we just need to clean up if there - // was not an error (don't want to modify any system dictionary - // data structures). - { - MutexLocker mu(SystemDictionary_lock, THREAD); - placeholders()->find_and_remove(p_index, p_hash, name, loader_data, THREAD); - SystemDictionary_lock->notify_all(); - } // parallelCapable class loaders do NOT wait for parallel superclass loads to complete // Serial class loaders and bootstrap classloader do wait for superclass loads @@ -595,6 +586,10 @@ Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, Handle cla // Do lookup to see if class already exist and the protection domain // has the right access + // This call uses find which checks protection domain already matches + // All subsequent calls use find_class, and set has_loaded_class so that + // before we return a result we call out to java to check for valid protection domain + // to allow returning the Klass* and add it to the pd_set if it is valid unsigned int d_hash = dictionary()->compute_hash(name, loader_data); int d_index = dictionary()->hash_to_index(d_hash); Klass* probe = dictionary()->find(d_index, d_hash, name, loader_data, @@ -652,7 +647,7 @@ Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, Handle cla } } - // If the class in is in the placeholder table, class loading is in progress + // If the class is in the placeholder table, class loading is in progress if (super_load_in_progress && havesupername==true) { k = SystemDictionary::handle_parallel_super_load(name, superclassname, class_loader, protection_domain, lockObject, THREAD); @@ -664,7 +659,9 @@ Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, Handle cla } } + bool throw_circularity_error = false; if (!class_has_been_loaded) { + bool load_instance_added = false; // add placeholder entry to record loading instance class // Five cases: @@ -690,7 +687,7 @@ Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, Handle cla // No performance benefit and no deadlock issues. // case 5. parallelCapable user level classloaders - without objectLocker // Allow parallel classloading of a class/classloader pair - bool throw_circularity_error = false; + { MutexLocker mu(SystemDictionary_lock, THREAD); if (class_loader.is_null() || !is_parallelCapable(class_loader)) { @@ -726,12 +723,13 @@ Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, Handle cla } } } - // All cases: add LOAD_INSTANCE + // All cases: add LOAD_INSTANCE holding SystemDictionary_lock // case 3: UnsyncloadClass || case 5: parallelCapable: allow competing threads to try // LOAD_INSTANCE in parallel - // add placeholder entry even if error - callers will remove on error + if (!throw_circularity_error && !class_has_been_loaded) { PlaceholderEntry* newprobe = placeholders()->find_and_add(p_index, p_hash, name, loader_data, PlaceholderTable::LOAD_INSTANCE, NULL, THREAD); + load_instance_added = true; // For class loaders that do not acquire the classloader object lock, // if they did not catch another thread holding LOAD_INSTANCE, // need a check analogous to the acquire ObjectLocker/find_class @@ -740,19 +738,18 @@ Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, Handle cla // class loaders holding the ObjectLock shouldn't find the class here Klass* check = find_class(d_index, d_hash, name, loader_data); if (check != NULL) { - // Klass is already loaded, so just return it + // Klass is already loaded, so return it after checking/adding protection domain k = instanceKlassHandle(THREAD, check); class_has_been_loaded = true; - newprobe->remove_seen_thread(THREAD, PlaceholderTable::LOAD_INSTANCE); - placeholders()->find_and_remove(p_index, p_hash, name, loader_data, THREAD); - SystemDictionary_lock->notify_all(); } } } + // must throw error outside of owning lock if (throw_circularity_error) { + assert(!HAS_PENDING_EXCEPTION && load_instance_added == false,"circularity error cleanup"); ResourceMark rm(THREAD); - THROW_MSG_0(vmSymbols::java_lang_ClassCircularityError(), name->as_C_string()); + THROW_MSG_NULL(vmSymbols::java_lang_ClassCircularityError(), name->as_C_string()); } if (!class_has_been_loaded) { @@ -782,20 +779,6 @@ Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, Handle cla } } - // clean up placeholder entries for success or error - // This cleans up LOAD_INSTANCE entries - // It also cleans up LOAD_SUPER entries on errors from - // calling load_instance_class - { - MutexLocker mu(SystemDictionary_lock, THREAD); - PlaceholderEntry* probe = placeholders()->get_entry(p_index, p_hash, name, loader_data); - if (probe != NULL) { - probe->remove_seen_thread(THREAD, PlaceholderTable::LOAD_INSTANCE); - placeholders()->find_and_remove(p_index, p_hash, name, loader_data, THREAD); - SystemDictionary_lock->notify_all(); - } - } - // If everything was OK (no exceptions, no null return value), and // class_loader is NOT the defining loader, do a little more bookkeeping. if (!HAS_PENDING_EXCEPTION && !k.is_null() && @@ -819,18 +802,22 @@ Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, Handle cla } } } - if (HAS_PENDING_EXCEPTION || k.is_null()) { - // On error, clean up placeholders - { - MutexLocker mu(SystemDictionary_lock, THREAD); - placeholders()->find_and_remove(p_index, p_hash, name, loader_data, THREAD); - SystemDictionary_lock->notify_all(); - } - return NULL; - } + } // load_instance_class loop + + if (load_instance_added == true) { + // clean up placeholder entries for LOAD_INSTANCE success or error + // This brackets the SystemDictionary updates for both defining + // and initiating loaders + MutexLocker mu(SystemDictionary_lock, THREAD); + placeholders()->find_and_remove(p_index, p_hash, name, loader_data, PlaceholderTable::LOAD_INSTANCE, THREAD); + SystemDictionary_lock->notify_all(); } } + if (HAS_PENDING_EXCEPTION || k.is_null()) { + return NULL; + } + #ifdef ASSERT { ClassLoaderData* loader_data = k->class_loader_data(); @@ -850,8 +837,8 @@ Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, Handle cla // so we cannot allow GC to occur while we're holding this entry. // We're using a No_Safepoint_Verifier to catch any place where we // might potentially do a GC at all. - // SystemDictionary::do_unloading() asserts that classes are only - // unloaded at a safepoint. + // Dictionary::do_unloading() asserts that classes in SD are only + // unloaded at a safepoint. Anonymous classes are not in SD. No_Safepoint_Verifier nosafepoint; if (dictionary()->is_valid_protection_domain(d_index, d_hash, name, loader_data, @@ -898,8 +885,8 @@ Klass* SystemDictionary::find(Symbol* class_name, // so we cannot allow GC to occur while we're holding this entry. // We're using a No_Safepoint_Verifier to catch any place where we // might potentially do a GC at all. - // SystemDictionary::do_unloading() asserts that classes are only - // unloaded at a safepoint. + // Dictionary::do_unloading() asserts that classes in SD are only + // unloaded at a safepoint. Anonymous classes are not in SD. No_Safepoint_Verifier nosafepoint; return dictionary()->find(d_index, d_hash, class_name, loader_data, protection_domain, THREAD); @@ -965,10 +952,6 @@ Klass* SystemDictionary::parse_stream(Symbol* class_name, // throw potential ClassFormatErrors. // // Note: "name" is updated. - // Further note: a placeholder will be added for this class when - // super classes are loaded (resolve_super_or_fail). We expect this - // to be called for all classes but java.lang.Object; and we preload - // java.lang.Object through resolve_or_fail, not this path. instanceKlassHandle k = ClassFileParser(st).parseClassFile(class_name, loader_data, @@ -979,21 +962,6 @@ Klass* SystemDictionary::parse_stream(Symbol* class_name, true, THREAD); - // We don't redefine the class, so we just need to clean up whether there - // was an error or not (don't want to modify any system dictionary - // data structures). - // Parsed name could be null if we threw an error before we got far - // enough along to parse it -- in that case, there is nothing to clean up. - if (parsed_name != NULL) { - unsigned int p_hash = placeholders()->compute_hash(parsed_name, - loader_data); - int p_index = placeholders()->hash_to_index(p_hash); - { - MutexLocker mu(SystemDictionary_lock, THREAD); - placeholders()->find_and_remove(p_index, p_hash, parsed_name, loader_data, THREAD); - SystemDictionary_lock->notify_all(); - } - } if (host_klass.not_null() && k.not_null()) { assert(EnableInvokeDynamic, ""); @@ -1062,10 +1030,6 @@ Klass* SystemDictionary::resolve_from_stream(Symbol* class_name, // throw potential ClassFormatErrors. // // Note: "name" is updated. - // Further note: a placeholder will be added for this class when - // super classes are loaded (resolve_super_or_fail). We expect this - // to be called for all classes but java.lang.Object; and we preload - // java.lang.Object through resolve_or_fail, not this path. instanceKlassHandle k = ClassFileParser(st).parseClassFile(class_name, loader_data, @@ -1114,25 +1078,7 @@ Klass* SystemDictionary::resolve_from_stream(Symbol* class_name, } } - // If parsing the class file or define_instance_class failed, we - // need to remove the placeholder added on our behalf. But we - // must make sure parsed_name is valid first (it won't be if we had - // a format error before the class was parsed far enough to - // find the name). - if (HAS_PENDING_EXCEPTION && parsed_name != NULL) { - unsigned int p_hash = placeholders()->compute_hash(parsed_name, - loader_data); - int p_index = placeholders()->hash_to_index(p_hash); - { - MutexLocker mu(SystemDictionary_lock, THREAD); - placeholders()->find_and_remove(p_index, p_hash, parsed_name, loader_data, THREAD); - SystemDictionary_lock->notify_all(); - } - return NULL; - } - - // Make sure that we didn't leave a place holder in the - // SystemDictionary; this is only done on success + // Make sure we have an entry in the SystemDictionary on success debug_only( { if (!HAS_PENDING_EXCEPTION) { assert(parsed_name != NULL, "parsed_name is still null?"); @@ -1547,8 +1493,7 @@ instanceKlassHandle SystemDictionary::find_or_define_instance_class(Symbol* clas // Other cases fall through, and may run into duplicate defines // caught by finding an entry in the SystemDictionary if ((UnsyncloadClass || is_parallelDefine(class_loader)) && (probe->instance_klass() != NULL)) { - probe->remove_seen_thread(THREAD, PlaceholderTable::DEFINE_CLASS); - placeholders()->find_and_remove(p_index, p_hash, name_h, loader_data, THREAD); + placeholders()->find_and_remove(p_index, p_hash, name_h, loader_data, PlaceholderTable::DEFINE_CLASS, THREAD); SystemDictionary_lock->notify_all(); #ifdef ASSERT Klass* check = find_class(d_index, d_hash, name_h, loader_data); @@ -1578,8 +1523,7 @@ instanceKlassHandle SystemDictionary::find_or_define_instance_class(Symbol* clas probe->set_instance_klass(k()); } probe->set_definer(NULL); - probe->remove_seen_thread(THREAD, PlaceholderTable::DEFINE_CLASS); - placeholders()->find_and_remove(p_index, p_hash, name_h, loader_data, THREAD); + placeholders()->find_and_remove(p_index, p_hash, name_h, loader_data, PlaceholderTable::DEFINE_CLASS, THREAD); SystemDictionary_lock->notify_all(); } } @@ -1736,6 +1680,8 @@ int SystemDictionary::calculate_systemdictionary_size(int classcount) { } return newsize; } +// Assumes classes in the SystemDictionary are only unloaded at a safepoint +// Note: anonymous classes are not in the SD. bool SystemDictionary::do_unloading(BoolObjectClosure* is_alive) { // First, mark for unload all ClassLoaderData referencing a dead class loader. bool has_dead_loaders = ClassLoaderDataGraph::do_unloading(is_alive); @@ -2105,9 +2051,7 @@ void SystemDictionary::update_dictionary(int d_index, unsigned int d_hash, // All loaded classes get a unique ID. TRACE_INIT_ID(k); - // Check for a placeholder. If there, remove it and make a - // new system dictionary entry. - placeholders()->find_and_remove(p_index, p_hash, name, loader_data, THREAD); + // Make a new system dictionary entry. Klass* sd_check = find_class(d_index, d_hash, name, loader_data); if (sd_check == NULL) { dictionary()->add_klass(name, loader_data, k); @@ -2116,12 +2060,8 @@ void SystemDictionary::update_dictionary(int d_index, unsigned int d_hash, #ifdef ASSERT sd_check = find_class(d_index, d_hash, name, loader_data); assert (sd_check != NULL, "should have entry in system dictionary"); -// Changed to allow PH to remain to complete class circularity checking -// while only one thread can define a class at one time, multiple -// classes can resolve the superclass for a class at one time, -// and the placeholder is used to track that -// Symbol* ph_check = find_placeholder(name, class_loader); -// assert (ph_check == NULL, "should not have a placeholder entry"); + // Note: there may be a placeholder entry: for circularity testing + // or for parallel defines #endif SystemDictionary_lock->notify_all(); } diff --git a/hotspot/src/share/vm/utilities/exceptions.hpp b/hotspot/src/share/vm/utilities/exceptions.hpp index 740912d3909..089cd3e0811 100644 --- a/hotspot/src/share/vm/utilities/exceptions.hpp +++ b/hotspot/src/share/vm/utilities/exceptions.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -267,6 +267,7 @@ class Exceptions { #define THROW_WRAPPED_0(name, oop_to_wrap) THROW_WRAPPED_(name, oop_to_wrap, 0) #define THROW_ARG_0(name, signature, arg) THROW_ARG_(name, signature, arg, 0) #define THROW_MSG_CAUSE_0(name, message, cause) THROW_MSG_CAUSE_(name, message, cause, 0) +#define THROW_MSG_CAUSE_NULL(name, message, cause) THROW_MSG_CAUSE_(name, message, cause, NULL) #define THROW_NULL(name) THROW_(name, NULL) #define THROW_MSG_NULL(name, message) THROW_MSG_(name, message, NULL) From e7e6443c6d3c1e98d289306b38c5d2a101cc8e51 Mon Sep 17 00:00:00 2001 From: Mikael Vidstedt Date: Thu, 10 Jan 2013 17:06:26 -0800 Subject: [PATCH 004/138] 8004747: Remove last_entry from VM_STRUCT macros Instead of passing in last_entry to all the VM_ macros just expand it in the main vmStructs.cpp file. Reviewed-by: dholmes, sspitsyn, minqi --- hotspot/src/cpu/sparc/vm/vmStructs_sparc.hpp | 29 +- hotspot/src/cpu/x86/vm/vmStructs_x86.hpp | 27 +- hotspot/src/cpu/zero/vm/vmStructs_zero.hpp | 24 +- .../os_cpu/bsd_x86/vm/vmStructs_bsd_x86.hpp | 23 +- .../os_cpu/bsd_zero/vm/vmStructs_bsd_zero.hpp | 16 +- .../linux_sparc/vm/vmStructs_linux_sparc.hpp | 25 +- .../linux_x86/vm/vmStructs_linux_x86.hpp | 23 +- .../linux_zero/vm/vmStructs_linux_zero.hpp | 17 +- .../vm/vmStructs_solaris_sparc.hpp | 26 +- .../solaris_x86/vm/vmStructs_solaris_x86.hpp | 24 +- .../windows_x86/vm/vmStructs_windows_x86.hpp | 23 +- hotspot/src/share/vm/runtime/vmStructs.cpp | 248 +++++++++--------- 12 files changed, 179 insertions(+), 326 deletions(-) diff --git a/hotspot/src/cpu/sparc/vm/vmStructs_sparc.hpp b/hotspot/src/cpu/sparc/vm/vmStructs_sparc.hpp index 8103a6395b7..11f829385b8 100644 --- a/hotspot/src/cpu/sparc/vm/vmStructs_sparc.hpp +++ b/hotspot/src/cpu/sparc/vm/vmStructs_sparc.hpp @@ -29,7 +29,7 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field, last_entry) \ +#define VM_STRUCTS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ \ /******************************/ \ /* JavaCallWrapper */ \ @@ -37,22 +37,12 @@ /******************************/ \ /* JavaFrameAnchor */ \ /******************************/ \ - volatile_nonstatic_field(JavaFrameAnchor, _flags, int) \ - \ + volatile_nonstatic_field(JavaFrameAnchor, _flags, int) - /* NOTE that we do not use the last_entry() macro here; it is used */ - /* in vmStructs__.hpp's VM_STRUCTS_OS_CPU macro (and must */ - /* be present there) */ +#define VM_TYPES_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) -#define VM_TYPES_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type, last_entry) \ - - /* NOTE that we do not use the last_entry() macro here; it is used */ - /* in vmStructs__.hpp's VM_TYPES_OS_CPU macro (and must */ - /* be present there) */ - - -#define VM_INT_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ +#define VM_INT_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) \ /******************************/ \ /* Register numbers (C2 only) */ \ /******************************/ \ @@ -90,15 +80,6 @@ declare_c2_constant(R_G6_num) \ declare_c2_constant(R_G7_num) - - /* NOTE that we do not use the last_entry() macro here; it is used */ - /* in vmStructs__.hpp's VM_INT_CONSTANTS_OS_CPU macro (and must */ - /* be present there) */ - -#define VM_LONG_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ - - /* NOTE that we do not use the last_entry() macro here; it is used */ - /* in vmStructs__.hpp's VM_LONG_CONSTANTS_OS_CPU macro (and must */ - /* be present there) */ +#define VM_LONG_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) #endif // CPU_SPARC_VM_VMSTRUCTS_SPARC_HPP diff --git a/hotspot/src/cpu/x86/vm/vmStructs_x86.hpp b/hotspot/src/cpu/x86/vm/vmStructs_x86.hpp index 8dddc9c3e1d..847d08ed207 100644 --- a/hotspot/src/cpu/x86/vm/vmStructs_x86.hpp +++ b/hotspot/src/cpu/x86/vm/vmStructs_x86.hpp @@ -29,7 +29,7 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field, last_entry) \ +#define VM_STRUCTS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ \ /******************************/ \ /* JavaCallWrapper */ \ @@ -37,31 +37,14 @@ /******************************/ \ /* JavaFrameAnchor */ \ /******************************/ \ - volatile_nonstatic_field(JavaFrameAnchor, _last_Java_fp, intptr_t*) \ - \ - - /* NOTE that we do not use the last_entry() macro here; it is used */ - /* in vmStructs__.hpp's VM_STRUCTS_OS_CPU macro (and must */ - /* be present there) */ + volatile_nonstatic_field(JavaFrameAnchor, _last_Java_fp, intptr_t*) -#define VM_TYPES_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type, last_entry) \ - /* NOTE that we do not use the last_entry() macro here; it is used */ - /* in vmStructs__.hpp's VM_TYPES_OS_CPU macro (and must */ - /* be present there) */ +#define VM_TYPES_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) +#define VM_INT_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) -#define VM_INT_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ - - /* NOTE that we do not use the last_entry() macro here; it is used */ - /* in vmStructs__.hpp's VM_INT_CONSTANTS_OS_CPU macro (and must */ - /* be present there) */ - -#define VM_LONG_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ - - /* NOTE that we do not use the last_entry() macro here; it is used */ - /* in vmStructs__.hpp's VM_LONG_CONSTANTS_OS_CPU macro (and must */ - /* be present there) */ +#define VM_LONG_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) #endif // CPU_X86_VM_VMSTRUCTS_X86_HPP diff --git a/hotspot/src/cpu/zero/vm/vmStructs_zero.hpp b/hotspot/src/cpu/zero/vm/vmStructs_zero.hpp index 1b3815a0a2c..0bbc1f40f45 100644 --- a/hotspot/src/cpu/zero/vm/vmStructs_zero.hpp +++ b/hotspot/src/cpu/zero/vm/vmStructs_zero.hpp @@ -30,28 +30,12 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field, last_entry) \ +#define VM_STRUCTS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) - /* NOTE that we do not use the last_entry() macro here; it is used */ - /* in vmStructs__.hpp's VM_STRUCTS_OS_CPU macro (and must */ - /* be present there) */ +#define VM_TYPES_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) -#define VM_TYPES_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type, last_entry) \ +#define VM_INT_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) - /* NOTE that we do not use the last_entry() macro here; it is used */ - /* in vmStructs__.hpp's VM_TYPES_OS_CPU macro (and must */ - /* be present there) */ - -#define VM_INT_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ - - /* NOTE that we do not use the last_entry() macro here; it is used */ - /* in vmStructs__.hpp's VM_INT_CONSTANTS_OS_CPU macro (and must */ - /* be present there) */ - -#define VM_LONG_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ - - /* NOTE that we do not use the last_entry() macro here; it is used */ - /* in vmStructs__.hpp's VM_LONG_CONSTANTS_OS_CPU macro (and must */ - /* be present there) */ +#define VM_LONG_CONSTANTS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) #endif // CPU_ZERO_VM_VMSTRUCTS_ZERO_HPP diff --git a/hotspot/src/os_cpu/bsd_x86/vm/vmStructs_bsd_x86.hpp b/hotspot/src/os_cpu/bsd_x86/vm/vmStructs_bsd_x86.hpp index b98d975408a..edfbcfa9a54 100644 --- a/hotspot/src/os_cpu/bsd_x86/vm/vmStructs_bsd_x86.hpp +++ b/hotspot/src/os_cpu/bsd_x86/vm/vmStructs_bsd_x86.hpp @@ -29,37 +29,26 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field, last_entry) \ +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ \ /******************************/ \ /* Threads (NOTE: incomplete) */ \ /******************************/ \ nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ - nonstatic_field(OSThread, _pthread_id, pthread_t) \ - /* This must be the last entry, and must be present */ \ - last_entry() + nonstatic_field(OSThread, _pthread_id, pthread_t) -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type, last_entry) \ +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ \ /**********************/ \ /* Posix Thread IDs */ \ /**********************/ \ \ declare_unsigned_integer_type(OSThread::thread_id_t) \ - declare_unsigned_integer_type(pthread_t) \ - \ - /* This must be the last entry, and must be present */ \ - last_entry() + declare_unsigned_integer_type(pthread_t) -#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ - \ - /* This must be the last entry, and must be present */ \ - last_entry() +#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) -#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ - \ - /* This must be the last entry, and must be present */ \ - last_entry() +#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) #endif // OS_CPU_BSD_X86_VM_VMSTRUCTS_BSD_X86_HPP diff --git a/hotspot/src/os_cpu/bsd_zero/vm/vmStructs_bsd_zero.hpp b/hotspot/src/os_cpu/bsd_zero/vm/vmStructs_bsd_zero.hpp index 17165cc9d13..6673f448f81 100644 --- a/hotspot/src/os_cpu/bsd_zero/vm/vmStructs_bsd_zero.hpp +++ b/hotspot/src/os_cpu/bsd_zero/vm/vmStructs_bsd_zero.hpp @@ -30,21 +30,13 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field, last_entry) \ - /* This must be the last entry, and must be present */ \ - last_entry() +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type, last_entry) \ - /* This must be the last entry, and must be present */ \ - last_entry() +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) -#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ - /* This must be the last entry, and must be present */ \ - last_entry() +#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) -#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ - /* This must be the last entry, and must be present */ \ - last_entry() +#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) #endif // OS_CPU_BSD_ZERO_VM_VMSTRUCTS_BSD_ZERO_HPP diff --git a/hotspot/src/os_cpu/linux_sparc/vm/vmStructs_linux_sparc.hpp b/hotspot/src/os_cpu/linux_sparc/vm/vmStructs_linux_sparc.hpp index 58fc94bbc38..38bc63e6496 100644 --- a/hotspot/src/os_cpu/linux_sparc/vm/vmStructs_linux_sparc.hpp +++ b/hotspot/src/os_cpu/linux_sparc/vm/vmStructs_linux_sparc.hpp @@ -29,7 +29,7 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field, last_entry) \ +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ \ /******************************/ \ /* Threads (NOTE: incomplete) */ \ @@ -37,38 +37,27 @@ \ nonstatic_field(JavaThread, _base_of_stack_pointer, intptr_t*) \ nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ - nonstatic_field(OSThread, _pthread_id, pthread_t) \ - /* This must be the last entry, and must be present */ \ - last_entry() + nonstatic_field(OSThread, _pthread_id, pthread_t) -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type, last_entry) \ +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ \ /**********************/ \ /* POSIX Thread IDs */ \ /**********************/ \ \ declare_integer_type(OSThread::thread_id_t) \ - declare_unsigned_integer_type(pthread_t) \ - \ - /* This must be the last entry, and must be present */ \ - last_entry() + declare_unsigned_integer_type(pthread_t) -#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ +#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) \ \ /************************/ \ /* JavaThread constants */ \ /************************/ \ \ - declare_constant(JavaFrameAnchor::flushed) \ - \ - /* This must be the last entry, and must be present */ \ - last_entry() + declare_constant(JavaFrameAnchor::flushed) -#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ - \ - /* This must be the last entry, and must be present */ \ - last_entry() +#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) #endif // OS_CPU_LINUX_SPARC_VM_VMSTRUCTS_LINUX_SPARC_HPP diff --git a/hotspot/src/os_cpu/linux_x86/vm/vmStructs_linux_x86.hpp b/hotspot/src/os_cpu/linux_x86/vm/vmStructs_linux_x86.hpp index c01e6c91c2a..828f992d8ba 100644 --- a/hotspot/src/os_cpu/linux_x86/vm/vmStructs_linux_x86.hpp +++ b/hotspot/src/os_cpu/linux_x86/vm/vmStructs_linux_x86.hpp @@ -29,37 +29,26 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field, last_entry) \ +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ \ /******************************/ \ /* Threads (NOTE: incomplete) */ \ /******************************/ \ nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ - nonstatic_field(OSThread, _pthread_id, pthread_t) \ - /* This must be the last entry, and must be present */ \ - last_entry() + nonstatic_field(OSThread, _pthread_id, pthread_t) -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type, last_entry) \ +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ \ /**********************/ \ /* Posix Thread IDs */ \ /**********************/ \ \ declare_integer_type(OSThread::thread_id_t) \ - declare_unsigned_integer_type(pthread_t) \ - \ - /* This must be the last entry, and must be present */ \ - last_entry() + declare_unsigned_integer_type(pthread_t) -#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ - \ - /* This must be the last entry, and must be present */ \ - last_entry() +#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) -#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ - \ - /* This must be the last entry, and must be present */ \ - last_entry() +#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) #endif // OS_CPU_LINUX_X86_VM_VMSTRUCTS_LINUX_X86_HPP diff --git a/hotspot/src/os_cpu/linux_zero/vm/vmStructs_linux_zero.hpp b/hotspot/src/os_cpu/linux_zero/vm/vmStructs_linux_zero.hpp index 46c7912c372..34b82a96d79 100644 --- a/hotspot/src/os_cpu/linux_zero/vm/vmStructs_linux_zero.hpp +++ b/hotspot/src/os_cpu/linux_zero/vm/vmStructs_linux_zero.hpp @@ -30,21 +30,12 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field, last_entry) \ - /* This must be the last entry, and must be present */ \ - last_entry() +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type, last_entry) \ - /* This must be the last entry, and must be present */ \ - last_entry() +#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) -#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ - /* This must be the last entry, and must be present */ \ - last_entry() - -#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ - /* This must be the last entry, and must be present */ \ - last_entry() +#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) #endif // OS_CPU_LINUX_ZERO_VM_VMSTRUCTS_LINUX_ZERO_HPP diff --git a/hotspot/src/os_cpu/solaris_sparc/vm/vmStructs_solaris_sparc.hpp b/hotspot/src/os_cpu/solaris_sparc/vm/vmStructs_solaris_sparc.hpp index 58113a9ea49..40d6ae735aa 100644 --- a/hotspot/src/os_cpu/solaris_sparc/vm/vmStructs_solaris_sparc.hpp +++ b/hotspot/src/os_cpu/solaris_sparc/vm/vmStructs_solaris_sparc.hpp @@ -29,44 +29,32 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field, last_entry) \ +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ \ /******************************/ \ /* Threads (NOTE: incomplete) */ \ /******************************/ \ \ nonstatic_field(JavaThread, _base_of_stack_pointer, intptr_t*) \ - nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ - /* This must be the last entry, and must be present */ \ - last_entry() + nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) - -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type, last_entry) \ +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ \ /**********************/ \ /* Solaris Thread IDs */ \ /**********************/ \ \ - declare_unsigned_integer_type(OSThread::thread_id_t) \ - \ - /* This must be the last entry, and must be present */ \ - last_entry() + declare_unsigned_integer_type(OSThread::thread_id_t) -#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ +#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) \ \ /************************/ \ /* JavaThread constants */ \ /************************/ \ \ - declare_constant(JavaFrameAnchor::flushed) \ - \ - /* This must be the last entry, and must be present */ \ - last_entry() + declare_constant(JavaFrameAnchor::flushed) -#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ - \ - /* This must be the last entry, and must be present */ \ - last_entry() +#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) #endif // OS_CPU_SOLARIS_SPARC_VM_VMSTRUCTS_SOLARIS_SPARC_HPP diff --git a/hotspot/src/os_cpu/solaris_x86/vm/vmStructs_solaris_x86.hpp b/hotspot/src/os_cpu/solaris_x86/vm/vmStructs_solaris_x86.hpp index a2a4f7c60b7..523fdc7cc42 100644 --- a/hotspot/src/os_cpu/solaris_x86/vm/vmStructs_solaris_x86.hpp +++ b/hotspot/src/os_cpu/solaris_x86/vm/vmStructs_solaris_x86.hpp @@ -29,36 +29,24 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field, last_entry) \ +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ \ /******************************/ \ /* Threads (NOTE: incomplete) */ \ /******************************/ \ \ - nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ - \ - /* This must be the last entry, and must be present */ \ - last_entry() + nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type, last_entry) \ +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ \ /**********************/ \ /* Solaris Thread IDs */ \ /**********************/ \ \ - declare_unsigned_integer_type(OSThread::thread_id_t) \ - \ - /* This must be the last entry, and must be present */ \ - last_entry() + declare_unsigned_integer_type(OSThread::thread_id_t) -#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ - \ - /* This must be the last entry, and must be present */ \ - last_entry() +#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) -#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ - \ - /* This must be the last entry, and must be present */ \ - last_entry() +#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) #endif // OS_CPU_SOLARIS_X86_VM_VMSTRUCTS_SOLARIS_X86_HPP diff --git a/hotspot/src/os_cpu/windows_x86/vm/vmStructs_windows_x86.hpp b/hotspot/src/os_cpu/windows_x86/vm/vmStructs_windows_x86.hpp index 69d25c93186..a392b63632c 100644 --- a/hotspot/src/os_cpu/windows_x86/vm/vmStructs_windows_x86.hpp +++ b/hotspot/src/os_cpu/windows_x86/vm/vmStructs_windows_x86.hpp @@ -29,32 +29,21 @@ // constants required by the Serviceability Agent. This file is // referenced by vmStructs.cpp. -#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field, last_entry) \ +#define VM_STRUCTS_OS_CPU(nonstatic_field, static_field, unchecked_nonstatic_field, volatile_nonstatic_field, nonproduct_nonstatic_field, c2_nonstatic_field, unchecked_c1_static_field, unchecked_c2_static_field) \ \ /******************************/ \ /* Threads (NOTE: incomplete) */ \ /******************************/ \ \ nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ - unchecked_nonstatic_field(OSThread, _thread_handle, sizeof(HANDLE)) /* NOTE: no type */ \ - \ - /* This must be the last entry, and must be present */ \ - last_entry() + unchecked_nonstatic_field(OSThread, _thread_handle, sizeof(HANDLE)) /* NOTE: no type */ -#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type, last_entry) \ +#define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ \ - declare_unsigned_integer_type(OSThread::thread_id_t) \ - /* This must be the last entry, and must be present */ \ - last_entry() + declare_unsigned_integer_type(OSThread::thread_id_t) -#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ - \ - /* This must be the last entry, and must be present */ \ - last_entry() +#define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) -#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ - \ - /* This must be the last entry, and must be present */ \ - last_entry() +#define VM_LONG_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) #endif // OS_CPU_WINDOWS_X86_VM_VMSTRUCTS_WINDOWS_X86_HPP diff --git a/hotspot/src/share/vm/runtime/vmStructs.cpp b/hotspot/src/share/vm/runtime/vmStructs.cpp index 9ac10e26d4c..958298bf994 100644 --- a/hotspot/src/share/vm/runtime/vmStructs.cpp +++ b/hotspot/src/share/vm/runtime/vmStructs.cpp @@ -257,8 +257,7 @@ typedef BinaryTreeDictionary MetablockTreeDictionary; c1_nonstatic_field, \ c2_nonstatic_field, \ unchecked_c1_static_field, \ - unchecked_c2_static_field, \ - last_entry) \ + unchecked_c2_static_field) \ \ /******************************************************************/ \ /* OopDesc and Klass hierarchies (NOTE: MethodData* incomplete) */ \ @@ -1238,9 +1237,6 @@ typedef BinaryTreeDictionary MetablockTreeDictionary; nonstatic_field(FreeList, _count, ssize_t) \ nonstatic_field(MetablockTreeDictionary, _total_size, size_t) - /* NOTE that we do not use the last_entry() macro here; it is used */ - /* in vmStructs__.hpp's VM_STRUCTS_OS_CPU macro (and must */ - /* be present there) */ //-------------------------------------------------------------------------------- // VM_TYPES @@ -1280,8 +1276,7 @@ typedef BinaryTreeDictionary MetablockTreeDictionary; declare_unsigned_integer_type, \ declare_c1_toplevel_type, \ declare_c2_type, \ - declare_c2_toplevel_type, \ - last_entry) \ + declare_c2_toplevel_type) \ \ /*************************************************************/ \ /* Java primitive types -- required by the SA implementation */ \ @@ -2098,10 +2093,6 @@ typedef BinaryTreeDictionary MetablockTreeDictionary; declare_type(MetablockTreeDictionary, FreeBlockDictionary) - /* NOTE that we do not use the last_entry() macro here; it is used */ - /* in vmStructs__.hpp's VM_TYPES_OS_CPU macro (and must be */ - /* present there) */ - //-------------------------------------------------------------------------------- // VM_INT_CONSTANTS // @@ -2114,8 +2105,7 @@ typedef BinaryTreeDictionary MetablockTreeDictionary; declare_preprocessor_constant, \ declare_c1_constant, \ declare_c2_constant, \ - declare_c2_preprocessor_constant, \ - last_entry) \ + declare_c2_preprocessor_constant) \ \ /******************/ \ /* Useful globals */ \ @@ -2483,9 +2473,6 @@ typedef BinaryTreeDictionary MetablockTreeDictionary; declare_c2_preprocessor_constant("SAVED_ON_ENTRY_REG_COUNT", SAVED_ON_ENTRY_REG_COUNT) \ declare_c2_preprocessor_constant("C_SAVED_ON_ENTRY_REG_COUNT", C_SAVED_ON_ENTRY_REG_COUNT) - /* NOTE that we do not use the last_entry() macro here; it is used */ - /* in vmStructs__.hpp's VM_INT_CONSTANTS_OS_CPU macro (and */ - /* must be present there) */ //-------------------------------------------------------------------------------- // VM_LONG_CONSTANTS @@ -2495,7 +2482,7 @@ typedef BinaryTreeDictionary MetablockTreeDictionary; // enums, etc., while "declare_preprocessor_constant" must be used for // all #defined constants. -#define VM_LONG_CONSTANTS(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant, last_entry) \ +#define VM_LONG_CONSTANTS(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) \ \ /*********************/ \ /* MarkOop constants */ \ @@ -2541,11 +2528,7 @@ typedef BinaryTreeDictionary MetablockTreeDictionary; /* Constants in markOop used by CMS. */ \ declare_constant(markOopDesc::cms_shift) \ declare_constant(markOopDesc::cms_mask) \ - declare_constant(markOopDesc::size_shift) \ - - /* NOTE that we do not use the last_entry() macro here; it is used */ - /* in vmStructs__.hpp's VM_LONG_CONSTANTS_OS_CPU macro (and */ - /* must be present there) */ + declare_constant(markOopDesc::size_shift) //-------------------------------------------------------------------------------- @@ -2608,9 +2591,6 @@ typedef BinaryTreeDictionary MetablockTreeDictionary; // This is a no-op macro for unchecked fields #define CHECK_NO_OP(a, b, c) -// This is a no-op macro for the sentinel value -#define CHECK_SENTINEL() - // // Build-specific macros: // @@ -2789,48 +2769,47 @@ typedef BinaryTreeDictionary MetablockTreeDictionary; // as long as class VMStructs is a friend VMStructEntry VMStructs::localHotSpotVMStructs[] = { - VM_STRUCTS(GENERATE_NONSTATIC_VM_STRUCT_ENTRY, \ - GENERATE_STATIC_VM_STRUCT_ENTRY, \ - GENERATE_UNCHECKED_NONSTATIC_VM_STRUCT_ENTRY, \ - GENERATE_NONSTATIC_VM_STRUCT_ENTRY, \ - GENERATE_NONPRODUCT_NONSTATIC_VM_STRUCT_ENTRY, \ - GENERATE_C1_NONSTATIC_VM_STRUCT_ENTRY, \ - GENERATE_C2_NONSTATIC_VM_STRUCT_ENTRY, \ - GENERATE_C1_UNCHECKED_STATIC_VM_STRUCT_ENTRY, \ - GENERATE_C2_UNCHECKED_STATIC_VM_STRUCT_ENTRY, \ - GENERATE_VM_STRUCT_LAST_ENTRY) + VM_STRUCTS(GENERATE_NONSTATIC_VM_STRUCT_ENTRY, + GENERATE_STATIC_VM_STRUCT_ENTRY, + GENERATE_UNCHECKED_NONSTATIC_VM_STRUCT_ENTRY, + GENERATE_NONSTATIC_VM_STRUCT_ENTRY, + GENERATE_NONPRODUCT_NONSTATIC_VM_STRUCT_ENTRY, + GENERATE_C1_NONSTATIC_VM_STRUCT_ENTRY, + GENERATE_C2_NONSTATIC_VM_STRUCT_ENTRY, + GENERATE_C1_UNCHECKED_STATIC_VM_STRUCT_ENTRY, + GENERATE_C2_UNCHECKED_STATIC_VM_STRUCT_ENTRY) #ifndef SERIALGC - VM_STRUCTS_PARALLELGC(GENERATE_NONSTATIC_VM_STRUCT_ENTRY, \ + VM_STRUCTS_PARALLELGC(GENERATE_NONSTATIC_VM_STRUCT_ENTRY, GENERATE_STATIC_VM_STRUCT_ENTRY) - VM_STRUCTS_CMS(GENERATE_NONSTATIC_VM_STRUCT_ENTRY, \ - GENERATE_NONSTATIC_VM_STRUCT_ENTRY, \ + VM_STRUCTS_CMS(GENERATE_NONSTATIC_VM_STRUCT_ENTRY, + GENERATE_NONSTATIC_VM_STRUCT_ENTRY, GENERATE_STATIC_VM_STRUCT_ENTRY) - VM_STRUCTS_G1(GENERATE_NONSTATIC_VM_STRUCT_ENTRY, \ + VM_STRUCTS_G1(GENERATE_NONSTATIC_VM_STRUCT_ENTRY, GENERATE_STATIC_VM_STRUCT_ENTRY) #endif // SERIALGC - VM_STRUCTS_CPU(GENERATE_NONSTATIC_VM_STRUCT_ENTRY, \ - GENERATE_STATIC_VM_STRUCT_ENTRY, \ - GENERATE_UNCHECKED_NONSTATIC_VM_STRUCT_ENTRY, \ - GENERATE_NONSTATIC_VM_STRUCT_ENTRY, \ - GENERATE_NONPRODUCT_NONSTATIC_VM_STRUCT_ENTRY, \ - GENERATE_C2_NONSTATIC_VM_STRUCT_ENTRY, \ - GENERATE_C1_UNCHECKED_STATIC_VM_STRUCT_ENTRY, \ - GENERATE_C2_UNCHECKED_STATIC_VM_STRUCT_ENTRY, \ - GENERATE_VM_STRUCT_LAST_ENTRY) + VM_STRUCTS_CPU(GENERATE_NONSTATIC_VM_STRUCT_ENTRY, + GENERATE_STATIC_VM_STRUCT_ENTRY, + GENERATE_UNCHECKED_NONSTATIC_VM_STRUCT_ENTRY, + GENERATE_NONSTATIC_VM_STRUCT_ENTRY, + GENERATE_NONPRODUCT_NONSTATIC_VM_STRUCT_ENTRY, + GENERATE_C2_NONSTATIC_VM_STRUCT_ENTRY, + GENERATE_C1_UNCHECKED_STATIC_VM_STRUCT_ENTRY, + GENERATE_C2_UNCHECKED_STATIC_VM_STRUCT_ENTRY) - VM_STRUCTS_OS_CPU(GENERATE_NONSTATIC_VM_STRUCT_ENTRY, \ - GENERATE_STATIC_VM_STRUCT_ENTRY, \ - GENERATE_UNCHECKED_NONSTATIC_VM_STRUCT_ENTRY, \ - GENERATE_NONSTATIC_VM_STRUCT_ENTRY, \ - GENERATE_NONPRODUCT_NONSTATIC_VM_STRUCT_ENTRY, \ - GENERATE_C2_NONSTATIC_VM_STRUCT_ENTRY, \ - GENERATE_C1_UNCHECKED_STATIC_VM_STRUCT_ENTRY, \ - GENERATE_C2_UNCHECKED_STATIC_VM_STRUCT_ENTRY, \ - GENERATE_VM_STRUCT_LAST_ENTRY) + VM_STRUCTS_OS_CPU(GENERATE_NONSTATIC_VM_STRUCT_ENTRY, + GENERATE_STATIC_VM_STRUCT_ENTRY, + GENERATE_UNCHECKED_NONSTATIC_VM_STRUCT_ENTRY, + GENERATE_NONSTATIC_VM_STRUCT_ENTRY, + GENERATE_NONPRODUCT_NONSTATIC_VM_STRUCT_ENTRY, + GENERATE_C2_NONSTATIC_VM_STRUCT_ENTRY, + GENERATE_C1_UNCHECKED_STATIC_VM_STRUCT_ENTRY, + GENERATE_C2_UNCHECKED_STATIC_VM_STRUCT_ENTRY) + + GENERATE_VM_STRUCT_LAST_ENTRY() }; VMTypeEntry VMStructs::localHotSpotVMTypes[] = { @@ -2842,8 +2821,7 @@ VMTypeEntry VMStructs::localHotSpotVMTypes[] = { GENERATE_UNSIGNED_INTEGER_VM_TYPE_ENTRY, GENERATE_C1_TOPLEVEL_VM_TYPE_ENTRY, GENERATE_C2_VM_TYPE_ENTRY, - GENERATE_C2_TOPLEVEL_VM_TYPE_ENTRY, - GENERATE_VM_TYPE_LAST_ENTRY) + GENERATE_C2_TOPLEVEL_VM_TYPE_ENTRY) #ifndef SERIALGC VM_TYPES_PARALLELGC(GENERATE_VM_TYPE_ENTRY, @@ -2865,8 +2843,7 @@ VMTypeEntry VMStructs::localHotSpotVMTypes[] = { GENERATE_UNSIGNED_INTEGER_VM_TYPE_ENTRY, GENERATE_C1_TOPLEVEL_VM_TYPE_ENTRY, GENERATE_C2_VM_TYPE_ENTRY, - GENERATE_C2_TOPLEVEL_VM_TYPE_ENTRY, - GENERATE_VM_TYPE_LAST_ENTRY) + GENERATE_C2_TOPLEVEL_VM_TYPE_ENTRY) VM_TYPES_OS_CPU(GENERATE_VM_TYPE_ENTRY, GENERATE_TOPLEVEL_VM_TYPE_ENTRY, @@ -2875,8 +2852,9 @@ VMTypeEntry VMStructs::localHotSpotVMTypes[] = { GENERATE_UNSIGNED_INTEGER_VM_TYPE_ENTRY, GENERATE_C1_TOPLEVEL_VM_TYPE_ENTRY, GENERATE_C2_VM_TYPE_ENTRY, - GENERATE_C2_TOPLEVEL_VM_TYPE_ENTRY, - GENERATE_VM_TYPE_LAST_ENTRY) + GENERATE_C2_TOPLEVEL_VM_TYPE_ENTRY) + + GENERATE_VM_TYPE_LAST_ENTRY() }; VMIntConstantEntry VMStructs::localHotSpotVMIntConstants[] = { @@ -2885,8 +2863,7 @@ VMIntConstantEntry VMStructs::localHotSpotVMIntConstants[] = { GENERATE_PREPROCESSOR_VM_INT_CONSTANT_ENTRY, GENERATE_C1_VM_INT_CONSTANT_ENTRY, GENERATE_C2_VM_INT_CONSTANT_ENTRY, - GENERATE_C2_PREPROCESSOR_VM_INT_CONSTANT_ENTRY, - GENERATE_VM_INT_CONSTANT_LAST_ENTRY) + GENERATE_C2_PREPROCESSOR_VM_INT_CONSTANT_ENTRY) #ifndef SERIALGC VM_INT_CONSTANTS_CMS(GENERATE_VM_INT_CONSTANT_ENTRY) @@ -2898,15 +2875,15 @@ VMIntConstantEntry VMStructs::localHotSpotVMIntConstants[] = { GENERATE_PREPROCESSOR_VM_INT_CONSTANT_ENTRY, GENERATE_C1_VM_INT_CONSTANT_ENTRY, GENERATE_C2_VM_INT_CONSTANT_ENTRY, - GENERATE_C2_PREPROCESSOR_VM_INT_CONSTANT_ENTRY, - GENERATE_VM_INT_CONSTANT_LAST_ENTRY) + GENERATE_C2_PREPROCESSOR_VM_INT_CONSTANT_ENTRY) VM_INT_CONSTANTS_OS_CPU(GENERATE_VM_INT_CONSTANT_ENTRY, GENERATE_PREPROCESSOR_VM_INT_CONSTANT_ENTRY, GENERATE_C1_VM_INT_CONSTANT_ENTRY, GENERATE_C2_VM_INT_CONSTANT_ENTRY, - GENERATE_C2_PREPROCESSOR_VM_INT_CONSTANT_ENTRY, - GENERATE_VM_INT_CONSTANT_LAST_ENTRY) + GENERATE_C2_PREPROCESSOR_VM_INT_CONSTANT_ENTRY) + + GENERATE_VM_INT_CONSTANT_LAST_ENTRY() }; VMLongConstantEntry VMStructs::localHotSpotVMLongConstants[] = { @@ -2915,22 +2892,21 @@ VMLongConstantEntry VMStructs::localHotSpotVMLongConstants[] = { GENERATE_PREPROCESSOR_VM_LONG_CONSTANT_ENTRY, GENERATE_C1_VM_LONG_CONSTANT_ENTRY, GENERATE_C2_VM_LONG_CONSTANT_ENTRY, - GENERATE_C2_PREPROCESSOR_VM_LONG_CONSTANT_ENTRY, - GENERATE_VM_LONG_CONSTANT_LAST_ENTRY) + GENERATE_C2_PREPROCESSOR_VM_LONG_CONSTANT_ENTRY) VM_LONG_CONSTANTS_CPU(GENERATE_VM_LONG_CONSTANT_ENTRY, GENERATE_PREPROCESSOR_VM_LONG_CONSTANT_ENTRY, GENERATE_C1_VM_LONG_CONSTANT_ENTRY, GENERATE_C2_VM_LONG_CONSTANT_ENTRY, - GENERATE_C2_PREPROCESSOR_VM_LONG_CONSTANT_ENTRY, - GENERATE_VM_LONG_CONSTANT_LAST_ENTRY) + GENERATE_C2_PREPROCESSOR_VM_LONG_CONSTANT_ENTRY) VM_LONG_CONSTANTS_OS_CPU(GENERATE_VM_LONG_CONSTANT_ENTRY, GENERATE_PREPROCESSOR_VM_LONG_CONSTANT_ENTRY, GENERATE_C1_VM_LONG_CONSTANT_ENTRY, GENERATE_C2_VM_LONG_CONSTANT_ENTRY, - GENERATE_C2_PREPROCESSOR_VM_LONG_CONSTANT_ENTRY, - GENERATE_VM_LONG_CONSTANT_LAST_ENTRY) + GENERATE_C2_PREPROCESSOR_VM_LONG_CONSTANT_ENTRY) + + GENERATE_VM_LONG_CONSTANT_LAST_ENTRY() }; // This is used both to check the types of referenced fields and, in @@ -2945,8 +2921,7 @@ VMStructs::init() { CHECK_C1_NONSTATIC_VM_STRUCT_ENTRY, CHECK_C2_NONSTATIC_VM_STRUCT_ENTRY, CHECK_NO_OP, - CHECK_NO_OP, - CHECK_SENTINEL); + CHECK_NO_OP); #ifndef SERIALGC VM_STRUCTS_PARALLELGC(CHECK_NONSTATIC_VM_STRUCT_ENTRY, @@ -2967,8 +2942,7 @@ VMStructs::init() { CHECK_NONPRODUCT_NONSTATIC_VM_STRUCT_ENTRY, CHECK_C2_NONSTATIC_VM_STRUCT_ENTRY, CHECK_NO_OP, - CHECK_NO_OP, - CHECK_SENTINEL); + CHECK_NO_OP); VM_STRUCTS_OS_CPU(CHECK_NONSTATIC_VM_STRUCT_ENTRY, CHECK_STATIC_VM_STRUCT_ENTRY, @@ -2977,8 +2951,7 @@ VMStructs::init() { CHECK_NONPRODUCT_NONSTATIC_VM_STRUCT_ENTRY, CHECK_C2_NONSTATIC_VM_STRUCT_ENTRY, CHECK_NO_OP, - CHECK_NO_OP, - CHECK_SENTINEL); + CHECK_NO_OP); VM_TYPES(CHECK_VM_TYPE_ENTRY, CHECK_SINGLE_ARG_VM_TYPE_NO_OP, @@ -2987,8 +2960,7 @@ VMStructs::init() { CHECK_SINGLE_ARG_VM_TYPE_NO_OP, CHECK_C1_TOPLEVEL_VM_TYPE_ENTRY, CHECK_C2_VM_TYPE_ENTRY, - CHECK_C2_TOPLEVEL_VM_TYPE_ENTRY, - CHECK_SENTINEL); + CHECK_C2_TOPLEVEL_VM_TYPE_ENTRY); #ifndef SERIALGC VM_TYPES_PARALLELGC(CHECK_VM_TYPE_ENTRY, @@ -3010,8 +2982,7 @@ VMStructs::init() { CHECK_SINGLE_ARG_VM_TYPE_NO_OP, CHECK_C1_TOPLEVEL_VM_TYPE_ENTRY, CHECK_C2_VM_TYPE_ENTRY, - CHECK_C2_TOPLEVEL_VM_TYPE_ENTRY, - CHECK_SENTINEL); + CHECK_C2_TOPLEVEL_VM_TYPE_ENTRY); VM_TYPES_OS_CPU(CHECK_VM_TYPE_ENTRY, CHECK_SINGLE_ARG_VM_TYPE_NO_OP, @@ -3020,8 +2991,7 @@ VMStructs::init() { CHECK_SINGLE_ARG_VM_TYPE_NO_OP, CHECK_C1_TOPLEVEL_VM_TYPE_ENTRY, CHECK_C2_VM_TYPE_ENTRY, - CHECK_C2_TOPLEVEL_VM_TYPE_ENTRY, - CHECK_SENTINEL); + CHECK_C2_TOPLEVEL_VM_TYPE_ENTRY); // // Split VM_STRUCTS() invocation into two parts to allow MS VC++ 6.0 @@ -3040,53 +3010,49 @@ VMStructs::init() { // Solstice NFS setup. If everyone switches to local workspaces on // Win32, we can put this back in. #ifndef _WINDOWS - debug_only(VM_STRUCTS(ENSURE_FIELD_TYPE_PRESENT, \ - CHECK_NO_OP, \ - CHECK_NO_OP, \ - CHECK_NO_OP, \ - CHECK_NO_OP, \ - CHECK_NO_OP, \ - CHECK_NO_OP, \ - CHECK_NO_OP, \ - CHECK_NO_OP, \ - CHECK_SENTINEL)); - debug_only(VM_STRUCTS(CHECK_NO_OP, \ - ENSURE_FIELD_TYPE_PRESENT, \ - CHECK_NO_OP, \ - ENSURE_FIELD_TYPE_PRESENT, \ - ENSURE_NONPRODUCT_FIELD_TYPE_PRESENT, \ - ENSURE_C1_FIELD_TYPE_PRESENT, \ - ENSURE_C2_FIELD_TYPE_PRESENT, \ - CHECK_NO_OP, \ - CHECK_NO_OP, \ - CHECK_SENTINEL)); + debug_only(VM_STRUCTS(ENSURE_FIELD_TYPE_PRESENT, + CHECK_NO_OP, + CHECK_NO_OP, + CHECK_NO_OP, + CHECK_NO_OP, + CHECK_NO_OP, + CHECK_NO_OP, + CHECK_NO_OP, + CHECK_NO_OP)); + debug_only(VM_STRUCTS(CHECK_NO_OP, + ENSURE_FIELD_TYPE_PRESENT, + CHECK_NO_OP, + ENSURE_FIELD_TYPE_PRESENT, + ENSURE_NONPRODUCT_FIELD_TYPE_PRESENT, + ENSURE_C1_FIELD_TYPE_PRESENT, + ENSURE_C2_FIELD_TYPE_PRESENT, + CHECK_NO_OP, + CHECK_NO_OP)); #ifndef SERIALGC - debug_only(VM_STRUCTS_PARALLELGC(ENSURE_FIELD_TYPE_PRESENT, \ + debug_only(VM_STRUCTS_PARALLELGC(ENSURE_FIELD_TYPE_PRESENT, ENSURE_FIELD_TYPE_PRESENT)); - debug_only(VM_STRUCTS_CMS(ENSURE_FIELD_TYPE_PRESENT, \ - ENSURE_FIELD_TYPE_PRESENT, \ + debug_only(VM_STRUCTS_CMS(ENSURE_FIELD_TYPE_PRESENT, + ENSURE_FIELD_TYPE_PRESENT, ENSURE_FIELD_TYPE_PRESENT)); - debug_only(VM_STRUCTS_G1(ENSURE_FIELD_TYPE_PRESENT, \ + debug_only(VM_STRUCTS_G1(ENSURE_FIELD_TYPE_PRESENT, ENSURE_FIELD_TYPE_PRESENT)); #endif // SERIALGC - debug_only(VM_STRUCTS_CPU(ENSURE_FIELD_TYPE_PRESENT, \ - ENSURE_FIELD_TYPE_PRESENT, \ - CHECK_NO_OP, \ - ENSURE_FIELD_TYPE_PRESENT, \ - ENSURE_NONPRODUCT_FIELD_TYPE_PRESENT, \ - ENSURE_C2_FIELD_TYPE_PRESENT, \ - CHECK_NO_OP, \ - CHECK_NO_OP, \ - CHECK_SENTINEL)); - debug_only(VM_STRUCTS_OS_CPU(ENSURE_FIELD_TYPE_PRESENT, \ - ENSURE_FIELD_TYPE_PRESENT, \ - CHECK_NO_OP, \ - ENSURE_FIELD_TYPE_PRESENT, \ - ENSURE_NONPRODUCT_FIELD_TYPE_PRESENT, \ - ENSURE_C2_FIELD_TYPE_PRESENT, \ - CHECK_NO_OP, \ - CHECK_NO_OP, \ - CHECK_SENTINEL)); + debug_only(VM_STRUCTS_CPU(ENSURE_FIELD_TYPE_PRESENT, + ENSURE_FIELD_TYPE_PRESENT, + CHECK_NO_OP, + ENSURE_FIELD_TYPE_PRESENT, + ENSURE_NONPRODUCT_FIELD_TYPE_PRESENT, + ENSURE_C2_FIELD_TYPE_PRESENT, + CHECK_NO_OP, + CHECK_NO_OP)); + debug_only(VM_STRUCTS_OS_CPU(ENSURE_FIELD_TYPE_PRESENT, + ENSURE_FIELD_TYPE_PRESENT, + CHECK_NO_OP, + ENSURE_FIELD_TYPE_PRESENT, + ENSURE_NONPRODUCT_FIELD_TYPE_PRESENT, + ENSURE_C2_FIELD_TYPE_PRESENT, + CHECK_NO_OP, + CHECK_NO_OP)); #endif } @@ -3206,6 +3172,30 @@ void vmStructs_init() { #ifndef PRODUCT void VMStructs::test() { + // Make sure last entry in the each array is indeed the correct end marker. + // The reason why these are static is to make sure they are zero initialized. + // Putting them on the stack will leave some garbage in the padding of some fields. + static VMStructEntry struct_last_entry = GENERATE_VM_STRUCT_LAST_ENTRY(); + assert(memcmp(&localHotSpotVMStructs[(sizeof(localHotSpotVMStructs) / sizeof(VMStructEntry)) - 1], + &struct_last_entry, + sizeof(VMStructEntry)) == 0, "Incorrect last entry in localHotSpotVMStructs"); + + static VMTypeEntry type_last_entry = GENERATE_VM_TYPE_LAST_ENTRY(); + assert(memcmp(&localHotSpotVMTypes[sizeof(localHotSpotVMTypes) / sizeof(VMTypeEntry) - 1], + &type_last_entry, + sizeof(VMTypeEntry)) == 0, "Incorrect last entry in localHotSpotVMTypes"); + + static VMIntConstantEntry int_last_entry = GENERATE_VM_INT_CONSTANT_LAST_ENTRY(); + assert(memcmp(&localHotSpotVMIntConstants[sizeof(localHotSpotVMIntConstants) / sizeof(VMIntConstantEntry) - 1], + &int_last_entry, + sizeof(VMIntConstantEntry)) == 0, "Incorrect last entry in localHotSpotVMIntConstants"); + + static VMLongConstantEntry long_last_entry = GENERATE_VM_LONG_CONSTANT_LAST_ENTRY(); + assert(memcmp(&localHotSpotVMLongConstants[sizeof(localHotSpotVMLongConstants) / sizeof(VMLongConstantEntry) - 1], + &long_last_entry, + sizeof(VMLongConstantEntry)) == 0, "Incorrect last entry in localHotSpotVMLongConstants"); + + // Check for duplicate entries in type array for (int i = 0; localHotSpotVMTypes[i].typeName != NULL; i++) { for (int j = i + 1; localHotSpotVMTypes[j].typeName != NULL; j++) { From cac8a55fb2a9eb6c53253cd0daf0223977371f17 Mon Sep 17 00:00:00 2001 From: Jeremy Manson Date: Thu, 10 Jan 2013 21:00:11 -0500 Subject: [PATCH 005/138] 8005921: Memory leaks in vmStructs.cpp Reviewed-by: dholmes, mikael, rasbold --- hotspot/src/share/vm/runtime/vmStructs.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/hotspot/src/share/vm/runtime/vmStructs.cpp b/hotspot/src/share/vm/runtime/vmStructs.cpp index 958298bf994..09d557cdbf9 100644 --- a/hotspot/src/share/vm/runtime/vmStructs.cpp +++ b/hotspot/src/share/vm/runtime/vmStructs.cpp @@ -3112,10 +3112,10 @@ static int recursiveFindType(VMTypeEntry* origtypes, const char* typeName, bool s[len-1] = '\0'; // tty->print_cr("checking \"%s\" for \"%s\"", s, typeName); if (recursiveFindType(origtypes, s, true) == 1) { - delete s; + delete [] s; return 1; } - delete s; + delete [] s; } const char* start = NULL; if (strstr(typeName, "GrowableArray<") == typeName) { @@ -3131,10 +3131,10 @@ static int recursiveFindType(VMTypeEntry* origtypes, const char* typeName, bool s[len-1] = '\0'; // tty->print_cr("checking \"%s\" for \"%s\"", s, typeName); if (recursiveFindType(origtypes, s, true) == 1) { - delete s; + delete [] s; return 1; } - delete s; + delete [] s; } if (strstr(typeName, "const ") == typeName) { const char * s = typeName + strlen("const "); @@ -3148,8 +3148,10 @@ static int recursiveFindType(VMTypeEntry* origtypes, const char* typeName, bool s[len - 6] = '\0'; // tty->print_cr("checking \"%s\" for \"%s\"", s, typeName); if (recursiveFindType(origtypes, s, true) == 1) { + free(s); return 1; } + free(s); } if (!isRecurse) { tty->print_cr("type \"%s\" not found", typeName); From ce51b6ebee9ab3b0051243e8cb9be689b99f65a8 Mon Sep 17 00:00:00 2001 From: Jonathan Gibbons Date: Thu, 10 Jan 2013 19:36:13 -0800 Subject: [PATCH 006/138] 8004834: Add doclint support into javadoc Reviewed-by: erikj, tbell --- common/makefiles/javadoc/Javadoc.gmk | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/makefiles/javadoc/Javadoc.gmk b/common/makefiles/javadoc/Javadoc.gmk index 84f0b5f8494..2d919c05462 100644 --- a/common/makefiles/javadoc/Javadoc.gmk +++ b/common/makefiles/javadoc/Javadoc.gmk @@ -1,4 +1,4 @@ -# Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -271,6 +271,7 @@ COMMON_JAVADOCFLAGS = \ -quiet \ -use \ -keywords \ + -Xdoclint:none \ $(ADDITIONAL_JAVADOCFLAGS) ifdef OPENJDK From 4837a1c13da1ae7bbc6a3e9b856da3dd7f9871b0 Mon Sep 17 00:00:00 2001 From: Jonathan Gibbons Date: Thu, 10 Jan 2013 19:36:36 -0800 Subject: [PATCH 007/138] 8004834: Add doclint support into javadoc Reviewed-by: erikj, tbell --- jdk/make/docs/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/jdk/make/docs/Makefile b/jdk/make/docs/Makefile index e181b66a4ed..c49fa8fcfcc 100644 --- a/jdk/make/docs/Makefile +++ b/jdk/make/docs/Makefile @@ -207,6 +207,7 @@ COMMON_JAVADOCFLAGS = \ -quiet \ -use \ -keywords \ + -Xdoclint:none \ $(ADDITIONAL_JAVADOCFLAGS) ifdef OPENJDK From 3dfc6b7512f4c1aca89e910df43a60bead790bfa Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Thu, 10 Jan 2013 21:12:27 -0800 Subject: [PATCH 008/138] 8006062: Add @Repeatable to repeating annotations regression tests in JDK repo Reviewed-by: jjg --- .../annotation/repeatingAnnotations/subpackage/Containee.java | 1 + .../repeatingAnnotations/subpackage/InheritedContainee.java | 1 + 2 files changed, 2 insertions(+) diff --git a/jdk/test/java/lang/annotation/repeatingAnnotations/subpackage/Containee.java b/jdk/test/java/lang/annotation/repeatingAnnotations/subpackage/Containee.java index 93fd36b6e73..c977b2e5c2d 100644 --- a/jdk/test/java/lang/annotation/repeatingAnnotations/subpackage/Containee.java +++ b/jdk/test/java/lang/annotation/repeatingAnnotations/subpackage/Containee.java @@ -27,6 +27,7 @@ import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @ContainedBy(Container.class) +@Repeatable(Container.class) public @interface Containee { int value(); } diff --git a/jdk/test/java/lang/annotation/repeatingAnnotations/subpackage/InheritedContainee.java b/jdk/test/java/lang/annotation/repeatingAnnotations/subpackage/InheritedContainee.java index be68836ef1d..080d12ce3f5 100644 --- a/jdk/test/java/lang/annotation/repeatingAnnotations/subpackage/InheritedContainee.java +++ b/jdk/test/java/lang/annotation/repeatingAnnotations/subpackage/InheritedContainee.java @@ -28,6 +28,7 @@ import java.lang.annotation.*; @Inherited @Retention(RetentionPolicy.RUNTIME) @ContainedBy(InheritedContainer.class) +@Repeatable(InheritedContainer.class) public @interface InheritedContainee { int value(); } From d06c06026ac94ec811ac7b1d4825f9be5a0a9c0b Mon Sep 17 00:00:00 2001 From: Alejandro Murillo Date: Fri, 11 Jan 2013 02:02:51 -0800 Subject: [PATCH 009/138] 8006034: new hotspot build - hs25-b16 Reviewed-by: jcoomes --- hotspot/make/hotspot_version | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hotspot/make/hotspot_version b/hotspot/make/hotspot_version index d6959b3b86c..946cb5c1aa8 100644 --- a/hotspot/make/hotspot_version +++ b/hotspot/make/hotspot_version @@ -1,5 +1,5 @@ # -# Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -31,11 +31,11 @@ # # Don't put quotes (fail windows build). -HOTSPOT_VM_COPYRIGHT=Copyright 2012 +HOTSPOT_VM_COPYRIGHT=Copyright 2013 HS_MAJOR_VER=25 HS_MINOR_VER=0 -HS_BUILD_NUMBER=15 +HS_BUILD_NUMBER=16 JDK_MAJOR_VER=1 JDK_MINOR_VER=8 From 36e2ef2ba85f9fdabef67c1e4a2384f8eda47c81 Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Fri, 11 Jan 2013 12:27:57 +0000 Subject: [PATCH 010/138] 8005566: (fs) test/java/nio/file/Files/Misc.java failing (sol) Reviewed-by: chegar --- .../nio/fs/SolarisAclFileAttributeView.java | 27 ++++++++----------- jdk/test/java/nio/file/Files/Misc.java | 2 +- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/jdk/src/solaris/classes/sun/nio/fs/SolarisAclFileAttributeView.java b/jdk/src/solaris/classes/sun/nio/fs/SolarisAclFileAttributeView.java index 54023bf8265..74c804d94d9 100644 --- a/jdk/src/solaris/classes/sun/nio/fs/SolarisAclFileAttributeView.java +++ b/jdk/src/solaris/classes/sun/nio/fs/SolarisAclFileAttributeView.java @@ -52,10 +52,10 @@ class SolarisAclFileAttributeView /** * typedef struct ace { * uid_t a_who; - * uitn32_t a_access_mark; + * uint32_t a_access_mask; * uint16_t a_flags; * uint16_t a_type; - * } act_t; + * } ace_t; */ private static final short SIZEOF_ACE_T = 12; private static final short OFFSETOF_UID = 0; @@ -209,21 +209,16 @@ class SolarisAclFileAttributeView // map uid and flags to UserPrincipal UnixUserPrincipals.User who = null; - if (uid == -1) { - if ((flags & ACE_OWNER) > 0) - who = UnixUserPrincipals.SPECIAL_OWNER; - if ((flags & ACE_GROUP) > 0) - who = UnixUserPrincipals.SPECIAL_GROUP; - if ((flags & ACE_EVERYONE) > 0) - who = UnixUserPrincipals.SPECIAL_EVERYONE; - if (who == null) - throw new AssertionError("ACE who not handled"); + if ((flags & ACE_OWNER) > 0) { + who = UnixUserPrincipals.SPECIAL_OWNER; + } else if ((flags & ACE_GROUP) > 0) { + who = UnixUserPrincipals.SPECIAL_GROUP; + } else if ((flags & ACE_EVERYONE) > 0) { + who = UnixUserPrincipals.SPECIAL_EVERYONE; + } else if ((flags & ACE_IDENTIFIER_GROUP) > 0) { + who = UnixUserPrincipals.fromGid(uid); } else { - // can be gid - if ((flags & ACE_IDENTIFIER_GROUP) > 0) - who = UnixUserPrincipals.fromGid(uid); - else - who = UnixUserPrincipals.fromUid(uid); + who = UnixUserPrincipals.fromUid(uid); } AclEntryType aceType = null; diff --git a/jdk/test/java/nio/file/Files/Misc.java b/jdk/test/java/nio/file/Files/Misc.java index 785dbefa1d1..6e704ee4489 100644 --- a/jdk/test/java/nio/file/Files/Misc.java +++ b/jdk/test/java/nio/file/Files/Misc.java @@ -22,7 +22,7 @@ */ /* @test - * @bug 4313887 6838333 + * @bug 4313887 6838333 8005566 * @summary Unit test for miscellenous methods in java.nio.file.Files * @library .. */ From 93d2366337fae6b1607a4c6368bc3fa9e872dfca Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Fri, 11 Jan 2013 12:30:54 -0500 Subject: [PATCH 011/138] 8005936: PrintNMTStatistics doesn't work for normal JVM exit Moved NMT shutdown code to JVM exit handler to ensure NMT statistics is printed when PrintNMTStatistics is enabled Reviewed-by: acorn, dholmes, coleenp --- hotspot/src/share/vm/runtime/java.cpp | 4 ++++ hotspot/src/share/vm/runtime/thread.cpp | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hotspot/src/share/vm/runtime/java.cpp b/hotspot/src/share/vm/runtime/java.cpp index ad6399495b0..fb26ad9ddfc 100644 --- a/hotspot/src/share/vm/runtime/java.cpp +++ b/hotspot/src/share/vm/runtime/java.cpp @@ -542,6 +542,10 @@ void before_exit(JavaThread * thread) { BeforeExit_lock->notify_all(); } + // Shutdown NMT before exit. Otherwise, + // it will run into trouble when system destroys static variables. + MemTracker::shutdown(MemTracker::NMT_normal); + #undef BEFORE_EXIT_NOT_RUN #undef BEFORE_EXIT_RUNNING #undef BEFORE_EXIT_DONE diff --git a/hotspot/src/share/vm/runtime/thread.cpp b/hotspot/src/share/vm/runtime/thread.cpp index db3cace3d64..971f4f8dad1 100644 --- a/hotspot/src/share/vm/runtime/thread.cpp +++ b/hotspot/src/share/vm/runtime/thread.cpp @@ -4011,10 +4011,6 @@ bool Threads::destroy_vm() { Mutex::_as_suspend_equivalent_flag); } - // Shutdown NMT before exit. Otherwise, - // it will run into trouble when system destroys static variables. - MemTracker::shutdown(MemTracker::NMT_normal); - // Hang forever on exit if we are reporting an error. if (ShowMessageBoxOnError && is_error_reported()) { os::infinite_sleep(); From c12b62407867972568c9c6c25327dbd8fbee4397 Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Fri, 11 Jan 2013 20:19:55 +0000 Subject: [PATCH 012/138] 8005978: shell tests need to use the $COMPILEJDK for javac, jar and other tools Reviewed-by: chegar --- jdk/test/ProblemList.txt | 3 ++ .../HotSpotDiagnosticMXBean/DumpHeap.sh | 12 +++---- .../GetMaxFileDescriptorCount.sh | 12 +++---- .../GetOpenFileDescriptorCount.sh | 13 ++++--- jdk/test/java/io/FileOutputStream/FileOpen.sh | 6 ++-- jdk/test/java/io/Serializable/class/run.sh | 36 ++++++++++--------- .../evolution/RenamePackage/run.sh | 15 +++++--- .../Serializable/maskSyntheticModifier/run.sh | 10 ++++-- .../java/io/Serializable/packageAccess/run.sh | 15 +++++--- .../Serializable/resolveClass/consTest/run.sh | 15 +++++--- .../resolveClass/deserializeButton/run.sh | 14 +++++--- .../io/Serializable/superclassDataLoss/run.sh | 16 ++++++--- .../Serializable/unnamedPackageSwitch/run.sh | 11 ++++-- .../lang/Class/getEnclosingClass/build.sh | 5 +-- jdk/test/java/lang/ClassLoader/Assert.sh | 6 +++- .../ClassLoader/deadlock/TestCrossDelegate.sh | 8 +++-- .../deadlock/TestOneWayDelegate.sh | 9 +++-- .../System/MacJNUEncoding/MacJNUEncoding.sh | 9 +++-- .../java/lang/Thread/UncaughtExceptions.sh | 4 +-- .../lang/annotation/loaderLeak/LoaderLeak.sh | 8 +++-- .../AppendToBootstrapClassPathSetUp.sh | 8 ++++- .../lang/instrument/AppendToClassPathSetUp.sh | 10 ++++-- .../BootClassPath/BootClassPathTest.sh | 27 +++++++++----- jdk/test/java/lang/instrument/MakeJAR.sh | 15 +++++--- jdk/test/java/lang/instrument/MakeJAR2.sh | 18 ++++++---- jdk/test/java/lang/instrument/MakeJAR3.sh | 14 +++++--- jdk/test/java/lang/instrument/MakeJAR4.sh | 14 +++++--- jdk/test/java/lang/instrument/ManifestTest.sh | 16 ++++++--- .../instrument/ParallelTransformerLoader.sh | 14 +++++--- .../instrument/PremainClass/NoPremainAgent.sh | 8 ++++- .../PremainClass/PremainClassTest.sh | 10 ++++-- .../PremainClass/ZeroArgPremainAgent.sh | 8 ++++- .../java/lang/instrument/RedefineBigClass.sh | 8 ++++- .../RedefineClassWithNativeMethod.sh | 8 ++++- .../instrument/RedefineMethodAddInvoke.sh | 12 +++++-- .../java/lang/instrument/RedefineSetUp.sh | 12 +++++-- .../lang/instrument/RetransformBigClass.sh | 8 ++++- .../CircularityErrorTest.sh | 15 +++++--- .../ClassUnloadTest.sh | 5 +-- .../appendToClassLoaderSearch/CommonSetup.sh | 10 ++++-- .../appendToClassLoaderSearch/run_tests.sh | 16 ++++----- jdk/test/java/net/Authenticator/B4933582.sh | 3 +- jdk/test/java/net/URL/B5086147.sh | 2 +- jdk/test/java/net/URL/runconstructor.sh | 3 +- jdk/test/java/net/URLClassLoader/B5077773.sh | 3 +- .../net/URLClassLoader/closetest/build.sh | 19 ++++++---- .../getresourceasstream/test.sh | 2 +- .../net/URLClassLoader/sealing/checksealed.sh | 4 ++- .../java/net/URLConnection/6212146/test.sh | 2 +- jdk/test/java/net/URLConnection/UNCTest.sh | 2 +- jdk/test/java/nio/charset/spi/basic.sh | 5 +-- .../Activatable/extLoadedImpl/ext.sh | 2 +- .../java/rmi/registry/readTest/readTest.sh | 6 ++-- .../ClassLoaderDeadlock.sh | 8 +++-- .../Security/ClassLoaderDeadlock/Deadlock2.sh | 8 +++-- .../java/security/Security/signedfirst/Dyn.sh | 6 +++- .../security/Security/signedfirst/Static.sh | 6 +++- .../cert/CertificateFactory/slowstream.sh | 6 +++- jdk/test/java/util/Formatter/Basic.sh | 5 +-- jdk/test/java/util/Locale/LocaleProviders.sh | 9 +++-- .../java/util/PluggableLocale/ExecTest.sh | 7 +++- jdk/test/java/util/ServiceLoader/basic.sh | 5 +-- .../TimeZone/TimeZoneDatePermissionCheck.sh | 12 ++++--- jdk/test/java/util/prefs/PrefsSpi.sh | 11 +++--- .../crypto/SecretKeyFactory/FailOverTest.sh | 7 +++- jdk/test/javax/script/CommonSetup.sh | 9 +++-- jdk/test/javax/script/ProviderTest.sh | 2 +- .../javax/security/auth/Subject/doAs/Test.sh | 3 +- .../lib/security/java.policy/Ext_AllPolicy.sh | 5 ++- .../bootstrap/PasswordFilePermissionTest.sh | 2 +- .../bootstrap/SSLConfigFilePermissionTest.sh | 2 +- .../jmxremote/startstop/JMXStartStopTest.sh | 10 +++--- jdk/test/sun/net/www/MarkResetTest.sh | 2 +- .../sun/net/www/http/HttpClient/RetryPost.sh | 2 +- jdk/test/sun/net/www/protocol/jar/B5105410.sh | 2 +- .../sun/net/www/protocol/jar/jarbug/run.sh | 8 ++--- jdk/test/sun/security/krb5/config/dns.sh | 7 ++-- jdk/test/sun/security/krb5/runNameEquals.sh | 6 +++- .../security/mscapi/IsSunMSCAPIAvailable.sh | 2 +- .../sun/security/pkcs11/KeyStore/Basic.sh | 6 +++- .../security/pkcs11/KeyStore/ClientAuth.sh | 6 +++- .../sun/security/pkcs11/KeyStore/Solaris.sh | 6 +++- .../pkcs11/Provider/ConfigQuotedString.sh | 6 +++- .../sun/security/pkcs11/Provider/Login.sh | 6 +++- .../GrantAllPermToExtWhenNoPolicy.sh | 12 +++++-- .../PolicyFile/getinstance/getinstance.sh | 14 +++++--- .../EngineArgs/DebugReportsOneExtraByte.sh | 3 +- .../ssl/SSLSocketImpl/NotifyHandshakeTest.sh | 10 ++++-- .../https/HttpsURLConnection/PostThruProxy.sh | 6 ++-- .../PostThruProxyWithAuth.sh | 2 +- .../sun/security/tools/keytool/autotest.sh | 5 ++- .../sun/security/tools/keytool/printssl.sh | 5 ++- .../sun/security/tools/keytool/readjar.sh | 11 +++--- .../sun/security/tools/keytool/standard.sh | 3 +- jdk/test/sun/security/util/Oid/S11N.sh | 5 ++- .../sun/security/validator/certreplace.sh | 5 +-- jdk/test/sun/security/validator/samedn.sh | 5 +-- jdk/test/tools/launcher/ClassPathWildCard.sh | 12 ++++--- jdk/test/tools/launcher/MultipleJRE.sh | 6 +++- 99 files changed, 568 insertions(+), 254 deletions(-) diff --git a/jdk/test/ProblemList.txt b/jdk/test/ProblemList.txt index 3aa7cfff170..6929924565e 100644 --- a/jdk/test/ProblemList.txt +++ b/jdk/test/ProblemList.txt @@ -347,6 +347,9 @@ com/sun/jdi/ProcessAttachTest.sh generic-all # jdk_util +# 8006090 +java/util/Formatter/Basic.java generic-all + # Filed 6933803 java/util/concurrent/ThreadPoolExecutor/CoreThreadTimeOut.java generic-all diff --git a/jdk/test/com/sun/management/HotSpotDiagnosticMXBean/DumpHeap.sh b/jdk/test/com/sun/management/HotSpotDiagnosticMXBean/DumpHeap.sh index fe11904a471..9d750c5627f 100644 --- a/jdk/test/com/sun/management/HotSpotDiagnosticMXBean/DumpHeap.sh +++ b/jdk/test/com/sun/management/HotSpotDiagnosticMXBean/DumpHeap.sh @@ -32,15 +32,15 @@ # @build DumpHeap # @run shell DumpHeap.sh -#Set appropriate jdk - -if [ ! -z "${TESTJAVA}" ] ; then - jdk="$TESTJAVA" -else +if [ "${TESTJAVA}" = "" ] ; then echo "--Error: TESTJAVA must be defined as the pathname of a jdk to test." exit 1 fi +if [ "${COMPILEJAVA}" = "" ] ; then + COMPILEJAVA="${TESTJAVA}" +fi + failed=0 # we use the pid of this shell process to name the heap dump output file. @@ -50,7 +50,7 @@ ${TESTJAVA}/bin/java ${TESTVMOPTS} -classpath $TESTCLASSES \ DumpHeap ${DUMPFILE} || exit 2 # check that heap dump is parsable -${TESTJAVA}/bin/jhat -parseonly true ${DUMPFILE} +${COMPILEJAVA}/bin/jhat ${TESTTOOLVMOPTS} -parseonly true ${DUMPFILE} if [ $? != 0 ]; then failed=1; fi # dump file is large so remove it diff --git a/jdk/test/com/sun/management/UnixOperatingSystemMXBean/GetMaxFileDescriptorCount.sh b/jdk/test/com/sun/management/UnixOperatingSystemMXBean/GetMaxFileDescriptorCount.sh index 47110c0e752..401a40008bf 100644 --- a/jdk/test/com/sun/management/UnixOperatingSystemMXBean/GetMaxFileDescriptorCount.sh +++ b/jdk/test/com/sun/management/UnixOperatingSystemMXBean/GetMaxFileDescriptorCount.sh @@ -30,19 +30,19 @@ # @run shell GetMaxFileDescriptorCount.sh # -#Set appropriate jdk - -if [ ! -z "${TESTJAVA}" ] ; then - jdk="$TESTJAVA" -else +if [ "${TESTJAVA}" = "" ] ; then echo "--Error: TESTJAVA must be defined as the pathname of a jdk to test." exit 1 fi +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi runOne() { echo "runOne $@" - $TESTJAVA/bin/javac -d $TESTCLASSES $TESTSRC/$@.java || exit 2 + $COMPILEJAVA/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d $TESTCLASSES \ + $TESTSRC/$@.java || exit 2 $TESTJAVA/bin/java ${TESTVMOPTS} -classpath $TESTCLASSES $@ || exit 3 } diff --git a/jdk/test/com/sun/management/UnixOperatingSystemMXBean/GetOpenFileDescriptorCount.sh b/jdk/test/com/sun/management/UnixOperatingSystemMXBean/GetOpenFileDescriptorCount.sh index bc3ef465fc3..28ea936733d 100644 --- a/jdk/test/com/sun/management/UnixOperatingSystemMXBean/GetOpenFileDescriptorCount.sh +++ b/jdk/test/com/sun/management/UnixOperatingSystemMXBean/GetOpenFileDescriptorCount.sh @@ -30,19 +30,18 @@ # @run shell GetOpenFileDescriptorCount.sh # -#Set appropriate jdk - -if [ ! -z "${TESTJAVA}" ] ; then - jdk="$TESTJAVA" -else +if [ "${TESTJAVA}" = "" ] ; then echo "--Error: TESTJAVA must be defined as the pathname of a jdk to test." exit 1 fi - +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi runOne() { echo "runOne $@" - $TESTJAVA/bin/javac -d $TESTCLASSES $TESTSRC/$@.java || exit 2 + $COMPILEJAVA/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d $TESTCLASSES \ + $TESTSRC/$@.java || exit 2 $TESTJAVA/bin/java ${TESTVMOPTS} -classpath $TESTCLASSES $@ || exit 3 } diff --git a/jdk/test/java/io/FileOutputStream/FileOpen.sh b/jdk/test/java/io/FileOutputStream/FileOpen.sh index d79c262d441..83dfb2773e4 100644 --- a/jdk/test/java/io/FileOutputStream/FileOpen.sh +++ b/jdk/test/java/io/FileOutputStream/FileOpen.sh @@ -46,8 +46,10 @@ case "$OS" in echo "Could not find the directory-" ${TMP} "- passing test" exit 0; fi - ${TESTJAVA}/bin/javac -d . ${TESTSRC}\\FileOpenPos.java - ${TESTJAVA}/bin/javac -d . ${TESTSRC}\\FileOpenNeg.java + ${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . \ + ${TESTSRC}\\FileOpenPos.java + ${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . \ + ${TESTSRC}\\FileOpenNeg.java echo "Opening Writable Normal File.." ${TESTJAVA}/bin/java ${TESTVMOPTS} FileOpenPos ${hfile} diff --git a/jdk/test/java/io/Serializable/class/run.sh b/jdk/test/java/io/Serializable/class/run.sh index 26ad083908b..9d82501d01b 100644 --- a/jdk/test/java/io/Serializable/class/run.sh +++ b/jdk/test/java/io/Serializable/class/run.sh @@ -34,17 +34,21 @@ then exit 1 fi +if [ "${COMPILEJAVA}" = "" ] ; then + COMPILEJAVA="${TESTJAVA}" +fi + if [ "${TESTSRC}" = "" ] then TESTSRC="." fi -${TESTJAVA}/bin/javac -d . ${TESTSRC}/Test.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . ${TESTSRC}/Test.java echo Write NonSerial1, Read NonSerial1 rm -f A.java cp ${TESTSRC}/NonSerialA_1.java A.java -${TESTJAVA}/bin/javac A.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} A.java ${TESTJAVA}/bin/java ${TESTVMOPTS} Test -s A ${TESTJAVA}/bin/java ${TESTVMOPTS} Test -d echo @@ -52,77 +56,77 @@ echo echo Write NonSerial1, Read NonSerial2 rm -f A.java cp ${TESTSRC}/NonSerialA_1.java A.java -${TESTJAVA}/bin/javac A.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} A.java ${TESTJAVA}/bin/java ${TESTVMOPTS} Test -s A rm -f A.java cp ${TESTSRC}/NonSerialA_2.java A.java -${TESTJAVA}/bin/javac A.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} A.java ${TESTJAVA}/bin/java ${TESTVMOPTS} Test -d echo echo Write NonSerial1, Read Serial1 rm -f A.java cp ${TESTSRC}/NonSerialA_1.java A.java -${TESTJAVA}/bin/javac A.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} A.java ${TESTJAVA}/bin/java ${TESTVMOPTS} Test -s A rm -f A.java cp ${TESTSRC}/SerialA_1.java A.java -${TESTJAVA}/bin/javac A.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} A.java ${TESTJAVA}/bin/java ${TESTVMOPTS} Test -d echo echo Write Serial1, Read NonSerial1 rm -f A.java cp ${TESTSRC}/SerialA_1.java A.java -${TESTJAVA}/bin/javac A.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} A.java ${TESTJAVA}/bin/java ${TESTVMOPTS} Test -s A rm -f A.java cp ${TESTSRC}/NonSerialA_1.java A.java -${TESTJAVA}/bin/javac A.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} A.java ${TESTJAVA}/bin/java ${TESTVMOPTS} Test -doe echo echo Write Serial1, Read Serial2 rm -f A.java cp ${TESTSRC}/SerialA_1.java A.java -${TESTJAVA}/bin/javac A.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} A.java ${TESTJAVA}/bin/java ${TESTVMOPTS} Test -s A rm -f A.java cp ${TESTSRC}/SerialA_2.java A.java -${TESTJAVA}/bin/javac A.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} A.java ${TESTJAVA}/bin/java ${TESTVMOPTS} Test -d echo echo Write Serial2, Read Serial1 rm -f A.java cp ${TESTSRC}/SerialA_2.java A.java -${TESTJAVA}/bin/javac A.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} A.java ${TESTJAVA}/bin/java ${TESTVMOPTS} Test -s A rm -f A.java cp ${TESTSRC}/SerialA_1.java A.java -${TESTJAVA}/bin/javac A.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} A.java ${TESTJAVA}/bin/java ${TESTVMOPTS} Test -d echo echo Write Serial1, Read Serial3 rm -f A.java cp ${TESTSRC}/SerialA_1.java A.java -${TESTJAVA}/bin/javac A.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} A.java ${TESTJAVA}/bin/java ${TESTVMOPTS} Test -s A rm -f A.java cp ${TESTSRC}/SerialA_3.java A.java -${TESTJAVA}/bin/javac A.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} A.java ${TESTJAVA}/bin/java ${TESTVMOPTS} Test -de echo echo Write Serial3, Read Serial1 rm -f A.java cp ${TESTSRC}/SerialA_3.java A.java -${TESTJAVA}/bin/javac A.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} A.java ${TESTJAVA}/bin/java ${TESTVMOPTS} Test -s A rm -f A.java cp ${TESTSRC}/SerialA_1.java A.java -${TESTJAVA}/bin/javac A.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} A.java ${TESTJAVA}/bin/java ${TESTVMOPTS} Test -de echo diff --git a/jdk/test/java/io/Serializable/evolution/RenamePackage/run.sh b/jdk/test/java/io/Serializable/evolution/RenamePackage/run.sh index afa751a8474..deda2826115 100644 --- a/jdk/test/java/io/Serializable/evolution/RenamePackage/run.sh +++ b/jdk/test/java/io/Serializable/evolution/RenamePackage/run.sh @@ -36,6 +36,10 @@ then exit 1 fi +if [ "${COMPILEJAVA}" = "" ] ; then + COMPILEJAVA="${TESTJAVA}" +fi + OS=`uname -s` # Need to determine the classpath separator and filepath separator based on the @@ -51,7 +55,7 @@ Windows* | CYGWIN* ) esac JAVA=${TESTJAVA}/bin/java -JAVAC=${TESTJAVA}/bin/javac +JAVAC=${COMPILEJAVA}/bin/javac MKDIR=mkdir RDEL="rm -r" @@ -78,11 +82,14 @@ mkdir ${TESTCLASSES}/nclasses # Build sources set -e -${JAVAC} -d ${TESTCLASSES}/share ${TESTSRC}/extension/ExtendedObjectInputStream.java +${JAVAC} ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d ${TESTCLASSES}/share \ + ${TESTSRC}/extension/ExtendedObjectInputStream.java CLASSPATH=${TESTCLASSES}/share; export CLASSPATH; -${JAVAC} -d ${TESTCLASSES}/oclasses ${TESTSRC}/test/SerialDriver.java +${JAVAC} ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d ${TESTCLASSES}/oclasses \ + ${TESTSRC}/test/SerialDriver.java CLASSPATH=${TESTCLASSES}/share; export CLASSPATH; -${JAVAC} -d ${TESTCLASSES}/nclasses ${TESTSRC}/install/SerialDriver.java +${JAVAC} ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d ${TESTCLASSES}/nclasses \ + ${TESTSRC}/install/SerialDriver.java # Run Case 1. Map test.SerialDriver within stream to install.SerialDriver. CLASSPATH="${TESTCLASSES}/oclasses${PS}${TESTCLASSES}/share"; export CLASSPATH; diff --git a/jdk/test/java/io/Serializable/maskSyntheticModifier/run.sh b/jdk/test/java/io/Serializable/maskSyntheticModifier/run.sh index 075e0dfafb8..fdf903fd5c4 100644 --- a/jdk/test/java/io/Serializable/maskSyntheticModifier/run.sh +++ b/jdk/test/java/io/Serializable/maskSyntheticModifier/run.sh @@ -29,17 +29,21 @@ if [ "${TESTJAVA}" = "" ] then - echo "TESTJAVA not set. Test cannot execute. Failed." + echo "TESTJAVA not set. Test cannot execute. Failed." exit 1 fi +if [ "${COMPILEJAVA}" = "" ] ; then + COMPILEJAVA="${TESTJAVA}" +fi + if [ "${TESTSRC}" = "" ] then - TESTSRC="." + TESTSRC="." fi set -ex cp ${TESTSRC}/Foo.class . -${TESTJAVA}/bin/javac -d . ${TESTSRC}/Test.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . ${TESTSRC}/Test.java ${TESTJAVA}/bin/java ${TESTVMOPTS} Test rm -f *.class diff --git a/jdk/test/java/io/Serializable/packageAccess/run.sh b/jdk/test/java/io/Serializable/packageAccess/run.sh index 3a7f0c148f7..7c946674fd1 100644 --- a/jdk/test/java/io/Serializable/packageAccess/run.sh +++ b/jdk/test/java/io/Serializable/packageAccess/run.sh @@ -29,20 +29,25 @@ if [ "${TESTJAVA}" = "" ] then - echo "TESTJAVA not set. Test cannot execute. Failed." + echo "TESTJAVA not set. Test cannot execute. Failed." exit 1 fi +if [ "${COMPILEJAVA}" = "" ] ; then + COMPILEJAVA="${TESTJAVA}" +fi + if [ "${TESTSRC}" = "" ] then - TESTSRC="." + TESTSRC="." fi set -ex -${TESTJAVA}/bin/javac -d . ${TESTSRC}/A.java ${TESTSRC}/B.java \ - ${TESTSRC}/C.java ${TESTSRC}/D.java ${TESTSRC}/Test.java -${TESTJAVA}/bin/jar cf foo.jar B.class D.class +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . \ + ${TESTSRC}/A.java ${TESTSRC}/B.java ${TESTSRC}/C.java ${TESTSRC}/D.java \ + ${TESTSRC}/Test.java +${COMPILEJAVA}/bin/jar ${TESTTOOLVMOPTS} cf foo.jar B.class D.class rm -f B.class D.class ${TESTJAVA}/bin/java ${TESTVMOPTS} Test diff --git a/jdk/test/java/io/Serializable/resolveClass/consTest/run.sh b/jdk/test/java/io/Serializable/resolveClass/consTest/run.sh index 62cc8ff759a..22e1fb7800b 100644 --- a/jdk/test/java/io/Serializable/resolveClass/consTest/run.sh +++ b/jdk/test/java/io/Serializable/resolveClass/consTest/run.sh @@ -28,21 +28,26 @@ if [ "${TESTJAVA}" = "" ] then - echo "TESTJAVA not set. Test cannot execute. Failed." + echo "TESTJAVA not set. Test cannot execute. Failed." exit 1 fi +if [ "${COMPILEJAVA}" = "" ] ; then + COMPILEJAVA="${TESTJAVA}" +fi + if [ "${TESTSRC}" = "" ] then - TESTSRC="." + TESTSRC="." fi set -ex rm -f *.class *.jar -${TESTJAVA}/bin/javac -d . ${TESTSRC}/Boot.java -${TESTJAVA}/bin/jar cf boot.jar *.class +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . ${TESTSRC}/Boot.java +${COMPILEJAVA}/bin/jar ${TESTTOOLVMOPTS} cf boot.jar *.class rm -f *.class -${TESTJAVA}/bin/javac -classpath boot.jar -d . ${TESTSRC}/Test.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -classpath boot.jar -d . \ + ${TESTSRC}/Test.java ${TESTJAVA}/bin/java ${TESTVMOPTS} -Xbootclasspath/a:boot.jar Test rm -f *.class *.jar diff --git a/jdk/test/java/io/Serializable/resolveClass/deserializeButton/run.sh b/jdk/test/java/io/Serializable/resolveClass/deserializeButton/run.sh index 48208aec4b8..d9cdc3e5078 100644 --- a/jdk/test/java/io/Serializable/resolveClass/deserializeButton/run.sh +++ b/jdk/test/java/io/Serializable/resolveClass/deserializeButton/run.sh @@ -30,21 +30,25 @@ if [ "${TESTJAVA}" = "" ] then - echo "TESTJAVA not set. Test cannot execute. Failed." + echo "TESTJAVA not set. Test cannot execute. Failed." exit 1 fi +if [ "${COMPILEJAVA}" = "" ] ; then + COMPILEJAVA="${TESTJAVA}" +fi + if [ "${TESTSRC}" = "" ] then - TESTSRC="." + TESTSRC="." fi set -ex rm -f *.class *.jar -${TESTJAVA}/bin/javac -d . ${TESTSRC}/Foo.java -${TESTJAVA}/bin/jar cf cb.jar *.class +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . ${TESTSRC}/Foo.java +${COMPILEJAVA}/bin/jar ${TESTTOOLVMOPTS} cf cb.jar *.class rm -f *.class -${TESTJAVA}/bin/javac -d . ${TESTSRC}/Test.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . ${TESTSRC}/Test.java ${TESTJAVA}/bin/java ${TESTVMOPTS} Test rm -f *.class *.jar diff --git a/jdk/test/java/io/Serializable/superclassDataLoss/run.sh b/jdk/test/java/io/Serializable/superclassDataLoss/run.sh index 44473748e3d..52b6c87d41c 100644 --- a/jdk/test/java/io/Serializable/superclassDataLoss/run.sh +++ b/jdk/test/java/io/Serializable/superclassDataLoss/run.sh @@ -29,22 +29,28 @@ if [ "${TESTJAVA}" = "" ] then - echo "TESTJAVA not set. Test cannot execute. Failed." + echo "TESTJAVA not set. Test cannot execute. Failed." exit 1 fi +if [ "${COMPILEJAVA}" = "" ] ; then + COMPILEJAVA="${TESTJAVA}" +fi + if [ "${TESTSRC}" = "" ] then - TESTSRC="." + TESTSRC="." fi set -ex -${TESTJAVA}/bin/javac -d . ${TESTSRC}/A.java ${TESTSRC}/B.java -${TESTJAVA}/bin/jar cf cb1.jar A.class B.class +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . \ + ${TESTSRC}/A.java ${TESTSRC}/B.java +${COMPILEJAVA}/bin/jar ${TESTTOOLVMOPTS} cf cb1.jar A.class B.class cp cb1.jar cb2.jar rm -f A.class B.class -${TESTJAVA}/bin/javac -d . ${TESTSRC}/Test.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . \ + ${TESTSRC}/Test.java ${TESTJAVA}/bin/java ${TESTVMOPTS} Test rm -f *.class *.jar diff --git a/jdk/test/java/io/Serializable/unnamedPackageSwitch/run.sh b/jdk/test/java/io/Serializable/unnamedPackageSwitch/run.sh index f31e5c11495..7dccfb9ca9b 100644 --- a/jdk/test/java/io/Serializable/unnamedPackageSwitch/run.sh +++ b/jdk/test/java/io/Serializable/unnamedPackageSwitch/run.sh @@ -29,16 +29,21 @@ if [ "${TESTJAVA}" = "" ] then - echo "TESTJAVA not set. Test cannot execute. Failed." + echo "TESTJAVA not set. Test cannot execute. Failed." exit 1 fi +if [ "${COMPILEJAVA}" = "" ] ; then + COMPILEJAVA="${TESTJAVA}" +fi + if [ "${TESTSRC}" = "" ] then - TESTSRC="." + TESTSRC="." fi set -ex -${TESTJAVA}/bin/javac -d . ${TESTSRC}/A.java ${TESTSRC}/Test.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . \ + ${TESTSRC}/A.java ${TESTSRC}/Test.java ${TESTJAVA}/bin/java ${TESTVMOPTS} Test diff --git a/jdk/test/java/lang/Class/getEnclosingClass/build.sh b/jdk/test/java/lang/Class/getEnclosingClass/build.sh index 6aeccf0092b..b02c951e25b 100644 --- a/jdk/test/java/lang/Class/getEnclosingClass/build.sh +++ b/jdk/test/java/lang/Class/getEnclosingClass/build.sh @@ -37,5 +37,6 @@ case "${OS}" in ;; esac -JAVAC=${TESTJAVA}/bin/javac -${JAVAC} -d ${TESTCLASSES} -sourcepath ${TESTSRC}${SEP}. ${TESTSRC}/EnclosingClassTest.java +JAVAC=${COMPILEJAVA}/bin/javac +${JAVAC} ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d ${TESTCLASSES} -sourcepath ${TESTSRC}${SEP}. \ + ${TESTSRC}/EnclosingClassTest.java diff --git a/jdk/test/java/lang/ClassLoader/Assert.sh b/jdk/test/java/lang/ClassLoader/Assert.sh index 9bc5e01067b..812e6f43340 100644 --- a/jdk/test/java/lang/ClassLoader/Assert.sh +++ b/jdk/test/java/lang/ClassLoader/Assert.sh @@ -35,6 +35,10 @@ then exit 1 fi echo "TESTJAVA=${TESTJAVA}" +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi +echo "COMPILEJAVA=${COMPILEJAVA}" if [ "${TESTCLASSES}" = "" ] then echo "TESTCLASSES not set. Test cannot execute. Failed." @@ -47,7 +51,7 @@ cp ${TESTSRC}/Assert.java . cp -R ${TESTSRC}/package1 . cp -R ${TESTSRC}/package2 . -${TESTJAVA}/bin/javac Assert.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} Assert.java ${TESTJAVA}/bin/java ${TESTVMOPTS} Assert diff --git a/jdk/test/java/lang/ClassLoader/deadlock/TestCrossDelegate.sh b/jdk/test/java/lang/ClassLoader/deadlock/TestCrossDelegate.sh index 930ba2fc202..24b129aecbc 100644 --- a/jdk/test/java/lang/ClassLoader/deadlock/TestCrossDelegate.sh +++ b/jdk/test/java/lang/ClassLoader/deadlock/TestCrossDelegate.sh @@ -42,6 +42,10 @@ if [ "${TESTJAVA}" = "" ] ; then exit 1 fi +if [ "${COMPILEJAVA}" = "" ] ; then + COMPILEJAVA="${TESTJAVA}" +fi + # set platform-specific variables OS=`uname -s` case "$OS" in @@ -69,7 +73,7 @@ echo TESTJAVA=${TESTJAVA} echo "" # compile test -${TESTJAVA}${FS}bin${FS}javac \ +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ -d ${TESTCLASSES} \ ${TESTSRC}${FS}Starter.java ${TESTSRC}${FS}DelegatingLoader.java @@ -80,7 +84,7 @@ then fi # set up test -${TESTJAVA}${FS}bin${FS}javac \ +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ -d ${TESTCLASSES}${FS} \ ${TESTSRC}${FS}Alice.java ${TESTSRC}${FS}SupBob.java \ ${TESTSRC}${FS}Bob.java ${TESTSRC}${FS}SupAlice.java diff --git a/jdk/test/java/lang/ClassLoader/deadlock/TestOneWayDelegate.sh b/jdk/test/java/lang/ClassLoader/deadlock/TestOneWayDelegate.sh index 311a27602e0..6d5aabb0728 100644 --- a/jdk/test/java/lang/ClassLoader/deadlock/TestOneWayDelegate.sh +++ b/jdk/test/java/lang/ClassLoader/deadlock/TestOneWayDelegate.sh @@ -41,9 +41,14 @@ if [ "${TESTJAVA}" = "" ] ; then echo "FAILED!!!" exit 1 fi +if [ "${COMPILEJAVA}" = "" ] ; then + COMPILEJAVA="${TESTJAVA}" +fi + echo TESTSRC=${TESTSRC} echo TESTCLASSES=${TESTCLASSES} echo TESTJAVA=${TESTJAVA} +echo COMPILEJAVA=${COMPILEJAVA} echo "" # set platform-specific variables @@ -64,7 +69,7 @@ case "$OS" in esac # compile test -${TESTJAVA}${FS}bin${FS}javac \ +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ -d ${TESTCLASSES} \ ${TESTSRC}${FS}Starter.java ${TESTSRC}${FS}DelegatingLoader.java @@ -75,7 +80,7 @@ then fi # set up test -${TESTJAVA}${FS}bin${FS}javac \ +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ -d ${TESTCLASSES}${FS} \ ${TESTSRC}${FS}Alice.java ${TESTSRC}${FS}SupBob.java \ ${TESTSRC}${FS}Bob.java ${TESTSRC}${FS}SupAlice.java diff --git a/jdk/test/java/lang/System/MacJNUEncoding/MacJNUEncoding.sh b/jdk/test/java/lang/System/MacJNUEncoding/MacJNUEncoding.sh index a03bdd91f17..4cf63328236 100644 --- a/jdk/test/java/lang/System/MacJNUEncoding/MacJNUEncoding.sh +++ b/jdk/test/java/lang/System/MacJNUEncoding/MacJNUEncoding.sh @@ -44,6 +44,11 @@ then exit 1 fi +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi + + if [ "${TESTSRC}" = "" ] then echo "TESTSRC not set. Test cannot execute. Failed." @@ -56,11 +61,11 @@ then exit 1 fi -JAVAC="${TESTJAVA}"/bin/javac +JAVAC="${COMPILEJAVA}"/bin/javac JAVA="${TESTJAVA}"/bin/java echo "Building test classes..." -"$JAVAC" -d "${TESTCLASSES}" "${TESTSRC}"/ExpectedEncoding.java +"$JAVAC" ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d "${TESTCLASSES}" "${TESTSRC}"/ExpectedEncoding.java echo "" echo "Running test for C locale" diff --git a/jdk/test/java/lang/Thread/UncaughtExceptions.sh b/jdk/test/java/lang/Thread/UncaughtExceptions.sh index 700a9bca2c9..7ed96672a23 100644 --- a/jdk/test/java/lang/Thread/UncaughtExceptions.sh +++ b/jdk/test/java/lang/Thread/UncaughtExceptions.sh @@ -34,7 +34,7 @@ # To run this test manually, simply do ./UncaughtExceptions.sh java="${TESTJAVA+${TESTJAVA}/bin/}java" -javac="${TESTJAVA+${TESTJAVA}/bin/}javac" +javac="${COMPILEJAVA+${COMPILEJAVA}/bin/}javac" failed="" Fail() { echo "FAIL: $1"; failed="${failed}."; } @@ -121,7 +121,7 @@ public class Seppuku extends Thread implements Runnable { } EOJAVA - Sys "$javac" "Seppuku.java" + Sys "$javac" ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} "Seppuku.java" CheckCommandResults "$expectedRC" "$expectedOut" "$expectedErr" \ "$java" "Seppuku" Cleanup diff --git a/jdk/test/java/lang/annotation/loaderLeak/LoaderLeak.sh b/jdk/test/java/lang/annotation/loaderLeak/LoaderLeak.sh index bc72b936cc3..e62a63cd8a5 100644 --- a/jdk/test/java/lang/annotation/loaderLeak/LoaderLeak.sh +++ b/jdk/test/java/lang/annotation/loaderLeak/LoaderLeak.sh @@ -33,6 +33,10 @@ then exit 1 fi echo "TESTJAVA=${TESTJAVA}" +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi +echo "COMPILEJAVA=${COMPILEJAVA}" if [ "${TESTCLASSES}" = "" ] then echo "TESTCLASSES not set. Test cannot execute. Failed." @@ -67,8 +71,8 @@ esac mkdir -p classes cp ${TESTSRC}${FS}*.java . -${TESTJAVA}${FS}bin${FS}javac -d classes A.java B.java C.java -${TESTJAVA}${FS}bin${FS}javac Main.java +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d classes A.java B.java C.java +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} Main.java ${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} Main result=$? if [ $result -eq 0 ] diff --git a/jdk/test/java/lang/instrument/AppendToBootstrapClassPathSetUp.sh b/jdk/test/java/lang/instrument/AppendToBootstrapClassPathSetUp.sh index fd55fd85bca..e40e4f0bb09 100644 --- a/jdk/test/java/lang/instrument/AppendToBootstrapClassPathSetUp.sh +++ b/jdk/test/java/lang/instrument/AppendToBootstrapClassPathSetUp.sh @@ -42,6 +42,12 @@ then fi echo "TESTJAVA=${TESTJAVA}" +if [ "${COMPILEJAVA}" = "" ] +then + COMPILEJAVA="${TESTJAVA}" +fi +echo "COMPILEJAVA=${COMPILEJAVA}" + if [ "${TESTCLASSES}" = "" ] then echo "TESTCLASSES not set. Test cannot execute. Failed." @@ -51,7 +57,7 @@ fi echo "TESTCLASSES=${TESTCLASSES}" echo "CLASSPATH=${CLASSPATH}" -JAVAC="${TESTJAVA}/bin/javac -g" +JAVAC="${COMPILEJAVA}/bin/javac -g" mkdir -p hidden mv ${TESTCLASSES}/ExampleForBootClassPath.class hidden diff --git a/jdk/test/java/lang/instrument/AppendToClassPathSetUp.sh b/jdk/test/java/lang/instrument/AppendToClassPathSetUp.sh index 4c13f34178f..7c00e0b2520 100644 --- a/jdk/test/java/lang/instrument/AppendToClassPathSetUp.sh +++ b/jdk/test/java/lang/instrument/AppendToClassPathSetUp.sh @@ -42,6 +42,12 @@ then fi echo "TESTJAVA=${TESTJAVA}" +if [ "${COMPILEJAVA}" = "" ] +then + COMPILEJAVA="${TESTJAVA}" +fi +echo "COMPILEJAVA=${COMPILEJAVA}" + if [ "${TESTCLASSES}" = "" ] then echo "TESTCLASSES not set. Test cannot execute. Failed." @@ -51,10 +57,10 @@ fi echo "TESTCLASSES=${TESTCLASSES}" echo "CLASSPATH=${CLASSPATH}" -JAVAC="${TESTJAVA}/bin/javac -g" +JAVAC="${COMPILEJAVA}/bin/javac -g" cp ${TESTSRC}/ExampleForClassPath.java ExampleForClassPath.java -${JAVAC} ExampleForClassPath.java +${JAVAC} ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} ExampleForClassPath.java mkdir -p hidden mv ExampleForClassPath.class hidden rm -f ExampleForClassPath.java diff --git a/jdk/test/java/lang/instrument/BootClassPath/BootClassPathTest.sh b/jdk/test/java/lang/instrument/BootClassPath/BootClassPathTest.sh index fa3ef135c3f..d0e8df33cb4 100644 --- a/jdk/test/java/lang/instrument/BootClassPath/BootClassPathTest.sh +++ b/jdk/test/java/lang/instrument/BootClassPath/BootClassPathTest.sh @@ -34,6 +34,12 @@ then exit 1 fi +if [ "${COMPILEJAVA}" = "" ] +then + COMPILEJAVA="${TESTJAVA}" +fi +echo "COMPILEJAVA=${COMPILEJAVA}" + if [ "${TESTSRC}" = "" ] then echo "TESTSRC not set. Test cannot execute. Failed." @@ -46,30 +52,32 @@ then exit 1 fi -JAVAC="${TESTJAVA}"/bin/javac +JAVAC="${COMPILEJAVA}"/bin/javac JAVA="${TESTJAVA}"/bin/java -JAR="${TESTJAVA}"/bin/jar +JAR="${COMPILEJAVA}"/bin/jar echo "Creating manifest file..." -"$JAVAC" -d "${TESTCLASSES}" "${TESTSRC}"/Setup.java +"$JAVAC" ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d "${TESTCLASSES}" "${TESTSRC}"/Setup.java # java Setup # - outputs boot class path to boot.dir -"$JAVA" -classpath "${TESTCLASSES}" Setup "${TESTCLASSES}" Agent +"$JAVA" ${TESTVMOPTS} -classpath "${TESTCLASSES}" Setup "${TESTCLASSES}" Agent BOOTDIR=`cat ${TESTCLASSES}/boot.dir` echo "Created ${BOOTDIR}" echo "Building test classes..." -"$JAVAC" -d "${TESTCLASSES}" "${TESTSRC}"/Agent.java "${TESTSRC}"/DummyMain.java -"$JAVAC" -d "${BOOTDIR}" "${TESTSRC}"/AgentSupport.java +"$JAVAC" ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d "${TESTCLASSES}" \ + "${TESTSRC}"/Agent.java "${TESTSRC}"/DummyMain.java +"$JAVAC" ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d "${BOOTDIR}" \ + "${TESTSRC}"/AgentSupport.java echo "Creating agent jar file..." -"$JAR" -cvfm "${TESTCLASSES}"/Agent.jar "${TESTCLASSES}"/MANIFEST.MF \ +"$JAR" ${TESTTOOLVMOPTS} -cvfm "${TESTCLASSES}"/Agent.jar "${TESTCLASSES}"/MANIFEST.MF \ -C "${TESTCLASSES}" Agent.class || exit 1 echo "Running test..." @@ -79,7 +87,8 @@ result=$? echo "Cleanup..." -"$JAVAC" -d "${TESTCLASSES}" "${TESTSRC}"/Cleanup.java -"$JAVA" -classpath "${TESTCLASSES}" Cleanup "${BOOTDIR}" +"$JAVAC" ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d "${TESTCLASSES}" \ + "${TESTSRC}"/Cleanup.java +"$JAVA" ${TESTTOOLVMOPTS} -classpath "${TESTCLASSES}" Cleanup "${BOOTDIR}" exit $result diff --git a/jdk/test/java/lang/instrument/MakeJAR.sh b/jdk/test/java/lang/instrument/MakeJAR.sh index ddb1f6990d4..7847a4e66fa 100644 --- a/jdk/test/java/lang/instrument/MakeJAR.sh +++ b/jdk/test/java/lang/instrument/MakeJAR.sh @@ -23,7 +23,6 @@ # questions. # - if [ "${TESTSRC}" = "" ] then echo "TESTSRC not set. Test cannot execute. Failed." @@ -38,16 +37,22 @@ then fi echo "TESTJAVA=${TESTJAVA}" +if [ "${COMPILEJAVA}" = "" ] +then + COMPILEJAVA="${TESTJAVA}" +fi +echo "COMPILEJAVA=${COMPILEJAVA}" + if [ "${TESTCLASSES}" = "" ] then echo "TESTCLASSES not set. Test cannot execute. Failed." exit 1 fi -JAVAC="${TESTJAVA}/bin/javac -g" -JAR="${TESTJAVA}/bin/jar" +JAVAC="${COMPILEJAVA}/bin/javac -g" +JAR="${COMPILEJAVA}/bin/jar" cp ${TESTSRC}/InstrumentationHandoff.java InstrumentationHandoff.java -${JAVAC} InstrumentationHandoff.java -${JAR} cvfm $1.jar ${TESTSRC}/$1.mf InstrumentationHandoff.class +${JAVAC} ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} InstrumentationHandoff.java +${JAR} ${TESTTOOLVMOPTS} cvfm $1.jar ${TESTSRC}/$1.mf InstrumentationHandoff.class rm -f InstrumentationHandoff.class InstrumentationHandoff.java diff --git a/jdk/test/java/lang/instrument/MakeJAR2.sh b/jdk/test/java/lang/instrument/MakeJAR2.sh index 0132b52650b..062abe41444 100644 --- a/jdk/test/java/lang/instrument/MakeJAR2.sh +++ b/jdk/test/java/lang/instrument/MakeJAR2.sh @@ -41,6 +41,12 @@ then fi echo "TESTJAVA=${TESTJAVA}" +if [ "${COMPILEJAVA}" = "" ] +then + COMPILEJAVA="${TESTJAVA}" +fi +echo "COMPILEJAVA=${COMPILEJAVA}" + if [ "${TESTCLASSES}" = "" ] then echo "TESTCLASSES not set. Test cannot execute. Failed." @@ -64,8 +70,8 @@ case "$OS" in ;; esac -JAVAC="${TESTJAVA}/bin/javac -g" -JAR="${TESTJAVA}/bin/jar" +JAVAC="${COMPILEJAVA}/bin/javac -g" +JAR="${COMPILEJAVA}/bin/jar" cp ${TESTSRC}/${AGENT}.java . cp ${TESTSRC}/${APP}.java . @@ -77,11 +83,11 @@ mkdir -p bootpath/bootreporter cp ${TESTSRC}/bootreporter/*.java bootpath/bootreporter cd bootpath -${JAVAC} bootreporter/*.java +${JAVAC} ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} bootreporter/*.java cd .. -${JAVAC} ${AGENT}.java ilib/*.java -${JAVAC} -classpath .${PATHSEP}bootpath ${APP}.java +${JAVAC} ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} ${AGENT}.java ilib/*.java +${JAVAC} ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -classpath .${PATHSEP}bootpath ${APP}.java echo "Manifest-Version: 1.0" > ${AGENT}.mf echo Premain-Class: ${AGENT} >> ${AGENT}.mf @@ -92,6 +98,6 @@ while [ $# != 0 ] ; do shift done -${JAR} cvfm ${AGENT}.jar ${AGENT}.mf ${AGENT}*.class ilib/*.class +${JAR} ${TESTTOOLVMOPTS} cvfm ${AGENT}.jar ${AGENT}.mf ${AGENT}*.class ilib/*.class # rm -rf ${AGENT}.java ilib ${AGENT}.mf ${AGENT}*.class diff --git a/jdk/test/java/lang/instrument/MakeJAR3.sh b/jdk/test/java/lang/instrument/MakeJAR3.sh index bbac7492bf7..9c449406a9e 100644 --- a/jdk/test/java/lang/instrument/MakeJAR3.sh +++ b/jdk/test/java/lang/instrument/MakeJAR3.sh @@ -39,17 +39,23 @@ then fi echo "TESTJAVA=${TESTJAVA}" +if [ "${COMPILEJAVA}" = "" ] +then + COMPILEJAVA="${TESTJAVA}" +fi +echo "COMPILEJAVA=${COMPILEJAVA}" + if [ "${TESTCLASSES}" = "" ] then echo "TESTCLASSES not set. Test cannot execute. Failed." exit 1 fi -JAVAC="${TESTJAVA}/bin/javac -g" -JAR="${TESTJAVA}/bin/jar" +JAVAC="${COMPILEJAVA}/bin/javac -g" +JAR="${COMPILEJAVA}/bin/jar" cp ${TESTSRC}/${AGENT}.java . -${JAVAC} ${AGENT}.java +${JAVAC} ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} ${AGENT}.java echo "Manifest-Version: 1.0" > ${AGENT}.mf echo Premain-Class: ${AGENT} >> ${AGENT}.mf @@ -60,4 +66,4 @@ while [ $# != 0 ] ; do done -${JAR} cvfm ${AGENT}.jar ${AGENT}.mf ${AGENT}*.class +${JAR} ${TESTTOOLVMOPTS} cvfm ${AGENT}.jar ${AGENT}.mf ${AGENT}*.class diff --git a/jdk/test/java/lang/instrument/MakeJAR4.sh b/jdk/test/java/lang/instrument/MakeJAR4.sh index 78580d57ec2..3376bd7cc4c 100644 --- a/jdk/test/java/lang/instrument/MakeJAR4.sh +++ b/jdk/test/java/lang/instrument/MakeJAR4.sh @@ -17,17 +17,23 @@ then fi echo "TESTJAVA=${TESTJAVA}" +if [ "${COMPILEJAVA}" = "" ] +then + COMPILEJAVA="${TESTJAVA}" +fi +echo "COMPILEJAVA=${COMPILEJAVA}" + if [ "${TESTCLASSES}" = "" ] then echo "TESTCLASSES not set. Test cannot execute. Failed." exit 1 fi -JAVAC="${TESTJAVA}/bin/javac -g" -JAR="${TESTJAVA}/bin/jar" +JAVAC="${COMPILEJAVA}/bin/javac -g" +JAR="${COMPILEJAVA}/bin/jar" cp ${TESTSRC}/${AGENT}.java ${TESTSRC}/${OTHER}.java . -${JAVAC} ${AGENT}.java ${OTHER}.java +${JAVAC} ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} ${AGENT}.java ${OTHER}.java echo "Manifest-Version: 1.0" > ${AGENT}.mf echo Premain-Class: ${AGENT} >> ${AGENT}.mf @@ -37,4 +43,4 @@ while [ $# != 0 ] ; do done -${JAR} cvfm ${AGENT}.jar ${AGENT}.mf ${AGENT}*.class ${OTHER}*.java +${JAR} "{TESTTOOLVMOPTS}" cvfm ${AGENT}.jar ${AGENT}.mf ${AGENT}*.class ${OTHER}*.java diff --git a/jdk/test/java/lang/instrument/ManifestTest.sh b/jdk/test/java/lang/instrument/ManifestTest.sh index 4fbd51af158..c97639629a9 100644 --- a/jdk/test/java/lang/instrument/ManifestTest.sh +++ b/jdk/test/java/lang/instrument/ManifestTest.sh @@ -312,7 +312,7 @@ make_a_JAR() { fi rm -f ${AGENT}.jar - ${JAR} cvfm ${AGENT}.jar ${AGENT}.mf ${AGENT}.class + ${JAR} ${TESTTOOLVMOPTS} cvfm ${AGENT}.jar ${AGENT}.mf ${AGENT}.class echo "$expect_boot_cp_line" > expect_boot_cp_line echo "$expect_redef_line" > expect_redef_line @@ -326,6 +326,12 @@ then exit 1 fi +if [ "${COMPILEJAVA}" = "" ] +then + COMPILEJAVA="${TESTJAVA}" +fi +echo "COMPILEJAVA=${COMPILEJAVA}" + if [ "${TESTSRC}" = "" ] then echo "TESTSRC not set. Test cannot execute. Failed." @@ -338,8 +344,8 @@ then exit 1 fi -JAR="${TESTJAVA}/bin/jar" -JAVAC="${TESTJAVA}"/bin/javac +JAR="${COMPILEJAVA}/bin/jar" +JAVAC="${COMPILEJAVA}"/bin/javac JAVA="${TESTJAVA}"/bin/java # Now that ManifestTestApp.class is built, we move @@ -353,7 +359,7 @@ mv "${TESTCLASSES}/ExampleForBootClassPath.class" $OUT_OF_THE_WAY # so we can tell when the wrong version is run sed 's/return 15/return 42/' "${TESTSRC}"/ExampleForBootClassPath.java \ > ExampleForBootClassPath.java -"$JAVAC" ExampleForBootClassPath.java +"$JAVAC" ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} ExampleForBootClassPath.java mv ExampleForBootClassPath.class \ $OUT_OF_THE_WAY/ExampleForBootClassPath.class.bad mv ExampleForBootClassPath.java \ @@ -363,7 +369,7 @@ AGENT=ManifestTestAgent # We compile the agent in the working directory instead of with # a build task because we construct a different agent JAR file # for each test case. -${JAVAC} -d . ${TESTSRC}/${AGENT}.java +${JAVAC} ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . ${TESTSRC}/${AGENT}.java FAIL_MARKER=fail_marker rm -f $FAIL_MARKER diff --git a/jdk/test/java/lang/instrument/ParallelTransformerLoader.sh b/jdk/test/java/lang/instrument/ParallelTransformerLoader.sh index 4cfe4b4a1d8..6d8c6d5adf5 100644 --- a/jdk/test/java/lang/instrument/ParallelTransformerLoader.sh +++ b/jdk/test/java/lang/instrument/ParallelTransformerLoader.sh @@ -38,6 +38,12 @@ then exit 1 fi +if [ "${COMPILEJAVA}" = "" ] +then + COMPILEJAVA="${TESTJAVA}" +fi +echo "COMPILEJAVA=${COMPILEJAVA}" + if [ "${TESTSRC}" = "" ] then echo "TESTSRC not set. Test cannot execute. Failed." @@ -50,16 +56,16 @@ then exit 1 fi -JAR="${TESTJAVA}"/bin/jar -JAVAC="${TESTJAVA}"/bin/javac +JAR="${COMPILEJAVA}"/bin/jar +JAVAC="${COMPILEJAVA}"/bin/javac JAVA="${TESTJAVA}"/bin/java -"${JAVAC}" -d . \ +"${JAVAC}" ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d .\ "${TESTSRC}"/TestClass1.java \ "${TESTSRC}"/TestClass2.java \ "${TESTSRC}"/TestClass3.java -"${JAR}" cvf Test.jar Test*.class +"${JAR}" ${TESTTOOLVMOPTS} cvf Test.jar Test*.class # Removing the test class files is important. If these # .class files are available on the classpath other # than via Test.jar, then the deadlock will not reproduce. diff --git a/jdk/test/java/lang/instrument/PremainClass/NoPremainAgent.sh b/jdk/test/java/lang/instrument/PremainClass/NoPremainAgent.sh index fb2e5ccdcdc..3f8c745ddee 100644 --- a/jdk/test/java/lang/instrument/PremainClass/NoPremainAgent.sh +++ b/jdk/test/java/lang/instrument/PremainClass/NoPremainAgent.sh @@ -37,6 +37,12 @@ then exit 1 fi +if [ "${COMPILEJAVA}" = "" ] +then + COMPILEJAVA="${TESTJAVA}" +fi +echo "COMPILEJAVA=${COMPILEJAVA}" + if [ "${TESTSRC}" = "" ] then echo "TESTSRC not set. Test cannot execute. Failed." @@ -49,7 +55,7 @@ then exit 1 fi -JAVAC="${TESTJAVA}"/bin/javac +JAVAC="${COMPILEJAVA}"/bin/javac JAVA="${TESTJAVA}"/bin/java "${JAVA}" ${TESTVMOPTS} -javaagent:NoPremainAgent.jar \ diff --git a/jdk/test/java/lang/instrument/PremainClass/PremainClassTest.sh b/jdk/test/java/lang/instrument/PremainClass/PremainClassTest.sh index ba5c5e80fc6..e4cd42b1fc5 100644 --- a/jdk/test/java/lang/instrument/PremainClass/PremainClassTest.sh +++ b/jdk/test/java/lang/instrument/PremainClass/PremainClassTest.sh @@ -32,6 +32,12 @@ then exit 1 fi +if [ "${COMPILEJAVA}" = "" ] +then + COMPILEJAVA="${TESTJAVA}" +fi +echo "COMPILEJAVA=${COMPILEJAVA}" + if [ "${TESTSRC}" = "" ] then echo "TESTSRC not set. Test cannot execute. Failed." @@ -44,10 +50,10 @@ then exit 1 fi -JAVAC="${TESTJAVA}"/bin/javac +JAVAC="${COMPILEJAVA}"/bin/javac JAVA="${TESTJAVA}"/bin/java -"$JAVAC" -d "${TESTCLASSES}" "${TESTSRC}"/DummyMain.java +"$JAVAC" ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d "${TESTCLASSES}" "${TESTSRC}"/DummyMain.java "${JAVA}" ${TESTVMOPTS} -javaagent:"${TESTSRC}"/Agent.jar -classpath "${TESTCLASSES}" DummyMain result=$? diff --git a/jdk/test/java/lang/instrument/PremainClass/ZeroArgPremainAgent.sh b/jdk/test/java/lang/instrument/PremainClass/ZeroArgPremainAgent.sh index 56b9428216d..3fc7e48637b 100644 --- a/jdk/test/java/lang/instrument/PremainClass/ZeroArgPremainAgent.sh +++ b/jdk/test/java/lang/instrument/PremainClass/ZeroArgPremainAgent.sh @@ -37,6 +37,12 @@ then exit 1 fi +if [ "${COMPILEJAVA}" = "" ] +then + COMPILEJAVA="${TESTJAVA}" +fi +echo "COMPILEJAVA=${COMPILEJAVA}" + if [ "${TESTSRC}" = "" ] then echo "TESTSRC not set. Test cannot execute. Failed." @@ -49,7 +55,7 @@ then exit 1 fi -JAVAC="${TESTJAVA}"/bin/javac +JAVAC="${COMPILEJAVA}"/bin/javac JAVA="${TESTJAVA}"/bin/java "${JAVA}" ${TESTVMOPTS} -javaagent:ZeroArgPremainAgent.jar \ diff --git a/jdk/test/java/lang/instrument/RedefineBigClass.sh b/jdk/test/java/lang/instrument/RedefineBigClass.sh index 84196cf19a9..6b71271e7b9 100644 --- a/jdk/test/java/lang/instrument/RedefineBigClass.sh +++ b/jdk/test/java/lang/instrument/RedefineBigClass.sh @@ -37,6 +37,12 @@ then exit 1 fi +if [ "${COMPILEJAVA}" = "" ] +then + COMPILEJAVA="${TESTJAVA}" +fi +echo "COMPILEJAVA=${COMPILEJAVA}" + if [ "${TESTSRC}" = "" ] then echo "TESTSRC not set. Test cannot execute. Failed." @@ -49,7 +55,7 @@ then exit 1 fi -JAVAC="${TESTJAVA}"/bin/javac +JAVAC="${COMPILEJAVA}"/bin/javac JAVA="${TESTJAVA}"/bin/java "${JAVA}" ${TESTVMOPTS} \ diff --git a/jdk/test/java/lang/instrument/RedefineClassWithNativeMethod.sh b/jdk/test/java/lang/instrument/RedefineClassWithNativeMethod.sh index e049410319b..ba6f438cb59 100644 --- a/jdk/test/java/lang/instrument/RedefineClassWithNativeMethod.sh +++ b/jdk/test/java/lang/instrument/RedefineClassWithNativeMethod.sh @@ -37,6 +37,12 @@ then exit 1 fi +if [ "${COMPILEJAVA}" = "" ] +then + COMPILEJAVA="${TESTJAVA}" +fi +echo "COMPILEJAVA=${COMPILEJAVA}" + if [ "${TESTSRC}" = "" ] then echo "TESTSRC not set. Test cannot execute. Failed." @@ -49,7 +55,7 @@ then exit 1 fi -JAVAC="${TESTJAVA}"/bin/javac +JAVAC="${COMPILEJAVA}"/bin/javac JAVA="${TESTJAVA}"/bin/java "${JAVA}" ${TESTVMOPTS} \ diff --git a/jdk/test/java/lang/instrument/RedefineMethodAddInvoke.sh b/jdk/test/java/lang/instrument/RedefineMethodAddInvoke.sh index dfac6290f07..ed95bbfc2e5 100644 --- a/jdk/test/java/lang/instrument/RedefineMethodAddInvoke.sh +++ b/jdk/test/java/lang/instrument/RedefineMethodAddInvoke.sh @@ -37,6 +37,12 @@ then exit 1 fi +if [ "${COMPILEJAVA}" = "" ] +then + COMPILEJAVA="${TESTJAVA}" +fi +echo "COMPILEJAVA=${COMPILEJAVA}" + if [ "${TESTSRC}" = "" ] then echo "TESTSRC not set. Test cannot execute. Failed." @@ -49,18 +55,18 @@ then exit 1 fi -JAVAC="${TESTJAVA}"/bin/javac +JAVAC="${COMPILEJAVA}"/bin/javac JAVA="${TESTJAVA}"/bin/java cp "${TESTSRC}"/RedefineMethodAddInvokeTarget_1.java \ RedefineMethodAddInvokeTarget.java -"${JAVAC}" -d . RedefineMethodAddInvokeTarget.java +"${JAVAC}" ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . RedefineMethodAddInvokeTarget.java mv RedefineMethodAddInvokeTarget.java RedefineMethodAddInvokeTarget_1.java mv RedefineMethodAddInvokeTarget.class RedefineMethodAddInvokeTarget_1.class cp "${TESTSRC}"/RedefineMethodAddInvokeTarget_2.java \ RedefineMethodAddInvokeTarget.java -"${JAVAC}" -d . RedefineMethodAddInvokeTarget.java +"${JAVAC}" ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . RedefineMethodAddInvokeTarget.java mv RedefineMethodAddInvokeTarget.java RedefineMethodAddInvokeTarget_2.java mv RedefineMethodAddInvokeTarget.class RedefineMethodAddInvokeTarget_2.class diff --git a/jdk/test/java/lang/instrument/RedefineSetUp.sh b/jdk/test/java/lang/instrument/RedefineSetUp.sh index 26b1a2f5354..9f393b46198 100644 --- a/jdk/test/java/lang/instrument/RedefineSetUp.sh +++ b/jdk/test/java/lang/instrument/RedefineSetUp.sh @@ -41,6 +41,12 @@ then fi echo "TESTJAVA=${TESTJAVA}" +if [ "${COMPILEJAVA}" = "" ] +then + COMPILEJAVA="${TESTJAVA}" +fi +echo "COMPILEJAVA=${COMPILEJAVA}" + if [ "${TESTCLASSES}" = "" ] then echo "TESTCLASSES not set. Test cannot execute. Failed." @@ -50,15 +56,15 @@ fi echo "TESTCLASSES=${TESTCLASSES}" echo "CLASSPATH=${CLASSPATH}" -JAVAC="${TESTJAVA}/bin/javac -g" +JAVAC="${COMPILEJAVA}/bin/javac -g" cp ${TESTSRC}/Different_ExampleRedefine.java ExampleRedefine.java cp ${TESTSRC}/Counter.java . -${JAVAC} ExampleRedefine.java +${JAVAC} ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} ExampleRedefine.java mv ExampleRedefine.class Different_ExampleRedefine.class rm -f ExampleRedefine.java Counter.java cp ${TESTSRC}/ExampleRedefine.java ExampleRedefine.java cp ${TESTSRC}/Counter.java . -${JAVAC} ExampleRedefine.java +${JAVAC} ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} ExampleRedefine.java rm -f ExampleRedefine.java Counter.java diff --git a/jdk/test/java/lang/instrument/RetransformBigClass.sh b/jdk/test/java/lang/instrument/RetransformBigClass.sh index 6a06a78d1c9..582eca8e05a 100644 --- a/jdk/test/java/lang/instrument/RetransformBigClass.sh +++ b/jdk/test/java/lang/instrument/RetransformBigClass.sh @@ -38,6 +38,12 @@ then exit 1 fi +if [ "${COMPILEJAVA}" = "" ] +then + COMPILEJAVA="${TESTJAVA}" +fi +echo "COMPILEJAVA=${COMPILEJAVA}" + if [ "${TESTSRC}" = "" ] then echo "TESTSRC not set. Test cannot execute. Failed." @@ -50,7 +56,7 @@ then exit 1 fi -JAVAC="${TESTJAVA}"/bin/javac +JAVAC="${COMPILEJAVA}"/bin/javac JAVA="${TESTJAVA}"/bin/java "${JAVA}" ${TESTVMOPTS} \ diff --git a/jdk/test/java/lang/instrument/appendToClassLoaderSearch/CircularityErrorTest.sh b/jdk/test/java/lang/instrument/appendToClassLoaderSearch/CircularityErrorTest.sh index 45e30314fb5..8ce8238a2f3 100644 --- a/jdk/test/java/lang/instrument/appendToClassLoaderSearch/CircularityErrorTest.sh +++ b/jdk/test/java/lang/instrument/appendToClassLoaderSearch/CircularityErrorTest.sh @@ -34,6 +34,11 @@ then exit 1 fi +if [ "${COMPILEJAVA}" = "" ] +then + COMPILEJAVA="${TESTJAVA}" +fi + . ${TESTSRC}/CommonSetup.sh # Setup to create circularity condition @@ -44,9 +49,9 @@ rm -f "${TESTCLASSES}"/A.java "${TESTCLASSES}"/B.java cp "${TESTSRC}"/A.1 "${TESTCLASSES}"/A.java cp "${TESTSRC}"/B.1 "${TESTCLASSES}"/B.java (cd "${TESTCLASSES}"; \ - $JAVAC A.java B.java; \ - $JAVAC -d . "${TESTSRC}"/CircularityErrorTest.java; \ - $JAR cf A.jar A.class; \ + $JAVAC ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} A.java B.java; \ + $JAVAC ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . "${TESTSRC}"/CircularityErrorTest.java; \ + $JAR ${TESTTOOLVMOPTS} cf A.jar A.class; \ rm -f A.class; mv B.class B.keep) # A extends B @@ -55,7 +60,7 @@ rm -f "${TESTCLASSES}"/A.java "${TESTCLASSES}"/B.java cp "${TESTSRC}"/A.2 "${TESTCLASSES}"/A.java cp "${TESTSRC}"/B.2 "${TESTCLASSES}"/B.java (cd "${TESTCLASSES}"; \ - $JAVAC A.java B.java; rm -f B.class A.java B.java) + $JAVAC ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} A.java B.java; rm -f B.class A.java B.java) # Move B.keep to B.class creates the A extends B and # B extends A condition. @@ -67,7 +72,7 @@ rm -f "${MANIFEST}" echo "Premain-Class: CircularityErrorTest" > "${MANIFEST}" # Setup test case as an agent -$JAR -cfm "${TESTCLASSES}"/CircularityErrorTest.jar "${MANIFEST}" \ +$JAR ${TESTTOOLVMOPTS} -cfm "${TESTCLASSES}"/CircularityErrorTest.jar "${MANIFEST}" \ -C "${TESTCLASSES}" CircularityErrorTest.class # Finally we run the test diff --git a/jdk/test/java/lang/instrument/appendToClassLoaderSearch/ClassUnloadTest.sh b/jdk/test/java/lang/instrument/appendToClassLoaderSearch/ClassUnloadTest.sh index 36ea0d402f7..52744c0a519 100644 --- a/jdk/test/java/lang/instrument/appendToClassLoaderSearch/ClassUnloadTest.sh +++ b/jdk/test/java/lang/instrument/appendToClassLoaderSearch/ClassUnloadTest.sh @@ -65,7 +65,8 @@ EOF echo "public class Bar { }" > "${BAR}" (cd "${OTHERDIR}"; \ - $JAVAC Foo.java Bar.java; $JAR cf "${OTHERDIR}"/Bar.jar Bar.class; \ + $JAVAC ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} Foo.java Bar.java; \ + $JAR ${TESTTOOLVMOPTS} cf "${OTHERDIR}"/Bar.jar Bar.class; \ rm -f Bar.class) # Create the manifest @@ -74,7 +75,7 @@ rm -f "${MANIFEST}" echo "Premain-Class: ClassUnloadTest" > "${MANIFEST}" # Setup test case as an agent -$JAR -cfm "${TESTCLASSES}"/ClassUnloadTest.jar "${MANIFEST}" \ +$JAR ${TESTTOOLVMOPTS} -cfm "${TESTCLASSES}"/ClassUnloadTest.jar "${MANIFEST}" \ -C "${TESTCLASSES}" ClassUnloadTest.class # Finally we run the test diff --git a/jdk/test/java/lang/instrument/appendToClassLoaderSearch/CommonSetup.sh b/jdk/test/java/lang/instrument/appendToClassLoaderSearch/CommonSetup.sh index f5f034be5a2..3455959c3aa 100644 --- a/jdk/test/java/lang/instrument/appendToClassLoaderSearch/CommonSetup.sh +++ b/jdk/test/java/lang/instrument/appendToClassLoaderSearch/CommonSetup.sh @@ -70,6 +70,12 @@ then exit 1 fi +if [ "${COMPILEJAVA}" = "" ] +then + COMPILEJAVA="${TESTJAVA}" +fi +echo "COMPILEJAVA=${COMPILEJAVA}" + if [ "${TESTSRC}" = "" ] then echo "TESTSRC not set. Test cannot execute. Failed." @@ -83,6 +89,6 @@ then fi JAVA="${TESTJAVA}/bin/java" -JAVAC="${TESTJAVA}/bin/javac" -JAR="${TESTJAVA}/bin/jar" +JAVAC="${COMPILEJAVA}/bin/javac" +JAR="${COMPILEJAVA}/bin/jar" diff --git a/jdk/test/java/lang/instrument/appendToClassLoaderSearch/run_tests.sh b/jdk/test/java/lang/instrument/appendToClassLoaderSearch/run_tests.sh index 43378d39af7..be712202671 100644 --- a/jdk/test/java/lang/instrument/appendToClassLoaderSearch/run_tests.sh +++ b/jdk/test/java/lang/instrument/appendToClassLoaderSearch/run_tests.sh @@ -47,10 +47,10 @@ echo "Creating jar files for simple tests..." cd ${TESTCLASSES} -"$JAR" -cfm Agent.jar "${TESTSRC}"/manifest.mf Agent.class -"$JAR" -cf AgentSupport.jar AgentSupport.class -"$JAR" -cf BootSupport.jar BootSupport.class -"$JAR" -cf SimpleTests.jar BasicTest.class PrematureLoadTest.class +"$JAR" ${TESTTOOLVMOPTS} -cfm Agent.jar "${TESTSRC}"/manifest.mf Agent.class +"$JAR" ${TESTTOOLVMOPTS} -cf AgentSupport.jar AgentSupport.class +"$JAR" ${TESTTOOLVMOPTS} -cf BootSupport.jar BootSupport.class +"$JAR" ${TESTTOOLVMOPTS} -cf SimpleTests.jar BasicTest.class PrematureLoadTest.class failures=0 @@ -72,18 +72,18 @@ echo "Setup for functional tests..." # system class path mkdir tmp -"${JAVAC}" -d tmp "${TESTSRC}"/Tracer.java -(cd tmp; "${JAR}" cf ../Tracer.jar org/tools/Tracer.class) +"${JAVAC}" ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d tmp "${TESTSRC}"/Tracer.java +(cd tmp; "${JAR}" ${TESTTOOLVMOPTS} cf ../Tracer.jar org/tools/Tracer.class) # InstrumentedApplication is Application+instrmentation - don't copy as # we don't want the original file permission cat "${TESTSRC}"/InstrumentedApplication.java > ./Application.java -"${JAVAC}" -classpath Tracer.jar -d . Application.java +"${JAVAC}" ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -classpath Tracer.jar -d . Application.java mv Application.class InstrumentedApplication.bytes cp "${TESTSRC}"/Application.java . -"${JAVAC}" -d . Application.java +"${JAVAC}" ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . Application.java sh -xc "$JAVA ${TESTVMOPTS} -classpath . -javaagent:Agent.jar DynamicTest" 2>&1 if [ $? != 0 ]; then failures=`expr $failures + 1`; fi diff --git a/jdk/test/java/net/Authenticator/B4933582.sh b/jdk/test/java/net/Authenticator/B4933582.sh index 1dfed10cb2a..fa1768398ae 100644 --- a/jdk/test/java/net/Authenticator/B4933582.sh +++ b/jdk/test/java/net/Authenticator/B4933582.sh @@ -43,7 +43,8 @@ case "$OS" in exit 1; ;; esac -${TESTJAVA}${FS}bin${FS}javac -d . -classpath "${TESTSRC}${FS}..${FS}..${FS}..${FS}sun${FS}net${FS}www${FS}httptest" ${TESTSRC}${FS}B4933582.java +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . \ + -classpath "${TESTSRC}${FS}..${FS}..${FS}..${FS}sun${FS}net${FS}www${FS}httptest" ${TESTSRC}${FS}B4933582.java rm -f cache.ser auth.save ${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} -classpath "${TESTSRC}${FS}..${FS}..${FS}..${FS}sun${FS}net${FS}www${FS}httptest${PS}." B4933582 first ${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} -classpath "${TESTSRC}${FS}..${FS}..${FS}..${FS}sun${FS}net${FS}www${FS}httptest${PS}." B4933582 second diff --git a/jdk/test/java/net/URL/B5086147.sh b/jdk/test/java/net/URL/B5086147.sh index e5830b9ecce..a669e1347ee 100644 --- a/jdk/test/java/net/URL/B5086147.sh +++ b/jdk/test/java/net/URL/B5086147.sh @@ -42,7 +42,7 @@ case "$OS" in exit 1; ;; esac -${TESTJAVA}${FS}bin${FS}javac -d . ${TESTSRC}${FS}B5086147.java +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . ${TESTSRC}${FS}B5086147.java failures=0 diff --git a/jdk/test/java/net/URL/runconstructor.sh b/jdk/test/java/net/URL/runconstructor.sh index 5a8f09073d9..f64fd8509d1 100644 --- a/jdk/test/java/net/URL/runconstructor.sh +++ b/jdk/test/java/net/URL/runconstructor.sh @@ -44,7 +44,8 @@ case "$OS" in exit 1; ;; esac -${TESTJAVA}${FS}bin${FS}javac -d . ${TESTSRC}${FS}Constructor.java +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . \ + ${TESTSRC}${FS}Constructor.java failures=0 diff --git a/jdk/test/java/net/URLClassLoader/B5077773.sh b/jdk/test/java/net/URLClassLoader/B5077773.sh index 725829e58e5..9bd9a1e6f34 100644 --- a/jdk/test/java/net/URLClassLoader/B5077773.sh +++ b/jdk/test/java/net/URLClassLoader/B5077773.sh @@ -58,7 +58,8 @@ esac cp ${TESTSRC}${FS}foo.jar . -${TESTJAVA}${FS}bin${FS}javac -d . ${TESTSRC}${FS}B5077773.java +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . \ + ${TESTSRC}${FS}B5077773.java WD=`pwd` ${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} B5077773 diff --git a/jdk/test/java/net/URLClassLoader/closetest/build.sh b/jdk/test/java/net/URLClassLoader/closetest/build.sh index 0adb2e9efb1..651c8a45d6e 100644 --- a/jdk/test/java/net/URLClassLoader/closetest/build.sh +++ b/jdk/test/java/net/URLClassLoader/closetest/build.sh @@ -40,14 +40,19 @@ then fi echo "TESTJAVA=${TESTJAVA}" +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi +echo "COMPILEJAVA=${COMPILEJAVA}" + if [ "${TESTCLASSES}" = "" ] then echo "TESTCLASSES not set. Test cannot execute. Failed." exit 1 fi -JAVAC="${TESTJAVA}/bin/javac" -JAR="${TESTJAVA}/bin/jar" +JAVAC="${COMPILEJAVA}/bin/javac" +JAR="${COMPILEJAVA}/bin/jar" rm -rf ${TESTCLASSES}/test1 rm -rf ${TESTCLASSES}/test2 @@ -59,15 +64,15 @@ mkdir -p ${TESTCLASSES}/serverRoot cd ${TESTSRC}/test1/com/foo cp * ${TESTCLASSES}/test1/com/foo cd ${TESTCLASSES}/test1 -${JAVAC} com/foo/*.java -${JAR} cvf ../test1.jar com/foo/*.class com/foo/Resource* +${JAVAC} ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} com/foo/*.java +${JAR} ${TESTTOOLVMOPTS} cvf ../test1.jar com/foo/*.class com/foo/Resource* cd ${TESTSRC}/test2/com/foo cp * ${TESTCLASSES}/test2/com/foo cd ${TESTCLASSES}/test2 -${JAVAC} com/foo/*.java -${JAR} cvf ../test2.jar com/foo/*.class com/foo/Resource* +${JAVAC} ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} com/foo/*.java +${JAR} ${TESTTOOLVMOPTS} cvf ../test2.jar com/foo/*.class com/foo/Resource* cp ${TESTSRC}/serverRoot/Test.java ${TESTCLASSES}/serverRoot cd ${TESTCLASSES}/serverRoot -${JAVAC} Test.java +${JAVAC} ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} Test.java diff --git a/jdk/test/java/net/URLClassLoader/getresourceasstream/test.sh b/jdk/test/java/net/URLClassLoader/getresourceasstream/test.sh index 1654f314f88..976b8e8d00c 100644 --- a/jdk/test/java/net/URLClassLoader/getresourceasstream/test.sh +++ b/jdk/test/java/net/URLClassLoader/getresourceasstream/test.sh @@ -43,7 +43,7 @@ checkExit () { fi } -${TESTJAVA}/bin/javac -d . ${TESTSRC}/Test.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . ${TESTSRC}/Test.java cp ${TESTSRC}/test.jar . ${TESTJAVA}/bin/java ${TESTVMOPTS} Test diff --git a/jdk/test/java/net/URLClassLoader/sealing/checksealed.sh b/jdk/test/java/net/URLClassLoader/sealing/checksealed.sh index b675eb2f1a2..0d212c62aae 100644 --- a/jdk/test/java/net/URLClassLoader/sealing/checksealed.sh +++ b/jdk/test/java/net/URLClassLoader/sealing/checksealed.sh @@ -51,11 +51,13 @@ esac if [ x"$TESTJAVA" = x ]; then TESTJAVA=$1; fi +if [ x"$COMPILEJAVA" = x ]; then COMPILEJAVA=$1; fi if [ x"$TESTSRC" = x ]; then TESTSRC=.; fi CLASSPATH=".${PS}${TESTSRC}${FS}a${PS}${TESTSRC}${FS}b.jar" -${TESTJAVA}${FS}bin${FS}javac -classpath "${CLASSPATH}" -d . ${TESTSRC}${FS}CheckSealed.java +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -classpath "${CLASSPATH}" -d . \ + ${TESTSRC}${FS}CheckSealed.java ${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} -cp "${CLASSPATH}" CheckSealed 1 if [ $? != 0 ]; then exit 1; fi ${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} -cp "${CLASSPATH}" CheckSealed 2 diff --git a/jdk/test/java/net/URLConnection/6212146/test.sh b/jdk/test/java/net/URLConnection/6212146/test.sh index 22c6473c77e..45f5310005e 100644 --- a/jdk/test/java/net/URLConnection/6212146/test.sh +++ b/jdk/test/java/net/URLConnection/6212146/test.sh @@ -63,7 +63,7 @@ mkdir jars cp ${TESTSRC}${FS}test.jar jars -${TESTJAVA}${FS}bin${FS}javac -d . ${TESTSRC}${FS}Test.java +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . ${TESTSRC}${FS}Test.java WD=`pwd` ulimit -H -n 300 diff --git a/jdk/test/java/net/URLConnection/UNCTest.sh b/jdk/test/java/net/URLConnection/UNCTest.sh index aa9f1842613..8030deb30e3 100644 --- a/jdk/test/java/net/URLConnection/UNCTest.sh +++ b/jdk/test/java/net/URLConnection/UNCTest.sh @@ -35,7 +35,7 @@ UNC="file://jdk/LOCAL-JAVA/jdk1.4/win/README.txt" OS=`uname -s` case "$OS" in Windows_95 | Windows_98 | Windows_NT ) - ${TESTJAVA}/bin/javac -d . ${TESTSRC}\\UNCTest.java + ${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . ${TESTSRC}\\UNCTest.java ${TESTJAVA}/bin/java ${TESTVMOPTS} UNCTest ${UNC} exit ;; diff --git a/jdk/test/java/nio/charset/spi/basic.sh b/jdk/test/java/nio/charset/spi/basic.sh index e4f89c5823c..6b5ad3108ac 100644 --- a/jdk/test/java/nio/charset/spi/basic.sh +++ b/jdk/test/java/nio/charset/spi/basic.sh @@ -38,12 +38,13 @@ if [ -z "$TESTJAVA" ]; then if [ $# -lt 1 ]; then exit 1; fi TESTJAVA=$1; shift + COMPILEJDK="${TESTJAVA}" TESTSRC=`pwd` TESTCLASSES=`pwd` fi JAVA=$TESTJAVA/bin/java -JAR=$TESTJAVA/bin/jar +JAR=$COMPILEJAVA/bin/jar DIR=`pwd` case `uname` in @@ -72,7 +73,7 @@ if [ \! -d $EXTD ]; then cp $TESTCLASSES/FooProvider.class $TESTCLASSES/FooCharset.class $JARD mkdir $TESTD cp $TESTCLASSES/Test.class $TESTD - (cd $JARD; $JAR -cf $EXTD/test.jar *) + (cd $JARD; $JAR ${TESTTOOLVMOPTS} -cf $EXTD/test.jar *) fi if [ $# -gt 0 ]; then diff --git a/jdk/test/java/rmi/activation/Activatable/extLoadedImpl/ext.sh b/jdk/test/java/rmi/activation/Activatable/extLoadedImpl/ext.sh index 4f758b410c4..9c7a2eac4ec 100644 --- a/jdk/test/java/rmi/activation/Activatable/extLoadedImpl/ext.sh +++ b/jdk/test/java/rmi/activation/Activatable/extLoadedImpl/ext.sh @@ -49,7 +49,7 @@ mkdir -p classes for dir in `echo ${TESTCLASSPATH:-$TESTCLASSES} | sed -e "s/$PS/ /"` ; do cp $dir/*.class classes ; done rm classes/ExtLoadedImpl.class classes/ExtLoadedImpl_Stub.class classes/CheckLoader.class mkdir -p ext -$TESTJAVA/bin/jar cf ext/ext.jar -C $TESTCLASSES ExtLoadedImpl.class -C $TESTCLASSES ExtLoadedImpl_Stub.class -C $TESTCLASSES CheckLoader.class +$COMPILEJAVA/bin/jar ${TESTTOOLVMOPTS} cf ext/ext.jar -C $TESTCLASSES ExtLoadedImpl.class -C $TESTCLASSES ExtLoadedImpl_Stub.class -C $TESTCLASSES CheckLoader.class $TESTJAVA/bin/java ${TESTVMOPTS} -cp classes -Dtest.src=$TESTSRC -Dtest.classes=$TESTCLASSES -Djava.security.policy=$TESTSRC/security.policy -Djava.ext.dirs=ext ExtLoadedImplTest diff --git a/jdk/test/java/rmi/registry/readTest/readTest.sh b/jdk/test/java/rmi/registry/readTest/readTest.sh index 5fda0a5a314..dad6847f304 100644 --- a/jdk/test/java/rmi/registry/readTest/readTest.sh +++ b/jdk/test/java/rmi/registry/readTest/readTest.sh @@ -53,15 +53,15 @@ esac TEST_CLASSPATH=.$PS${TESTCLASSPATH:-$TESTCLASSES} cp -r ${TESTSRC}${FS}* . -${TESTJAVA}${FS}bin${FS}javac testPkg${FS}*java -${TESTJAVA}${FS}bin${FS}javac -cp $TEST_CLASSPATH readTest.java +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} testPkg${FS}*java +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -cp $TEST_CLASSPATH readTest.java mkdir rmi_tmp RMIREG_OUT=rmi.out #start rmiregistry without any local classes on classpath cd rmi_tmp # NOTE: This RMI Registry port must match TestLibrary.READTEST_REGISTRY_PORT -${TESTJAVA}${FS}bin${FS}rmiregistry 64005 > ..${FS}${RMIREG_OUT} 2>&1 & +${TESTJAVA}${FS}bin${FS}rmiregistry ${TESTTOOLVMOPTS} 64005 > ..${FS}${RMIREG_OUT} 2>&1 & RMIREG_PID=$! # allow some time to start sleep 3 diff --git a/jdk/test/java/security/Security/ClassLoaderDeadlock/ClassLoaderDeadlock.sh b/jdk/test/java/security/Security/ClassLoaderDeadlock/ClassLoaderDeadlock.sh index a62cba4ec3a..7f5dc78c23a 100644 --- a/jdk/test/java/security/Security/ClassLoaderDeadlock/ClassLoaderDeadlock.sh +++ b/jdk/test/java/security/Security/ClassLoaderDeadlock/ClassLoaderDeadlock.sh @@ -43,6 +43,10 @@ if [ "${TESTJAVA}" = "" ] ; then exit 1 fi +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi + # set platform-dependent variables OS=`uname -s` case "$OS" in @@ -78,11 +82,11 @@ if [ ! -d provider ] ; then fi # compile the test program -${TESTJAVA}${FILESEP}bin${FILESEP}javac \ +${COMPILEJAVA}${FILESEP}bin${FILESEP}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ -d ${TESTCLASSES}${FILESEP} \ ${TESTSRC}${FILESEP}ClassLoaderDeadlock.java -${TESTJAVA}${FILESEP}bin${FILESEP}javac \ +${COMPILEJAVA}${FILESEP}bin${FILESEP}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ -d ${TESTCLASSES}${FILESEP}provider${FILESEP} \ ${TESTSRC}${FILESEP}provider${FILESEP}HashProvider.java diff --git a/jdk/test/java/security/Security/ClassLoaderDeadlock/Deadlock2.sh b/jdk/test/java/security/Security/ClassLoaderDeadlock/Deadlock2.sh index acf8ff21033..e817db37898 100644 --- a/jdk/test/java/security/Security/ClassLoaderDeadlock/Deadlock2.sh +++ b/jdk/test/java/security/Security/ClassLoaderDeadlock/Deadlock2.sh @@ -47,6 +47,10 @@ if [ "${TESTJAVA}" = "" ] ; then exit 1 fi +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi + # set platform-dependent variables OS=`uname -s` case "$OS" in @@ -88,12 +92,12 @@ else fi # compile and package the test program -${TESTJAVA}${FILESEP}bin${FILESEP}javac \ +${COMPILEJAVA}${FILESEP}bin${FILESEP}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ -d ${TESTCLASSES} \ ${TESTSRC}${FILESEP}CreateSerialized.java \ ${TESTSRC}${FILESEP}Deadlock2.java -${TESTJAVA}${FILESEP}bin${FILESEP}jar \ +${COMPILEJAVA}${FILESEP}bin${FILESEP}jar ${TESTTOOLVMOPTS} \ -cvf testlib${FILESEP}Deadlock2.jar \ Deadlock2*.class diff --git a/jdk/test/java/security/Security/signedfirst/Dyn.sh b/jdk/test/java/security/Security/signedfirst/Dyn.sh index 64d0dd3f775..73cff3e80da 100644 --- a/jdk/test/java/security/Security/signedfirst/Dyn.sh +++ b/jdk/test/java/security/Security/signedfirst/Dyn.sh @@ -43,6 +43,10 @@ if [ "${TESTJAVA}" = "" ] ; then exit 1 fi +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi + # set platform-dependent variables OS=`uname -s` case "$OS" in @@ -77,7 +81,7 @@ cd ${TESTCLASSES}${FILESEP} rm DynSignedProvFirst.class # compile the test program -${TESTJAVA}${FILESEP}bin${FILESEP}javac \ +${COMPILEJAVA}${FILESEP}bin${FILESEP}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ -classpath ${TESTSRC}${FILESEP}exp.jar \ -d ${TESTCLASSES}${FILESEP} \ ${TESTSRC}${FILESEP}DynSignedProvFirst.java diff --git a/jdk/test/java/security/Security/signedfirst/Static.sh b/jdk/test/java/security/Security/signedfirst/Static.sh index 544a3b1a3ff..46765e4a92b 100644 --- a/jdk/test/java/security/Security/signedfirst/Static.sh +++ b/jdk/test/java/security/Security/signedfirst/Static.sh @@ -43,6 +43,10 @@ if [ "${TESTJAVA}" = "" ] ; then exit 1 fi +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi + # set platform-dependent variables OS=`uname -s` case "$OS" in @@ -77,7 +81,7 @@ cd ${TESTCLASSES}${FILESEP} rm StaticSignedProvFirst.class # compile the test program -${TESTJAVA}${FILESEP}bin${FILESEP}javac \ +${COMPILEJAVA}${FILESEP}bin${FILESEP}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ -classpath "${TESTCLASSES}${PATHSEP}${TESTSRC}${FILESEP}exp.jar" \ -d ${TESTCLASSES}${FILESEP} \ ${TESTSRC}${FILESEP}StaticSignedProvFirst.java diff --git a/jdk/test/java/security/cert/CertificateFactory/slowstream.sh b/jdk/test/java/security/cert/CertificateFactory/slowstream.sh index 72d3fcf6c8c..40526970602 100644 --- a/jdk/test/java/security/cert/CertificateFactory/slowstream.sh +++ b/jdk/test/java/security/cert/CertificateFactory/slowstream.sh @@ -33,6 +33,9 @@ if [ "${TESTJAVA}" = "" ] ; then echo "FAILED!!!" exit 1 fi +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi # set platform-dependent variables OS=`uname -s` @@ -45,6 +48,7 @@ case "$OS" in ;; esac -${TESTJAVA}${FS}bin${FS}javac -d . ${TESTSRC}${FS}SlowStream.java +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . \ + ${TESTSRC}${FS}SlowStream.java ${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} -Dtest.src=${TESTSRC} SlowStreamWriter | \ ${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} SlowStreamReader diff --git a/jdk/test/java/util/Formatter/Basic.sh b/jdk/test/java/util/Formatter/Basic.sh index cfa72489c98..0296d9454c8 100644 --- a/jdk/test/java/util/Formatter/Basic.sh +++ b/jdk/test/java/util/Formatter/Basic.sh @@ -23,7 +23,8 @@ # -${TESTJAVA}/bin/javac -cp ${TESTSRC} -d . ${TESTSRC}/Basic.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -cp ${TESTSRC} -d . \ + ${TESTSRC}/Basic.java expectPass() { if [ $1 -eq 0 ] @@ -38,7 +39,7 @@ runTest() { echo "Testing:" ${1} TZ="${1}"; export TZ echo " " $TZ - ${TESTJAVA}/bin/java Basic + ${TESTJAVA}/bin/java ${TESTVMOPTS} Basic expectPass $? } diff --git a/jdk/test/java/util/Locale/LocaleProviders.sh b/jdk/test/java/util/Locale/LocaleProviders.sh index 3cf0d4e7c69..6790f871e79 100644 --- a/jdk/test/java/util/Locale/LocaleProviders.sh +++ b/jdk/test/java/util/Locale/LocaleProviders.sh @@ -39,6 +39,10 @@ then echo "TESTJAVA not set. Test cannot execute. Failed." exit 1 fi +if [ "${COMPILEJAVA}" = "" ] +then + COMPILEJAVA="${TESTJAVA}" +fi echo "TESTJAVA=${TESTJAVA}" if [ "${TESTCLASSES}" = "" ] then @@ -92,8 +96,9 @@ EOF mk ${SPIDIR}${FS}dest${FS}META-INF${FS}services${FS}java.util.spi.TimeZoneNameProvider << EOF tznp EOF -${TESTJAVA}${FS}bin${FS}javac -d ${SPIDIR}${FS}dest ${SPIDIR}${FS}src${FS}tznp.java -${TESTJAVA}${FS}bin${FS}jar cvf ${SPIDIR}${FS}tznp.jar -C ${SPIDIR}${FS}dest . +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d ${SPIDIR}${FS}dest \ + ${SPIDIR}${FS}src${FS}tznp.java +${COMPILEJAVA}${FS}bin${FS}jar ${TESTTOOLVMOPTS} cvf ${SPIDIR}${FS}tznp.jar -C ${SPIDIR}${FS}dest . # get the platform default locales PLATDEF=`${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} -classpath ${TESTCLASSES} LocaleProviders getPlatformLocale display` diff --git a/jdk/test/java/util/PluggableLocale/ExecTest.sh b/jdk/test/java/util/PluggableLocale/ExecTest.sh index c01e9e82742..698d027a4af 100644 --- a/jdk/test/java/util/PluggableLocale/ExecTest.sh +++ b/jdk/test/java/util/PluggableLocale/ExecTest.sh @@ -46,6 +46,10 @@ then echo "TESTJAVA not set. Test cannot execute. Failed." exit 1 fi +if [ "${COMPILEJAVA}" = "" ] +then + COMPILEJAVA="${TESTJAVA}" +fi echo "TESTJAVA=${TESTJAVA}" if [ "${TESTCLASSES}" = "" ] then @@ -99,7 +103,8 @@ esac # compile cp ${TESTSRC}${FS}ProviderTest.java . cp ${TESTSRC}${FS}$2.java . -COMPILE="${TESTJAVA}${FS}bin${FS}javac -XDignore.symbol.file -d . -classpath ${CLASSPATHARG} $2.java" +COMPILE="${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ + -XDignore.symbol.file -d . -classpath ${CLASSPATHARG} $2.java" echo ${COMPILE} ${COMPILE} result=$? diff --git a/jdk/test/java/util/ServiceLoader/basic.sh b/jdk/test/java/util/ServiceLoader/basic.sh index 72d68fab7ae..5d8846a02d8 100644 --- a/jdk/test/java/util/ServiceLoader/basic.sh +++ b/jdk/test/java/util/ServiceLoader/basic.sh @@ -33,12 +33,13 @@ if [ -z "$TESTJAVA" ]; then if [ $# -lt 1 ]; then exit 1; fi TESTJAVA="$1"; shift + COMPILEJAVA="${TESTJAVA}" TESTSRC="`pwd`" TESTCLASSES="`pwd`" fi JAVA="$TESTJAVA/bin/java" -JAR="$TESTJAVA/bin/jar" +JAR="$COMPILEJAVA/bin/jar" OS=`uname -s` case "$OS" in @@ -68,7 +69,7 @@ if [ \! -d $EXTD ]; then if [ $n = 3 ]; then cp $TESTCLASSES/FooService.class $JARD fi - (cd $JARD; "$JAR" -cf ../p$n.jar *) + (cd $JARD; "$JAR" ${TESTTOOLVMOPTS} -cf ../p$n.jar *) done mv p3.jar $EXTD diff --git a/jdk/test/java/util/TimeZone/TimeZoneDatePermissionCheck.sh b/jdk/test/java/util/TimeZone/TimeZoneDatePermissionCheck.sh index 4a295ce532f..ea40edfb055 100644 --- a/jdk/test/java/util/TimeZone/TimeZoneDatePermissionCheck.sh +++ b/jdk/test/java/util/TimeZone/TimeZoneDatePermissionCheck.sh @@ -30,10 +30,14 @@ fi if [ "${TESTJAVA}" = "" ] ; then TESTJAVA=/usr fi +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi -# create a test keystore and dummy cert +# create a test keystore and dummy cert. Note that we use the COMPILEJAVA +# as this test is a TimeZone test, it doesn't test keytool rm -f ${TESTCLASSES}/timezonedatetest.store -${TESTJAVA}/bin/keytool -genkeypair -alias testcert \ +${COMPILEJAVA}/bin/keytool ${TESTTOOLVMOPTS} -genkeypair -alias testcert \ -keystore ${TESTCLASSES}/timezonedatetest.store \ -storepass testpass -validity 360 \ -dname "cn=Mark Wildebeest, ou=FreeSoft, o=Red Hat, c=NL" \ @@ -41,12 +45,12 @@ ${TESTJAVA}/bin/keytool -genkeypair -alias testcert \ # create a jar file to sign with the test class in it. rm -f ${TESTCLASSES}/timezonedatetest.jar -${TESTJAVA}/bin/jar cf \ +${COMPILEJAVA}/bin/jar ${TESTTOOLVMOPTS} cf \ ${TESTCLASSES}/timezonedatetest.jar \ -C ${TESTCLASSES} TimeZoneDatePermissionCheck.class # sign it -${TESTJAVA}/bin/jarsigner \ +${COMPILEJAVA}/bin/jarsigner ${TESTTOOLVMOPTS} \ -keystore ${TESTCLASSES}/timezonedatetest.store \ -storepass testpass ${TESTCLASSES}/timezonedatetest.jar testcert diff --git a/jdk/test/java/util/prefs/PrefsSpi.sh b/jdk/test/java/util/prefs/PrefsSpi.sh index f1ce4f834ac..814c20978b9 100644 --- a/jdk/test/java/util/prefs/PrefsSpi.sh +++ b/jdk/test/java/util/prefs/PrefsSpi.sh @@ -39,10 +39,13 @@ if [ -z "$TESTJAVA" ]; then TESTSRC="`pwd`" TESTCLASSES="`pwd`" fi +if [ -z "$COMPILEJAVA" ]; then + COMPILEJAVA="${TESTJAVA}" +fi java="$TESTJAVA/bin/java" -javac="$TESTJAVA/bin/javac" - jar="$TESTJAVA/bin/jar" +javac="$COMPILEJAVA/bin/javac" + jar="$COMPILEJAVA/bin/jar" Die() { printf "%s\n" "$*"; exit 1; } @@ -81,9 +84,9 @@ Sys rm -rf jarDir extDir Sys mkdir -p jarDir/META-INF/services extDir echo "StubPreferencesFactory" \ > "jarDir/META-INF/services/java.util.prefs.PreferencesFactory" -Sys "$javac" -d jarDir StubPreferencesFactory.java StubPreferences.java +Sys "$javac" ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d jarDir StubPreferencesFactory.java StubPreferences.java -(cd jarDir && "$jar" "cf" "../extDir/PrefsSpi.jar" ".") +(cd jarDir && "$jar" ${TESTTOOLVMOPTS} "cf" "../extDir/PrefsSpi.jar" ".") case "`uname`" in Windows*|CYGWIN* ) CPS=';';; *) CPS=':';; esac diff --git a/jdk/test/javax/crypto/SecretKeyFactory/FailOverTest.sh b/jdk/test/javax/crypto/SecretKeyFactory/FailOverTest.sh index ab8bb0ed8c9..c16b60ff877 100644 --- a/jdk/test/javax/crypto/SecretKeyFactory/FailOverTest.sh +++ b/jdk/test/javax/crypto/SecretKeyFactory/FailOverTest.sh @@ -35,6 +35,11 @@ then fi echo "TESTJAVA=${TESTJAVA}" +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi +echo "COMPILEJAVA=${COMPILEJAVA}" + if [ "${TESTSRC}" = "" ] then TESTSRC="." @@ -72,7 +77,7 @@ case "$OS" in ;; esac -${TESTJAVA}${FS}bin${FS}javac \ +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ -d . \ -classpath "${TESTSRC}${FS}P1.jar${PS}${TESTSRC}${FS}P2.jar" \ ${TESTSRC}${FS}FailOverTest.java diff --git a/jdk/test/javax/script/CommonSetup.sh b/jdk/test/javax/script/CommonSetup.sh index 37dff6380c3..69ed2379eda 100644 --- a/jdk/test/javax/script/CommonSetup.sh +++ b/jdk/test/javax/script/CommonSetup.sh @@ -63,6 +63,11 @@ then exit 1 fi +if [ "${COMPILEJAVA}" = "" ] +then + COMPILEJAVA="${TESTJAVA}" +fi + if [ "${TESTSRC}" = "" ] then echo "TESTSRC not set. Test cannot execute. Failed." @@ -76,6 +81,6 @@ then fi JAVA="${TESTJAVA}/bin/java" -JAVAC="${TESTJAVA}/bin/javac" -JAR="${TESTJAVA}/bin/jar" +JAVAC="${COMPILEJAVA}/bin/javac" +JAR="${COMPILEJAVA}/bin/jar" diff --git a/jdk/test/javax/script/ProviderTest.sh b/jdk/test/javax/script/ProviderTest.sh index c39a9333914..e14ea48321f 100644 --- a/jdk/test/javax/script/ProviderTest.sh +++ b/jdk/test/javax/script/ProviderTest.sh @@ -38,7 +38,7 @@ fi echo "Creating JAR file ..." -$JAR -cf ${TESTCLASSES}/dummy.jar \ +$JAR ${TESTTOOLVMOPTS} -cf ${TESTCLASSES}/dummy.jar \ -C ${TESTCLASSES} DummyScriptEngine.class \ -C ${TESTCLASSES} DummyScriptEngineFactory.class \ -C "${TESTSRC}" META-INF/services/javax.script.ScriptEngineFactory diff --git a/jdk/test/javax/security/auth/Subject/doAs/Test.sh b/jdk/test/javax/security/auth/Subject/doAs/Test.sh index 65ef4531e26..73565211859 100644 --- a/jdk/test/javax/security/auth/Subject/doAs/Test.sh +++ b/jdk/test/javax/security/auth/Subject/doAs/Test.sh @@ -66,7 +66,8 @@ esac # remove any leftover built class cd ${TESTCLASSES}${FS} ${RM} Test.class -${TESTJAVA}${FS}bin${FS}javac -d ${TESTCLASSES}${FS} ${TESTSRC}${FS}Test.java +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d ${TESTCLASSES}${FS} \ + ${TESTSRC}${FS}Test.java WD=`pwd` cd ${TESTSRC}${FS} cd $WD diff --git a/jdk/test/lib/security/java.policy/Ext_AllPolicy.sh b/jdk/test/lib/security/java.policy/Ext_AllPolicy.sh index 3efcce9b4e0..b562afa4323 100644 --- a/jdk/test/lib/security/java.policy/Ext_AllPolicy.sh +++ b/jdk/test/lib/security/java.policy/Ext_AllPolicy.sh @@ -46,6 +46,9 @@ if [ "${TESTJAVA}" = "" ] ; then echo "FAILED!!!" exit 1 fi +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi # set platform-dependent variables OS=`uname -s` @@ -74,7 +77,7 @@ esac # the test code cd ${TESTCLASSES} -${TESTJAVA}${FS}bin${FS}jar -cvf Ext_AllPolicy.jar Ext_AllPolicy.class +${COMPILEJAVA}${FS}bin${FS}jar ${TESTTOOLVMOPTS} -cvf Ext_AllPolicy.jar Ext_AllPolicy.class rm Ext_AllPolicy.class ${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} \ diff --git a/jdk/test/sun/management/jmxremote/bootstrap/PasswordFilePermissionTest.sh b/jdk/test/sun/management/jmxremote/bootstrap/PasswordFilePermissionTest.sh index dfc4a85ad5c..fe1fec654a7 100644 --- a/jdk/test/sun/management/jmxremote/bootstrap/PasswordFilePermissionTest.sh +++ b/jdk/test/sun/management/jmxremote/bootstrap/PasswordFilePermissionTest.sh @@ -90,7 +90,7 @@ createPasswordFile ${PASSWD} # Compile test -${TESTJAVA}/bin/javac -d ${TESTCLASSES} ${TESTCLASSES}/Null.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d ${TESTCLASSES} ${TESTCLASSES}/Null.java JAVA=${TESTJAVA}/bin/java diff --git a/jdk/test/sun/management/jmxremote/bootstrap/SSLConfigFilePermissionTest.sh b/jdk/test/sun/management/jmxremote/bootstrap/SSLConfigFilePermissionTest.sh index 0fa765409b6..022aa2a4d26 100644 --- a/jdk/test/sun/management/jmxremote/bootstrap/SSLConfigFilePermissionTest.sh +++ b/jdk/test/sun/management/jmxremote/bootstrap/SSLConfigFilePermissionTest.sh @@ -88,7 +88,7 @@ createSSLConfigFile ${SSL} ${TESTSRC}/ssl/keystore # Compile test -${TESTJAVA}/bin/javac -d ${TESTCLASSES} ${TESTCLASSES}/Dummy.java +${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d ${TESTCLASSES} ${TESTCLASSES}/Dummy.java JAVA=${TESTJAVA}/bin/java CLASSPATH=${TESTCLASSES} diff --git a/jdk/test/sun/management/jmxremote/startstop/JMXStartStopTest.sh b/jdk/test/sun/management/jmxremote/startstop/JMXStartStopTest.sh index 2021381c247..e2e7598a994 100644 --- a/jdk/test/sun/management/jmxremote/startstop/JMXStartStopTest.sh +++ b/jdk/test/sun/management/jmxremote/startstop/JMXStartStopTest.sh @@ -51,7 +51,8 @@ _compile(){ rm -f ${_testclasses}/JMXStartStopTest.class # Compile testcase - ${TESTJAVA}/bin/javac -d ${_testclasses} JMXStartStopDoSomething.java JMXStartStopTest.java + ${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d ${_testclasses} \ + JMXStartStopDoSomething.java JMXStartStopTest.java if [ ! -f ${_testclasses}/JMXStartStopTest.class ] then @@ -82,7 +83,7 @@ _app_start(){ } _get_pid(){ - ${TESTJAVA}/bin/jps | sed -n "/JMXStartStopDoSomething/s/ .*//p" + ${COMPILEJAVA}/bin/jps ${TESTTOOLVMOPTS} | sed -n "/JMXStartStopDoSomething/s/ .*//p" } _app_stop(){ @@ -115,7 +116,7 @@ _testme(){ _jcmd(){ - ${TESTJAVA}/bin/jcmd JMXStartStopDoSomething $* > /dev/null 2>/dev/null + ${TESTJAVA}/bin/jcmd ${TESTTOOLVMOPTS} JMXStartStopDoSomething $* > /dev/null 2>/dev/null } _echo(){ @@ -445,7 +446,7 @@ test_11(){ _jcmd ManagementAgent.stop - pid=`${TESTJAVA}/bin/jps | sed -n "/JMXStartStopDoSomething/s/ .*//p"` + pid=`${COMPILEJAVA}/bin/jps ${TESTTOOLVMOPTS} | sed -n "/JMXStartStopDoSomething/s/ .*//p"` res2=`_testme local ${pid}` if [ "${res1}" = "OK_CONN" -a "${res2}" = "OK_CONN" ] @@ -528,6 +529,7 @@ if [ ! -x "${TESTJAVA}/bin/jcmd" ] then echo "${TESTJAVA}/bin/jcmd" echo "Doesn't exist or not an executable" + exit fi diff --git a/jdk/test/sun/net/www/MarkResetTest.sh b/jdk/test/sun/net/www/MarkResetTest.sh index 542bfe4b179..c998bcffef2 100644 --- a/jdk/test/sun/net/www/MarkResetTest.sh +++ b/jdk/test/sun/net/www/MarkResetTest.sh @@ -46,7 +46,7 @@ case "$OS" in ;; esac -${TESTJAVA}${FS}bin${FS}javac -d . ${TESTSRC}${FS}MarkResetTest.java +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . ${TESTSRC}${FS}MarkResetTest.java # ftp server used by the test requires the file to be present # in this directory diff --git a/jdk/test/sun/net/www/http/HttpClient/RetryPost.sh b/jdk/test/sun/net/www/http/HttpClient/RetryPost.sh index fc563c83b16..17d08cd30b0 100644 --- a/jdk/test/sun/net/www/http/HttpClient/RetryPost.sh +++ b/jdk/test/sun/net/www/http/HttpClient/RetryPost.sh @@ -47,7 +47,7 @@ case "$OS" in esac # compile -${TESTJAVA}${FS}bin${FS}javac -d . ${TESTSRC}${FS}RetryPost.java +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . ${TESTSRC}${FS}RetryPost.java # run with no option specified. Should retry POST request. ${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} RetryPost diff --git a/jdk/test/sun/net/www/protocol/jar/B5105410.sh b/jdk/test/sun/net/www/protocol/jar/B5105410.sh index 1fdbdd58ff8..f47ca5ed99d 100644 --- a/jdk/test/sun/net/www/protocol/jar/B5105410.sh +++ b/jdk/test/sun/net/www/protocol/jar/B5105410.sh @@ -50,6 +50,6 @@ case "$OS" in esac cp ${TESTSRC}${FS}foo2.jar . -${TESTJAVA}${FS}bin${FS}javac -d . ${TESTSRC}${FS}B5105410.java +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . ${TESTSRC}${FS}B5105410.java ${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} B5105410 diff --git a/jdk/test/sun/net/www/protocol/jar/jarbug/run.sh b/jdk/test/sun/net/www/protocol/jar/jarbug/run.sh index 72a11681d1c..5f039b97c93 100644 --- a/jdk/test/sun/net/www/protocol/jar/jarbug/run.sh +++ b/jdk/test/sun/net/www/protocol/jar/jarbug/run.sh @@ -59,17 +59,17 @@ esac mkdir -p ${DEST}${FS}jar1 cd ${TESTSRC}${FS}etc${FS}jar1 cp -r . ${DEST}${FS}jar1 -${TESTJAVA}${FS}bin${FS}javac -d ${DEST}${FS}jar1 \ +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d ${DEST}${FS}jar1 \ ${TESTSRC}${FS}src${FS}jar1${FS}LoadResourceBundle.java -${TESTJAVA}${FS}bin${FS}javac -d ${DEST}${FS}jar1 \ +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d ${DEST}${FS}jar1 \ ${TESTSRC}${FS}src${FS}jar1${FS}GetResource.java cd ${DEST}${FS}jar1 -${TESTJAVA}${FS}bin${FS}jar cfM jar1.jar jar1 res1.txt +${COMPILEJAVA}${FS}bin${FS}jar ${TESTTOOLVMOPTS} cfM jar1.jar jar1 res1.txt mv jar1.jar .. # # build the test sources and run them # -${TESTJAVA}${FS}bin${FS}javac -d ${DEST} ${TESTSRC}${FS}src${FS}test${FS}*.java +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d ${DEST} ${TESTSRC}${FS}src${FS}test${FS}*.java cd ${DEST} ${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} RunAllTests result=$? diff --git a/jdk/test/sun/security/krb5/config/dns.sh b/jdk/test/sun/security/krb5/config/dns.sh index 5c85f8aa776..71b2addc2d5 100644 --- a/jdk/test/sun/security/krb5/config/dns.sh +++ b/jdk/test/sun/security/krb5/config/dns.sh @@ -26,16 +26,19 @@ # @summary Krb5LoginModule config class does not return proper KDC list from DNS # +env + if [ "${TESTJAVA}" = "" ] ; then JAVAC_CMD=`which javac` TESTJAVA=`dirname $JAVAC_CMD`/.. + COMPILEJAVA="${TESTJAVA}" fi if [ "${TESTSRC}" = "" ] ; then TESTSRC="." fi -$TESTJAVA/bin/javac -d . \ - ${TESTSRC}/NamingManager.java ${TESTSRC}/DNS.java +$COMPILEJAVA/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . \ + ${TESTSRC}/NamingManager.java ${TESTSRC}/DNS.java $TESTJAVA/bin/java -Xbootclasspath/p:. DNS diff --git a/jdk/test/sun/security/krb5/runNameEquals.sh b/jdk/test/sun/security/krb5/runNameEquals.sh index 1fea3620e19..1c3dca34a3b 100644 --- a/jdk/test/sun/security/krb5/runNameEquals.sh +++ b/jdk/test/sun/security/krb5/runNameEquals.sh @@ -43,6 +43,10 @@ if [ "${TESTJAVA}" = "" ] ; then exit 1 fi +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi + NATIVE=false # set platform-dependent variables @@ -73,7 +77,7 @@ esac TEST=Krb5NameEquals -${TESTJAVA}${FILESEP}bin${FILESEP}javac \ +${COMPILEJAVA}${FILESEP}bin${FILESEP}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ -d ${TESTCLASSES}${FILESEP} \ ${TESTSRC}${FILESEP}${TEST}.java diff --git a/jdk/test/sun/security/mscapi/IsSunMSCAPIAvailable.sh b/jdk/test/sun/security/mscapi/IsSunMSCAPIAvailable.sh index 77e258e4cda..6e7ffa302b0 100644 --- a/jdk/test/sun/security/mscapi/IsSunMSCAPIAvailable.sh +++ b/jdk/test/sun/security/mscapi/IsSunMSCAPIAvailable.sh @@ -39,7 +39,7 @@ case "$OS" in # # execute test program - rely on it to exit if platform unsupported - ${TESTJAVA}/bin/javac -d . ${TESTSRC}\\IsSunMSCAPIAvailable.java + ${COMPILEJAVA}/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . ${TESTSRC}\\IsSunMSCAPIAvailable.java ${TESTJAVA}/bin/java ${TESTVMOPTS} IsSunMSCAPIAvailable exit ;; diff --git a/jdk/test/sun/security/pkcs11/KeyStore/Basic.sh b/jdk/test/sun/security/pkcs11/KeyStore/Basic.sh index 452b6290e4a..ab7045f12a9 100644 --- a/jdk/test/sun/security/pkcs11/KeyStore/Basic.sh +++ b/jdk/test/sun/security/pkcs11/KeyStore/Basic.sh @@ -54,9 +54,13 @@ fi if [ "${TESTJAVA}" = "" ] ; then TESTJAVA="/net/radiant/export1/charlie/mustang/build/solaris-sparc" fi +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi echo TESTSRC=${TESTSRC} echo TESTCLASSES=${TESTCLASSES} echo TESTJAVA=${TESTJAVA} +echo echo COMPILEJAVA=${COMPILEJAVA} echo "" # get command from input args - @@ -163,7 +167,7 @@ fi # compile test if [ "${RECOMPILE}" = "yes" ] ; then - ${TESTJAVA}${FS}bin${FS}javac \ + ${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ -classpath ${TESTSRC}${FS}..${PS}${TESTSRC}${FS}loader.jar \ -d ${TESTCLASSES} \ ${TESTSRC}${FS}Basic.java diff --git a/jdk/test/sun/security/pkcs11/KeyStore/ClientAuth.sh b/jdk/test/sun/security/pkcs11/KeyStore/ClientAuth.sh index d0996c1823a..eb340c919d5 100644 --- a/jdk/test/sun/security/pkcs11/KeyStore/ClientAuth.sh +++ b/jdk/test/sun/security/pkcs11/KeyStore/ClientAuth.sh @@ -40,9 +40,13 @@ fi if [ "${TESTJAVA}" = "" ] ; then TESTJAVA="/net/radiant/export1/charlie/mustang/build/solaris-sparc" fi +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi echo TESTSRC=${TESTSRC} echo TESTCLASSES=${TESTCLASSES} echo TESTJAVA=${TESTJAVA} +echo COMPILEJAVA=${COMPILEJAVA} echo "" OS=`uname -s` @@ -121,7 +125,7 @@ ${CP} ${TESTSRC}${FS}ClientAuthData${FS}key3.db ${TESTCLASSES} ${CHMOD} +w ${TESTCLASSES}${FS}key3.db # compile test -${TESTJAVA}${FS}bin${FS}javac \ +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ -classpath ${TESTSRC}${FS}..${PS}${TESTSRC}${FS}loader.jar \ -d ${TESTCLASSES} \ ${TESTSRC}${FS}ClientAuth.java diff --git a/jdk/test/sun/security/pkcs11/KeyStore/Solaris.sh b/jdk/test/sun/security/pkcs11/KeyStore/Solaris.sh index 5ec1ac2ec95..302ce50b3ed 100644 --- a/jdk/test/sun/security/pkcs11/KeyStore/Solaris.sh +++ b/jdk/test/sun/security/pkcs11/KeyStore/Solaris.sh @@ -53,9 +53,13 @@ fi if [ "${TESTJAVA}" = "" ] ; then TESTJAVA="/net/radiant/export1/charlie/mustang/build/solaris-sparc" fi +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi echo TESTSRC=${TESTSRC} echo TESTCLASSES=${TESTCLASSES} echo TESTJAVA=${TESTJAVA} +echo COMPILEJAVA=${COMPILEJAVA} echo "" # get command from input args - @@ -133,7 +137,7 @@ ${CHMOD} 600 ${TESTCLASSES}${FS}pkcs11_softtoken${FS}objstore_info if [ "${RECOMPILE}" = "yes" ] ; then cd ${TESTCLASSES} ${RM} *.class - ${TESTJAVA}${FS}bin${FS}javac \ + ${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ -classpath ${TESTSRC}${FS}..${PS}${TESTSRC}${FS}loader.jar \ -d ${TESTCLASSES} \ ${TESTSRC}${FS}Basic.java diff --git a/jdk/test/sun/security/pkcs11/Provider/ConfigQuotedString.sh b/jdk/test/sun/security/pkcs11/Provider/ConfigQuotedString.sh index 1a749d89490..b1cf0d9082c 100644 --- a/jdk/test/sun/security/pkcs11/Provider/ConfigQuotedString.sh +++ b/jdk/test/sun/security/pkcs11/Provider/ConfigQuotedString.sh @@ -41,9 +41,13 @@ fi if [ "${TESTJAVA}" = "" ] ; then TESTJAVA="/net/radiant/export1/charlie/mustang/build/solaris-sparc" fi +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi echo TESTSRC=${TESTSRC} echo TESTCLASSES=${TESTCLASSES} echo TESTJAVA=${TESTJAVA} +echo COMPILEJAVA=${COMPILEJAVA} echo "" # let java test exit if platform unsupported @@ -92,7 +96,7 @@ esac # compile test -${TESTJAVA}${FS}bin${FS}javac \ +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ -classpath ${TESTSRC}${FS}.. \ -d ${TESTCLASSES} \ ${TESTSRC}${FS}ConfigQuotedString.java diff --git a/jdk/test/sun/security/pkcs11/Provider/Login.sh b/jdk/test/sun/security/pkcs11/Provider/Login.sh index 6b37ef837d2..cbbcb06d318 100644 --- a/jdk/test/sun/security/pkcs11/Provider/Login.sh +++ b/jdk/test/sun/security/pkcs11/Provider/Login.sh @@ -42,9 +42,13 @@ fi if [ "${TESTJAVA}" = "" ] ; then TESTJAVA="/net/radiant/export1/charlie/mustang/build/solaris-sparc" fi +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi echo TESTSRC=${TESTSRC} echo TESTCLASSES=${TESTCLASSES} echo TESTJAVA=${TESTJAVA} +echo COMPILEJAVA=${COMPILEJAVA} echo "" # let java test exit if platform unsupported @@ -101,7 +105,7 @@ ${CHMOD} +w ${TESTCLASSES}${FS}key3.db # compile test -${TESTJAVA}${FS}bin${FS}javac \ +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ -classpath ${TESTSRC}${FS}.. \ -d ${TESTCLASSES} \ ${TESTSRC}${FS}Login.java diff --git a/jdk/test/sun/security/provider/PolicyFile/GrantAllPermToExtWhenNoPolicy.sh b/jdk/test/sun/security/provider/PolicyFile/GrantAllPermToExtWhenNoPolicy.sh index b09d1564b7f..70d42151373 100644 --- a/jdk/test/sun/security/provider/PolicyFile/GrantAllPermToExtWhenNoPolicy.sh +++ b/jdk/test/sun/security/provider/PolicyFile/GrantAllPermToExtWhenNoPolicy.sh @@ -41,6 +41,10 @@ if [ "${TESTJAVA}" = "" ] ; then exit 1 fi +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi + # set platform-dependent variables OS=`uname -s` case "$OS" in @@ -65,12 +69,14 @@ esac # compile the test program cd ${TESTSRC}${FILESEP} rm GrantAllPermToExtWhenNoPolicy.class -${TESTJAVA}${FILESEP}bin${FILESEP}javac -d ${TESTSRC}${FILESEP} ${TESTSRC}${FILESEP}SomeExtensionClass.java -${TESTJAVA}${FILESEP}bin${FILESEP}javac -d ${TESTSRC}${FILESEP} ${TESTSRC}${FILESEP}GrantAllPermToExtWhenNoPolicy.java +${COMPILEJAVA}${FILESEP}bin${FILESEP}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ + -d ${TESTSRC}${FILESEP} ${TESTSRC}${FILESEP}SomeExtensionClass.java +${COMPILEJAVA}${FILESEP}bin${FILESEP}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \ + -d ${TESTSRC}${FILESEP} ${TESTSRC}${FILESEP}GrantAllPermToExtWhenNoPolicy.java # create the extension JAR file cd ${TESTCLASSES} -${TESTJAVA}${FILESEP}bin${FILESEP}jar cvf SomeExt.jar SomeExtensionClass*.class +${COMPILEJAVA}${FILESEP}bin${FILESEP}jar cvf SomeExt.jar SomeExtensionClass*.class rm SomeExtensionClass.class # move the extension JAR file to the extension directory diff --git a/jdk/test/sun/security/provider/PolicyFile/getinstance/getinstance.sh b/jdk/test/sun/security/provider/PolicyFile/getinstance/getinstance.sh index 893bbb65bc5..5775eab80b1 100644 --- a/jdk/test/sun/security/provider/PolicyFile/getinstance/getinstance.sh +++ b/jdk/test/sun/security/provider/PolicyFile/getinstance/getinstance.sh @@ -44,6 +44,10 @@ if [ "${TESTJAVA}" = "" ] ; then exit 1 fi +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi + # set platform-dependent variables OS=`uname -s` case "$OS" in @@ -81,15 +85,15 @@ if [ ! -d ${TESTCLASSES}${FS}app ]; then fi cd ${TESTSRC}${FS} -${TESTJAVA}${FS}bin${FS}javac -d ${TESTCLASSES}${FS}boot \ +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d ${TESTCLASSES}${FS}boot \ ${TESTSRC}${FS}NoArgPermission.java -${TESTJAVA}${FS}bin${FS}javac -d ${TESTCLASSES}${FS}boot \ +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d ${TESTCLASSES}${FS}boot \ ${TESTSRC}${FS}OneArgPermission.java -${TESTJAVA}${FS}bin${FS}javac -d ${TESTCLASSES}${FS}boot \ +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d ${TESTCLASSES}${FS}boot \ ${TESTSRC}${FS}TwoArgPermission.java -${TESTJAVA}${FS}bin${FS}javac -d ${TESTCLASSES}${FS}boot \ +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d ${TESTCLASSES}${FS}boot \ ${TESTSRC}${FS}TwoArgNullActionsPermission.java -${TESTJAVA}${FS}bin${FS}javac -d ${TESTCLASSES}${FS}app \ +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d ${TESTCLASSES}${FS}app \ ${TESTSRC}${FS}GetInstance.java ${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} \ diff --git a/jdk/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/EngineArgs/DebugReportsOneExtraByte.sh b/jdk/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/EngineArgs/DebugReportsOneExtraByte.sh index f29f6cedd94..9b48bee7f3b 100644 --- a/jdk/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/EngineArgs/DebugReportsOneExtraByte.sh +++ b/jdk/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/EngineArgs/DebugReportsOneExtraByte.sh @@ -51,7 +51,8 @@ case "$OS" in ;; esac -${TESTJAVA}${FS}bin${FS}javac -d . ${TESTSRC}${FS}DebugReportsOneExtraByte.java +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . \ + ${TESTSRC}${FS}DebugReportsOneExtraByte.java STRING='main, WRITE: TLSv1 Application Data, length = 8' diff --git a/jdk/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/SSLSocketImpl/NotifyHandshakeTest.sh b/jdk/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/SSLSocketImpl/NotifyHandshakeTest.sh index 60a38468be5..eff4930de4a 100644 --- a/jdk/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/SSLSocketImpl/NotifyHandshakeTest.sh +++ b/jdk/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/SSLSocketImpl/NotifyHandshakeTest.sh @@ -35,6 +35,10 @@ then exit 1 fi +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi + if [ "${TESTSRC}" = "" ] then TESTSRC="." @@ -63,12 +67,12 @@ set -ex # # Compile the tests, package into their respective jars # -${TESTJAVA}${FILESEP}bin${FILESEP}javac -d . \ +${COMPILEJAVA}${FILESEP}bin${FILESEP}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . \ ${TESTSRC}${FILESEP}NotifyHandshakeTest.java \ ${TESTSRC}${FILESEP}NotifyHandshakeTestHeyYou.java -${TESTJAVA}${FILESEP}bin${FILESEP}jar -cvf com.jar \ +${COMPILEJAVA}${FILESEP}bin${FILESEP}jar ${TESTTOOLVMOPTS} -cvf com.jar \ com${FILESEP}NotifyHandshakeTest*.class -${TESTJAVA}${FILESEP}bin${FILESEP}jar -cvf edu.jar \ +${COMPILEJAVA}${FILESEP}bin${FILESEP}jar ${TESTTOOLVMOPTS} -cvf edu.jar \ edu${FILESEP}NotifyHandshakeTestHeyYou.class # diff --git a/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/PostThruProxy.sh b/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/PostThruProxy.sh index 85e9c22c4af..4cdc5d4d1b7 100644 --- a/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/PostThruProxy.sh +++ b/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/PostThruProxy.sh @@ -50,7 +50,9 @@ case "$OS" in ;; esac -${TESTJAVA}${FS}bin${FS}javac -d . ${TESTSRC}${FS}OriginServer.java \ - ${TESTSRC}${FS}ProxyTunnelServer.java ${TESTSRC}${FS}PostThruProxy.java +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . \ + ${TESTSRC}${FS}OriginServer.java \ + ${TESTSRC}${FS}ProxyTunnelServer.java \ + ${TESTSRC}${FS}PostThruProxy.java ${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} PostThruProxy ${HOSTNAME} ${TESTSRC} exit diff --git a/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/PostThruProxyWithAuth.sh b/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/PostThruProxyWithAuth.sh index 96c0642480b..de7b039488c 100644 --- a/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/PostThruProxyWithAuth.sh +++ b/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/PostThruProxyWithAuth.sh @@ -50,7 +50,7 @@ case "$OS" in ;; esac -${TESTJAVA}${FS}bin${FS}javac -d . ${TESTSRC}${FS}OriginServer.java \ +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . ${TESTSRC}${FS}OriginServer.java \ ${TESTSRC}${FS}ProxyTunnelServer.java \ ${TESTSRC}${FS}PostThruProxyWithAuth.java ${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} PostThruProxyWithAuth ${HOSTNAME} ${TESTSRC} diff --git a/jdk/test/sun/security/tools/keytool/autotest.sh b/jdk/test/sun/security/tools/keytool/autotest.sh index 56836d107c3..9c797dd21e5 100644 --- a/jdk/test/sun/security/tools/keytool/autotest.sh +++ b/jdk/test/sun/security/tools/keytool/autotest.sh @@ -40,6 +40,9 @@ if [ "${TESTJAVA}" = "" ] ; then echo "FAILED!!!" exit 1 fi +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi find_one() { for TARGET_FILE in $@; do @@ -82,7 +85,7 @@ if [ "$LIBNAME" = "" ]; then exit 1 fi -${TESTJAVA}${FS}bin${FS}javac -d . -XDignore.symbol.file \ +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . -XDignore.symbol.file \ ${TESTSRC}${FS}KeyToolTest.java || exit 10 NSS=${TESTSRC}${FS}..${FS}..${FS}pkcs11${FS}nss diff --git a/jdk/test/sun/security/tools/keytool/printssl.sh b/jdk/test/sun/security/tools/keytool/printssl.sh index 46de2609b0b..36ba9d09437 100644 --- a/jdk/test/sun/security/tools/keytool/printssl.sh +++ b/jdk/test/sun/security/tools/keytool/printssl.sh @@ -33,6 +33,9 @@ if [ "${TESTJAVA}" = "" ] ; then echo "FAILED!!!" exit 1 fi +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi # set platform-dependent variables OS=`uname -s` @@ -52,7 +55,7 @@ case "$OS" in ;; esac -${TESTJAVA}${FS}bin${FS}javac -d . ${TESTSRC}${FS}PrintSSL.java || exit 10 +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . ${TESTSRC}${FS}PrintSSL.java || exit 10 ${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} -Dtest.src=$TESTSRC PrintSSL | ( read PORT; ${TESTJAVA}${FS}bin${FS}keytool -printcert -sslserver localhost:$PORT ) status=$? diff --git a/jdk/test/sun/security/tools/keytool/readjar.sh b/jdk/test/sun/security/tools/keytool/readjar.sh index f0a51216b31..037a90818c8 100644 --- a/jdk/test/sun/security/tools/keytool/readjar.sh +++ b/jdk/test/sun/security/tools/keytool/readjar.sh @@ -29,6 +29,7 @@ if [ "${TESTJAVA}" = "" ] ; then JAVAC_CMD=`which javac` TESTJAVA=`dirname $JAVAC_CMD`/.. + COMPILEJAVA=${TESTJAVA} fi # set platform-dependent variables @@ -44,13 +45,13 @@ esac KS=readjar.jks rm $KS -$TESTJAVA${FS}bin${FS}keytool -storepass changeit -keypass changeit -keystore $KS \ +$TESTJAVA${FS}bin${FS}keytool ${TESTTOOLVMOPTS} -storepass changeit -keypass changeit -keystore $KS \ -alias x -dname CN=X -genkeypair -$TESTJAVA${FS}bin${FS}jar cvf readjar.jar $KS -$TESTJAVA${FS}bin${FS}jarsigner -storepass changeit -keystore $KS readjar.jar x +$COMPILEJAVA${FS}bin${FS}jar ${TESTTOOLVMOPTS} cvf readjar.jar $KS +$COMPILEJAVA${FS}bin${FS}jarsigner ${TESTTOOLVMOPTS} -storepass changeit -keystore $KS readjar.jar x -$TESTJAVA${FS}bin${FS}keytool -printcert -jarfile readjar.jar || exit 1 -$TESTJAVA${FS}bin${FS}keytool -printcert -jarfile readjar.jar -rfc || exit 1 +$TESTJAVA${FS}bin${FS}keytool ${TESTTOOLVMOPTS} -printcert -jarfile readjar.jar || exit 1 +$TESTJAVA${FS}bin${FS}keytool ${TESTTOOLVMOPTS} -printcert -jarfile readjar.jar -rfc || exit 1 exit 0 diff --git a/jdk/test/sun/security/tools/keytool/standard.sh b/jdk/test/sun/security/tools/keytool/standard.sh index c75a7a49b46..630aa41c46f 100644 --- a/jdk/test/sun/security/tools/keytool/standard.sh +++ b/jdk/test/sun/security/tools/keytool/standard.sh @@ -39,6 +39,7 @@ fi if [ "${TESTJAVA}" = "" ] ; then JAVAC_CMD=`which javac` TESTJAVA=`dirname $JAVAC_CMD`/.. + COMPILEJAVA="${TESTJAVA}" fi # set platform-dependent variables @@ -56,7 +57,7 @@ case "$OS" in ;; esac -${TESTJAVA}${FS}bin${FS}javac -d . -XDignore.symbol.file ${TESTSRC}${FS}KeyToolTest.java || exit 10 +${COMPILEJAVA}${FS}bin${FS}javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . -XDignore.symbol.file ${TESTSRC}${FS}KeyToolTest.java || exit 10 echo | ${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} -Dfile KeyToolTest status=$? diff --git a/jdk/test/sun/security/util/Oid/S11N.sh b/jdk/test/sun/security/util/Oid/S11N.sh index 488c8959094..a19af17be5f 100644 --- a/jdk/test/sun/security/util/Oid/S11N.sh +++ b/jdk/test/sun/security/util/Oid/S11N.sh @@ -39,6 +39,9 @@ if [ "${TESTJAVA}" = "" ] ; then echo "FAILED!!!" exit 1 fi +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi # set platform-dependent variables PF="" @@ -103,7 +106,7 @@ echo "===================================================" # the test code -${TESTJAVA}${FS}bin${FS}javac -target 1.4 -source 1.4 \ +${COMPILEJAVA}${FS}bin${FS}javac -target 1.4 -source 1.4 \ -d . ${TESTSRC}${FS}SerialTest.java || exit 10 # You can set ALT_JAVA_RE_JDK to another location that contains the diff --git a/jdk/test/sun/security/validator/certreplace.sh b/jdk/test/sun/security/validator/certreplace.sh index b45a70edaa8..78f821bf153 100644 --- a/jdk/test/sun/security/validator/certreplace.sh +++ b/jdk/test/sun/security/validator/certreplace.sh @@ -33,6 +33,7 @@ fi if [ "${TESTJAVA}" = "" ] ; then JAVAC_CMD=`which javac` TESTJAVA=`dirname $JAVAC_CMD`/.. + COMPILEJAVA="${TESTJAVA}" fi # set platform-dependent variables @@ -48,7 +49,7 @@ esac KT="$TESTJAVA${FS}bin${FS}keytool -storepass changeit \ -keypass changeit -keystore certreplace.jks" -JAVAC=$TESTJAVA${FS}bin${FS}javac +JAVAC=$COMPILEJAVA${FS}bin${FS}javac JAVA=$TESTJAVA${FS}bin${FS}java rm -rf certreplace.jks 2> /dev/null @@ -81,5 +82,5 @@ $KT -delete -alias user # 5. Build and run test -$JAVAC -d . ${TESTSRC}${FS}CertReplace.java +$JAVAC ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . ${TESTSRC}${FS}CertReplace.java $JAVA ${TESTVMOPTS} CertReplace certreplace.jks certreplace.certs diff --git a/jdk/test/sun/security/validator/samedn.sh b/jdk/test/sun/security/validator/samedn.sh index 5d9b0455713..0e3e8370800 100644 --- a/jdk/test/sun/security/validator/samedn.sh +++ b/jdk/test/sun/security/validator/samedn.sh @@ -33,6 +33,7 @@ fi if [ "${TESTJAVA}" = "" ] ; then JAVAC_CMD=`which javac` TESTJAVA=`dirname $JAVAC_CMD`/.. + COMPILEJAVA="${TESTJAVA}" fi # set platform-dependent variables @@ -48,7 +49,7 @@ esac KT="$TESTJAVA${FS}bin${FS}keytool -storepass changeit \ -keypass changeit -keystore samedn.jks" -JAVAC=$TESTJAVA${FS}bin${FS}javac +JAVAC=$COMPILEJAVA${FS}bin${FS}javac JAVA=$TESTJAVA${FS}bin${FS}java rm -rf samedn.jks 2> /dev/null @@ -77,6 +78,6 @@ $KT -delete -alias user # 5. Build and run test. Make sure the CA certs are ignored for validity check. # Check both, one of them might be dropped out of map in old codes. -$JAVAC -d . ${TESTSRC}${FS}CertReplace.java +$JAVAC ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d . ${TESTSRC}${FS}CertReplace.java $JAVA ${TESTVMOPTS} CertReplace samedn.jks samedn1.certs || exit 1 $JAVA ${TESTVMOPTS} CertReplace samedn.jks samedn2.certs || exit 2 diff --git a/jdk/test/tools/launcher/ClassPathWildCard.sh b/jdk/test/tools/launcher/ClassPathWildCard.sh index 78c03c8a574..67434b372b7 100644 --- a/jdk/test/tools/launcher/ClassPathWildCard.sh +++ b/jdk/test/tools/launcher/ClassPathWildCard.sh @@ -43,6 +43,10 @@ if [ "${TESTJAVA}" = "" ]; then exit 1 fi +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi + if [ "${TESTSRC}" = "" ]; then echo "TESTSRC not set. Test cannot execute. Failed." exit 1 @@ -54,8 +58,8 @@ if [ "${TESTCLASSES}" = "" ]; then fi JAVA=$TESTJAVA/bin/java -JAVAC=$TESTJAVA/bin/javac -JAR=$TESTJAVA/bin/jar +JAVAC=$COMPILEJAVA/bin/javac +JAR=$COMPILEJAVA/bin/jar OUTEXT=".out" @@ -91,14 +95,14 @@ CreateClassFiles() { Exp=$1 [ -d Test${Exp} ] || mkdir Test${Exp} EmitJavaFile Test${Exp}/Test${Exp}.java - $JAVAC -d Test${Exp} Test${Exp}/Test${Exp}.java || exit 1 + $JAVAC ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} -d Test${Exp} Test${Exp}/Test${Exp}.java || exit 1 } CreateJarFiles() { Exp=$1 [ -d JarDir ] || mkdir JarDir CreateClassFiles $Exp - $JAR -cvf JarDir/Test${Exp}.jar -C Test${Exp} . || exit 1 + $JAR ${TESTTOOLVMOPTS} -cvf JarDir/Test${Exp}.jar -C Test${Exp} . || exit 1 } CheckFail() { diff --git a/jdk/test/tools/launcher/MultipleJRE.sh b/jdk/test/tools/launcher/MultipleJRE.sh index b1ef16f4062..0c8e95d29fb 100644 --- a/jdk/test/tools/launcher/MultipleJRE.sh +++ b/jdk/test/tools/launcher/MultipleJRE.sh @@ -37,6 +37,10 @@ then exit 1 fi +if [ "${COMPILEJAVA}" = "" ]; then + COMPILEJAVA="${TESTJAVA}" +fi + if [ "${TESTSRC}" = "" ] then echo "TESTSRC not set. Test cannot execute. Failed." @@ -51,7 +55,7 @@ fi JAVAEXE="$TESTJAVA/bin/java ${TESTVMOPTS}" JAVA="$TESTJAVA/bin/java ${TESTVMOPTS} -classpath $TESTCLASSES" -JAR="$TESTJAVA/bin/jar" +JAR="$COMPILEJAVA/bin/jar ${TESTTOOLVMOPTS}" OS=`uname -s`; # From 2cbabcea4a6e675bd498173a64f1f061c3ac5199 Mon Sep 17 00:00:00 2001 From: Jiangli Zhou Date: Fri, 11 Jan 2013 16:55:07 -0500 Subject: [PATCH 013/138] 8005895: Inefficient InstanceKlass field packing wasts memory Pack _misc_has_default_methods into the _misc_flags, move _idnum_allocated_count. Reviewed-by: coleenp, shade --- hotspot/src/share/vm/oops/instanceKlass.hpp | 24 +++++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/hotspot/src/share/vm/oops/instanceKlass.hpp b/hotspot/src/share/vm/oops/instanceKlass.hpp index 0d173faa510..9793dd77d82 100644 --- a/hotspot/src/share/vm/oops/instanceKlass.hpp +++ b/hotspot/src/share/vm/oops/instanceKlass.hpp @@ -225,12 +225,16 @@ class InstanceKlass: public Klass { u2 _java_fields_count; // The number of declared Java fields int _nonstatic_oop_map_size;// size in words of nonstatic oop map blocks + // _is_marked_dependent can be set concurrently, thus cannot be part of the + // _misc_flags. bool _is_marked_dependent; // used for marking during flushing and deoptimization + enum { _misc_rewritten = 1 << 0, // methods rewritten. _misc_has_nonstatic_fields = 1 << 1, // for sizing with UseCompressedOops _misc_should_verify_class = 1 << 2, // allow caching of preverification - _misc_is_anonymous = 1 << 3 // has embedded _inner_classes field + _misc_is_anonymous = 1 << 3, // has embedded _inner_classes field + _misc_has_default_methods = 1 << 4 // class/superclass/implemented interfaces has default methods }; u2 _misc_flags; u2 _minor_version; // minor version number of class file @@ -253,10 +257,6 @@ class InstanceKlass: public Klass { jint _cached_class_file_len; // JVMTI: length of above JvmtiCachedClassFieldMap* _jvmti_cached_class_field_map; // JVMTI: used during heap iteration - // true if class, superclass, or implemented interfaces have default methods - bool _has_default_methods; - - volatile u2 _idnum_allocated_count; // JNI/JVMTI: increments with the addition of methods, old ids don't change // Method array. Array* _methods; // Interface (Klass*s) this class declares locally to implement. @@ -280,6 +280,8 @@ class InstanceKlass: public Klass { // ... Array* _fields; + volatile u2 _idnum_allocated_count; // JNI/JVMTI: increments with the addition of methods, old ids don't change + // Class states are defined as ClassState (see above). // Place the _init_state here to utilize the unused 2-byte after // _idnum_allocated_count. @@ -616,8 +618,16 @@ class InstanceKlass: public Klass { return _jvmti_cached_class_field_map; } - bool has_default_methods() const { return _has_default_methods; } - void set_has_default_methods(bool b) { _has_default_methods = b; } + bool has_default_methods() const { + return (_misc_flags & _misc_has_default_methods) != 0; + } + void set_has_default_methods(bool b) { + if (b) { + _misc_flags |= _misc_has_default_methods; + } else { + _misc_flags &= ~_misc_has_default_methods; + } + } // for adding methods, ConstMethod::UNSET_IDNUM means no more ids available inline u2 next_method_idnum(); From 6c57a4b9f8978dfa88f586d7dc109d95f6a62f96 Mon Sep 17 00:00:00 2001 From: Christian Thalinger Date: Fri, 11 Jan 2013 14:07:09 -0800 Subject: [PATCH 014/138] 8006031: LibraryCallKit::inline_array_copyOf disabled unintentionally with 7172640 Reviewed-by: kvn --- hotspot/src/share/vm/opto/library_call.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hotspot/src/share/vm/opto/library_call.cpp b/hotspot/src/share/vm/opto/library_call.cpp index 313339edfde..b2138abe931 100644 --- a/hotspot/src/share/vm/opto/library_call.cpp +++ b/hotspot/src/share/vm/opto/library_call.cpp @@ -3559,7 +3559,7 @@ bool LibraryCallKit::inline_native_getLength() { // public static T[] java.util.Arrays.copyOf( U[] original, int newLength, Class newType); // public static T[] java.util.Arrays.copyOfRange(U[] original, int from, int to, Class newType); bool LibraryCallKit::inline_array_copyOf(bool is_copyOfRange) { - return false; + tty->print_cr("LibraryCallKit::inline_array_copyOf: %d", is_copyOfRange); if (too_many_traps(Deoptimization::Reason_intrinsic)) return false; // Get the arguments. From 546e2eb164e64750e363ea018ade898d91238a21 Mon Sep 17 00:00:00 2001 From: Olivier Lagneau Date: Fri, 11 Jan 2013 15:39:08 -0800 Subject: [PATCH 015/138] 7131459: [Fmt-De] DecimalFormat produces wrong format() results when close to a tie Reviewed-by: darcy --- .../share/classes/java/text/DigitList.java | 143 +++++- .../classes/sun/misc/FloatingDecimal.java | 31 +- .../Format/DecimalFormat/TieRoundingTest.java | 411 ++++++++++++++++++ 3 files changed, 568 insertions(+), 17 deletions(-) create mode 100644 jdk/test/java/text/Format/DecimalFormat/TieRoundingTest.java diff --git a/jdk/src/share/classes/java/text/DigitList.java b/jdk/src/share/classes/java/text/DigitList.java index ce2a0685f99..da5e78d65b3 100644 --- a/jdk/src/share/classes/java/text/DigitList.java +++ b/jdk/src/share/classes/java/text/DigitList.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,6 +41,7 @@ package java.text; import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; +import sun.misc.FloatingDecimal; /** * Digit List. Private to DecimalFormat. @@ -62,7 +63,7 @@ import java.math.RoundingMode; * derived by placing all the digits of the list to the right of the * decimal point, by 10^exponent. * - * @see java.util.Locale + * @see Locale * @see Format * @see NumberFormat * @see DecimalFormat @@ -286,14 +287,27 @@ final class DigitList implements Cloneable { * fractional digits to be converted. If false, total digits. */ final void set(boolean isNegative, double source, int maximumDigits, boolean fixedPoint) { - set(isNegative, Double.toString(source), maximumDigits, fixedPoint); + + FloatingDecimal fd = new FloatingDecimal(source); + boolean hasBeenRoundedUp = fd.digitsRoundedUp(); + boolean allDecimalDigits = fd.decimalDigitsExact(); + String digitsString = fd.toJavaFormatString(); + + set(isNegative, digitsString, + hasBeenRoundedUp, allDecimalDigits, + maximumDigits, fixedPoint); } /** * Generate a representation of the form DDDDD, DDDDD.DDDDD, or * DDDDDE+/-DDDDD. + * @param roundedUp Boolean value indicating if the s digits were rounded-up. + * @param allDecimalDigits Boolean value indicating if the digits in s are + * an exact decimal representation of the double that was passed. */ - final void set(boolean isNegative, String s, int maximumDigits, boolean fixedPoint) { + final void set(boolean isNegative, String s, + boolean roundedUp, boolean allDecimalDigits, + int maximumDigits, boolean fixedPoint) { this.isNegative = isNegative; int len = s.length(); char[] source = getDataChars(len); @@ -346,7 +360,7 @@ final class DigitList implements Cloneable { } else if (-decimalAt == maximumDigits) { // If we round 0.0009 to 3 fractional digits, then we have to // create a new one digit in the least significant location. - if (shouldRoundUp(0)) { + if (shouldRoundUp(0, roundedUp, allDecimalDigits)) { count = 1; ++decimalAt; digits[0] = '1'; @@ -365,19 +379,26 @@ final class DigitList implements Cloneable { // Eliminate digits beyond maximum digits to be displayed. // Round up if appropriate. - round(fixedPoint ? (maximumDigits + decimalAt) : maximumDigits); + round(fixedPoint ? (maximumDigits + decimalAt) : maximumDigits, + roundedUp, allDecimalDigits); } /** * Round the representation to the given number of digits. * @param maximumDigits The maximum number of digits to be shown. + * @param alreadyRounded Boolean indicating if rounding up already happened. + * @param allDecimalDigits Boolean indicating if the digits provide an exact + * representation of the value. + * * Upon return, count will be less than or equal to maximumDigits. */ - private final void round(int maximumDigits) { + private final void round(int maximumDigits, + boolean alreadyRounded, + boolean allDecimalDigits) { // Eliminate digits beyond maximum digits to be displayed. // Round up if appropriate. if (maximumDigits >= 0 && maximumDigits < count) { - if (shouldRoundUp(maximumDigits)) { + if (shouldRoundUp(maximumDigits, alreadyRounded, allDecimalDigits)) { // Rounding up involved incrementing digits from LSD to MSD. // In most cases this is simple, but in a worst case situation // (9999..99) we have to adjust the decimalAt value. @@ -423,8 +444,56 @@ final class DigitList implements Cloneable { * @return true if digit maximumDigits-1 should be * incremented */ - private boolean shouldRoundUp(int maximumDigits) { + private boolean shouldRoundUp(int maximumDigits, + boolean alreadyRounded, + boolean allDecimalDigits) { if (maximumDigits < count) { + /* + * To avoid erroneous double-rounding or truncation when converting + * a binary double value to text, information about the exactness + * of the conversion result in FloatingDecimal, as well as any + * rounding done, is needed in this class. + * + * - For the HALF_DOWN, HALF_EVEN, HALF_UP rounding rules below: + * In the case of formating float or double, We must take into + * account what FloatingDecimal has done in the binary to decimal + * conversion. + * + * Considering the tie cases, FloatingDecimal may round-up the + * value (returning decimal digits equal to tie when it is below), + * or "truncate" the value to the tie while value is above it, + * or provide the exact decimal digits when the binary value can be + * converted exactly to its decimal representation given formating + * rules of FloatingDecimal ( we have thus an exact decimal + * representation of the binary value). + * + * - If the double binary value was converted exactly as a decimal + * value, then DigitList code must apply the expected rounding + * rule. + * + * - If FloatingDecimal already rounded up the decimal value, + * DigitList should neither round up the value again in any of + * the three rounding modes above. + * + * - If FloatingDecimal has truncated the decimal value to + * an ending '5' digit, DigitList should round up the value in + * all of the three rounding modes above. + * + * + * This has to be considered only if digit at maximumDigits index + * is exactly the last one in the set of digits, otherwise there are + * remaining digits after that position and we dont have to consider + * what FloatingDecimal did. + * + * - Other rounding modes are not impacted by these tie cases. + * + * - For other numbers that are always converted to exact digits + * (like BigInteger, Long, ...), the passed alreadyRounded boolean + * have to be set to false, and allDecimalDigits has to be set to + * true in the upper DigitList call stack, providing the right state + * for those situations.. + */ + switch(roundingMode) { case UP: for (int i=maximumDigits; i= '5') { + // We should not round up if the rounding digits position is + // exactly the last index and if digits were already rounded. + if ((maximumDigits == (count - 1)) && + (alreadyRounded)) + return false; + + // Value was exactly at or was above tie. We must round up. return true; } break; @@ -458,6 +534,21 @@ final class DigitList implements Cloneable { if (digits[maximumDigits] > '5') { return true; } else if (digits[maximumDigits] == '5' ) { + if (maximumDigits == (count - 1)) { + // The rounding position is exactly the last index. + if (allDecimalDigits || alreadyRounded) + /* FloatingDecimal rounded up (value was below tie), + * or provided the exact list of digits (value was + * an exact tie). We should not round up, following + * the HALF_DOWN rounding rule. + */ + return false; + else + // Value was above the tie, we must round up. + return true; + } + + // We must round up if it gives a non null digit after '5'. for (int i=maximumDigits+1; i '5') { return true; } else if (digits[maximumDigits] == '5' ) { - for (int i=maximumDigits+1; i 0) && + (digits[maximumDigits-1] % 2 != 0)); + } + } else { + // Rounds up if it gives a non null digit after '5' + for (int i=maximumDigits+1; i 0 && (digits[maximumDigits-1] % 2 != 0); } break; case UNNECESSARY: @@ -542,7 +653,7 @@ final class DigitList implements Cloneable { count = right - left + 1; System.arraycopy(digits, left, digits, 0, count); } - if (maximumDigits > 0) round(maximumDigits); + if (maximumDigits > 0) round(maximumDigits, false, true); } /** @@ -559,7 +670,9 @@ final class DigitList implements Cloneable { String s = source.toString(); extendDigits(s.length()); - set(isNegative, s, maximumDigits, fixedPoint); + set(isNegative, s, + false, true, + maximumDigits, fixedPoint); } /** @@ -584,7 +697,7 @@ final class DigitList implements Cloneable { count = right + 1; if (maximumDigits > 0) { - round(maximumDigits); + round(maximumDigits, false, true); } } diff --git a/jdk/src/share/classes/sun/misc/FloatingDecimal.java b/jdk/src/share/classes/sun/misc/FloatingDecimal.java index 2679a4eec3f..2db646d2cc1 100644 --- a/jdk/src/share/classes/sun/misc/FloatingDecimal.java +++ b/jdk/src/share/classes/sun/misc/FloatingDecimal.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,6 +41,19 @@ public class FloatingDecimal{ boolean fromHex = false; int roundDir = 0; // set by doubleValue + /* + * The fields below provides additional information about the result of + * the binary to decimal digits conversion done in dtoa() and roundup() + * methods. They are changed if needed by those two methods. + */ + + // True if the dtoa() binary to decimal conversion was exact. + boolean exactDecimalConversion = false; + + // True if the result of the binary to decimal conversion was rounded-up + // at the end of the conversion process, i.e. roundUp() method was called. + boolean decimalDigitsRoundedUp = false; + private FloatingDecimal( boolean negSign, int decExponent, char []digits, int n, boolean e ) { isNegative = negSign; @@ -396,6 +409,11 @@ public class FloatingDecimal{ // else fall through. } digits[i] = (char)(q+1); + decimalDigitsRoundedUp = true; + } + + public boolean digitsRoundedUp() { + return decimalDigitsRoundedUp; } /* @@ -751,6 +769,7 @@ public class FloatingDecimal{ digits[ndigit++] = (char)('0' + q); } lowDigitDifference = (b<<1) - tens; + exactDecimalConversion = (b == 0); } else { // still good! they're all longs! long b = (fractBits * long5pow[B5] ) << B2; @@ -804,8 +823,10 @@ public class FloatingDecimal{ digits[ndigit++] = (char)('0' + q); } lowDigitDifference = (b<<1) - tens; + exactDecimalConversion = (b == 0); } } else { + FDBigInt ZeroVal = new FDBigInt(0); FDBigInt tenSval; int shiftBias; @@ -859,8 +880,10 @@ public class FloatingDecimal{ if ( high && low ){ Bval.lshiftMe(1); lowDigitDifference = Bval.cmp(tenSval); - } else + } else { lowDigitDifference = 0L; // this here only for flow analysis! + } + exactDecimalConversion = (Bval.cmp( ZeroVal ) == 0); } this.decExponent = decExp+1; this.digits = digits; @@ -883,6 +906,10 @@ public class FloatingDecimal{ } } + public boolean decimalDigitsExact() { + return exactDecimalConversion; + } + public String toString(){ // most brain-dead version diff --git a/jdk/test/java/text/Format/DecimalFormat/TieRoundingTest.java b/jdk/test/java/text/Format/DecimalFormat/TieRoundingTest.java new file mode 100644 index 00000000000..fb460db2e1f --- /dev/null +++ b/jdk/test/java/text/Format/DecimalFormat/TieRoundingTest.java @@ -0,0 +1,411 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * + * @bug 7131459 + * @summary test various situations of NumberFormat rounding when close to tie + * @author Olivier Lagneau + * @run main TieRoundingTest + * + */ + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.NumberFormat; +import java.text.DecimalFormat; +import java.math.RoundingMode; +import java.util.Locale; + +public class TieRoundingTest { + + static int testCounter = 0; + static int errorCounter = 0; + static boolean allPassed = true; + + static void formatOutputTestDouble(NumberFormat nf, + double doubleToTest, + String tiePosition, + String inputDigits, + String expectedOutput) { + + int mfd = nf.getMaximumFractionDigits(); + RoundingMode rm = nf.getRoundingMode(); + String result = nf.format(doubleToTest); + + if (!result.equals(expectedOutput)) { + System.out.println(); + System.out.println("========================================"); + System.out.println("***Error formatting double value from string : " + + inputDigits); + System.out.println("NumberFormat pattern is : " + + ((DecimalFormat ) nf).toPattern()); + System.out.println("Maximum number of fractional digits : " + mfd); + System.out.println("Fractional rounding digit : " + (mfd + 1)); + System.out.println("Position of value relative to tie : " + tiePosition); + System.out.println("Rounding Mode : " + rm); + System.out.println("BigDecimal output : " + + new BigDecimal(doubleToTest).toString()); + System.out.println("FloatingDecimal output : " + doubleToTest); + System.out.println( + "Error. Formatted result different from expected." + + "\nExpected output is : \"" + expectedOutput + "\"" + + "\nFormated output is : \"" + result + "\""); + System.out.println("========================================"); + System.out.println(); + + errorCounter++; + allPassed = false; + } else { + testCounter++; + System.out.println("\nSuccess for double value : " + doubleToTest + " :"); + System.out.println(" Input digits :" + inputDigits + + ", BigDecimal value : " + + new BigDecimal(doubleToTest).toString()); + System.out.print(" Rounding mode: " + rm); + System.out.print(", fract digits : " + mfd); + System.out.print(", position : " + tiePosition + " tie"); + System.out.print(", result : " + result); + System.out.println(", expected : " + expectedOutput); + } + } + + static void formatOutputTestLong(NumberFormat nf, + long longToTest, + String tiePosition, + String inputDigits, + String expectedOutput) { + + int mfd = nf.getMaximumFractionDigits(); + RoundingMode rm = nf.getRoundingMode(); + String result = nf.format(longToTest); + + if (!result.equals(expectedOutput)) { + System.out.println(); + System.out.println("========================================"); + System.out.println("***Error formatting double value from string : " + + inputDigits); + System.out.println("NumberFormat pattern is : " + + ((DecimalFormat ) nf).toPattern()); + System.out.println("Maximum number of fractional digits : " + mfd); + System.out.println("Fractional rounding digit : " + (mfd + 1)); + System.out.println("Position of value relative to tie : " + tiePosition); + System.out.println("Rounding Mode : " + rm); + System.out.println( + "Error. Formatted result different from expected." + + "\nExpected output is : \"" + expectedOutput + "\"" + + "\nFormated output is : \"" + result + "\""); + System.out.println("========================================"); + System.out.println(); + + errorCounter++; + allPassed = false; + } else { + testCounter++; + System.out.print("Success. Long input :" + inputDigits); + System.out.print(", rounding : " + rm); + System.out.print(", fract digits : " + mfd); + System.out.print(", tie position : " + tiePosition); + System.out.println(", expected : " + expectedOutput); + + } + } + + static void formatOutputTestObject(NumberFormat nf, + Object someNumber, + String tiePosition, + String inputDigits, + String expectedOutput) { + + int mfd = nf.getMaximumFractionDigits(); + RoundingMode rm = nf.getRoundingMode(); + String result = nf.format(someNumber); + + if (!result.equals(expectedOutput)) { + System.out.println(); + System.out.println("========================================"); + System.out.println("***Error formatting number value from string : " + + inputDigits); + System.out.println("NumberFormat pattern is : " + + ((DecimalFormat ) nf).toPattern()); + System.out.println("Maximum number of fractional digits : " + mfd); + System.out.println("Fractional rounding digit : " + (mfd + 1)); + System.out.println("Position of value relative to tie : " + tiePosition); + System.out.println("Rounding Mode : " + rm); + System.out.println("Number self output representation: " + someNumber); + System.out.println( + "Error. Formatted result different from expected." + + "\nExpected output is : \"" + expectedOutput + "\"" + + "\nFormated output is : \"" + result + "\""); + System.out.println("========================================"); + System.out.println(); + + errorCounter++; + allPassed = false; + } else { + testCounter++; + System.out.print("Success. Number input :" + inputDigits); + System.out.print(", rounding : " + rm); + System.out.print(", fract digits : " + mfd); + System.out.print(", tie position : " + tiePosition); + System.out.println(", expected : " + expectedOutput); + } + } + + public static void main(String[] args) { + + // Only the 3 rounding modes below may be impacted by bug 7131459. + // So we do not test the other rounding modes. + RoundingMode[] roundingModes = { + RoundingMode.HALF_DOWN, + RoundingMode.HALF_EVEN, + RoundingMode.HALF_UP + }; + + // Precise the relative position of input value against its closest tie. + String[] tieRelativePositions = { + "below", "exact", "above", + "below", "exact", "above", + "below", "exact", "above", + "below", "exact", "above" + }; + + // =============== Testing double (and thus float) value cases ========= + + System.out.println("\n===== testing 3 digits rounding position ====="); + double[] values3FractDigits = { + // unimpacting values close to tie, with less than 3 input fract digits + 1.115d, 1.125d, 1.135d, + // impacting close to tie values covering all 6 cases + 0.3115d, 0.3125d, 0.3135d, + 0.6865d, 0.6875d, 0.6885d, + // unimpacting values close to tie, with more than 3 input fract digits + 1.46885d, 2.46875d, 1.46865d + }; + + String[] inputs3FractDigits = { + "1.115d", "1.125d", "1.135d", + "0.3115d", "0.3125d", "0.3135d", + "0.6865d", "0.6875d", "0.6885d", + "1.46885d", "2.46875d", "1.46865d" + }; + + String[][] expected3FractDigits = { + {"1.115", "1.125", "1.135", + "0.311", "0.312", "0.314", + "0.686", "0.687", "0.689", + "1.469", "2.469", "1.469" + }, + {"1.115", "1.125", "1.135", + "0.311", "0.312", "0.314", + "0.686", "0.688", "0.689", + "1.469", "2.469", "1.469" + }, + {"1.115", "1.125", "1.135", + "0.311", "0.313", "0.314", + "0.686", "0.688", "0.689", + "1.469", "2.469", "1.469" + }, + }; + + + for (int r = 0; r < roundingModes.length; r++) { + NumberFormat dfDefault = NumberFormat.getInstance(Locale.US); + RoundingMode rmode = roundingModes[r]; + dfDefault.setRoundingMode(rmode); + System.out.println("\n----- Now checking " + rmode + + " rounding mode -----"); + + for (int i = 0; i < values3FractDigits.length; i++) { + double d = values3FractDigits[i]; + String tiePosition = tieRelativePositions[i]; + String input = inputs3FractDigits[i]; + String expected = expected3FractDigits[r][i]; + + formatOutputTestDouble(dfDefault, d, tiePosition, input, expected); + } + } + + System.out.println("\n===== testing 5 digits rounding position ====="); + double[] values5FractDigits = { + // unimpacting values close to tie, with less than 5 input fract digits + 1.3135d, 1.3125d, 1.3115d, + // impacting values close to tie, covering all 6 cases + 1.328115d, 1.328125d, 1.328135d, + 1.796865d, 1.796875d, 1.796885d, + // unimpacting values close to tie, with more than 5 input fract digits + 1.3281149999999d, 1.75390625d, 1.7968750000001d + }; + + String[] inputs5FractDigits = { + "1.3135d", "1.3125d", "1.3115d", + "1.328115d", "1.328125d", "1.328135d", + "1.796865d", "1.796875d", "1.796885d", + "1.3281149999999d", "1.75390625d", "1.7968750000001d" + }; + + String[][] expected5FractDigits = { + {"1.3135", "1.3125", "1.3115", + "1.32811", "1.32812", "1.32814", + "1.79686", "1.79687", "1.79689", + "1.32811", "1.75391", "1.79688" + }, + {"1.3135", "1.3125", "1.3115", + "1.32811", "1.32812", "1.32814", + "1.79686", "1.79688", "1.79689", + "1.32811", "1.75391", "1.79688" + }, + {"1.3135", "1.3125", "1.3115", + "1.32811", "1.32813", "1.32814", + "1.79686", "1.79688", "1.79689", + "1.32811", "1.75391", "1.79688" + } + }; + + + for (int r = 0; r < roundingModes.length; r++) { + DecimalFormat df5 = (DecimalFormat) NumberFormat.getInstance(Locale.US); + RoundingMode rmode = roundingModes[r]; + df5.setRoundingMode(rmode); + System.out.println("\n----- Now checking " + rmode + + " rounding mode -----"); + df5.applyPattern("#,###.#####"); + + for (int i = 0; i < values5FractDigits.length; i++) { + double d = values5FractDigits[i]; + String tiePosition = tieRelativePositions[i]; + String input = inputs5FractDigits[i]; + String expected = expected5FractDigits[r][i]; + + formatOutputTestDouble(df5, d, tiePosition, input, expected); + } + } + + // ==================== Testing long value cases ==================== + + System.out.println("\n===== testing long values ====="); + long l = 123456789012345L; + DecimalFormat dfLong = (DecimalFormat) NumberFormat.getInstance(Locale.US); + String tiePosition = "exact"; + String input = "123456789012345L"; + String expected = "123,456,789,012,345"; + String result = dfLong.format(l); + formatOutputTestLong(dfLong, l, tiePosition, input, expected); + + dfLong.applyPattern("0.###E0"); + expected = "1.235E14"; + formatOutputTestLong(dfLong, l, tiePosition, input, expected); + + l = 123450000000000L; + input = "123450000000000L"; + expected = "1.234E14"; + formatOutputTestLong(dfLong, l, tiePosition, input, expected); + + l = 987750000000000L; + input = "987750000000000L"; + expected = "9.878E14"; + formatOutputTestLong(dfLong, l, tiePosition, input, expected); + + dfLong.applyPattern("#,###.0E0"); + l = 987755000000000L; + input = "987755000000000L"; + expected = "987.76E12"; + + formatOutputTestLong(dfLong, l, tiePosition, input, expected); + + + // ================= Testing BigInteger value cases ================= + + System.out.println("\n===== testing BigInteger values ====="); + String stringValue = "12345678901234567890123456789012345"; + BigInteger bi = new BigInteger(stringValue); + DecimalFormat dfBig = (DecimalFormat) NumberFormat.getInstance(Locale.US); + tiePosition = "exact"; + input = stringValue; + expected = "12,345,678,901,234,567,890,123,456,789,012,345"; + formatOutputTestObject(dfBig, bi, tiePosition, input, expected); + + dfBig.applyPattern("0.###E0"); + expected = "1.235E34"; + formatOutputTestObject(dfBig, bi, tiePosition, input, expected); + + stringValue = "12345000000000000000000000000000000"; + input = stringValue; + bi = new BigInteger(stringValue); + expected = "1.234E34"; + formatOutputTestObject(dfBig, bi, tiePosition, input, expected); + + stringValue = "12345000000000000000000000000000001"; + input = stringValue; + bi = new BigInteger(stringValue); + expected = "1.235E34"; + formatOutputTestObject(dfBig, bi, tiePosition, input, expected); + + stringValue = "98755000000000000000000000000000000"; + input = stringValue; + bi = new BigInteger(stringValue); + expected = "9.876E34"; + formatOutputTestObject(dfBig, bi, tiePosition, input, expected); + + dfLong.applyPattern("#,###.0E0"); + stringValue = "98775500000000000000000000000000000"; + input = stringValue; + expected = "987.76E34"; + + // =============== Testing BigDecimal value cases ================ + + System.out.println("\n===== testing BigDecimal values ====="); + dfBig = (DecimalFormat) NumberFormat.getInstance(Locale.US); + + stringValue = "0.68850000000000000088817841970012523233890533447265625"; + BigDecimal bd = new BigDecimal(stringValue); + tiePosition = "exact"; + input = stringValue; + expected = "0.689"; + formatOutputTestObject(dfBig, bd, tiePosition, input, expected); + + stringValue = "0.31149999999999999911182158029987476766109466552734375"; + bd = new BigDecimal(stringValue); + dfBig.applyPattern("#,##0.####"); + tiePosition = "exact"; + input = stringValue; + expected = "0.3115"; + formatOutputTestObject(dfBig, bd, tiePosition, input, expected); + + // ==================== Printing results and exiting =================== + + System.out.println(); + System.out.println("==> " + testCounter + " tests passed successfully"); + System.out.println("==> " + errorCounter + " tests failed"); + + System.out.println(); + if (allPassed) { + System.out.println( + "Success in formating all the values with the given parameters"); + } else { + String s = "Test failed with " + errorCounter + " formating error(s)."; + System.out.println(s); + throw new RuntimeException(s); + } + } +} From a42478ecf417902fbb5dd100ffbe44d5dc38c1d6 Mon Sep 17 00:00:00 2001 From: Roman Kennke Date: Fri, 11 Jan 2013 16:47:23 -0800 Subject: [PATCH 016/138] 8005816: Shark: fix volatile float field access Reviewed-by: twisti --- hotspot/src/share/vm/shark/sharkBlock.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/hotspot/src/share/vm/shark/sharkBlock.cpp b/hotspot/src/share/vm/shark/sharkBlock.cpp index b4e98365efc..723b2d1dbc6 100644 --- a/hotspot/src/share/vm/shark/sharkBlock.cpp +++ b/hotspot/src/share/vm/shark/sharkBlock.cpp @@ -1044,10 +1044,17 @@ void SharkBlock::do_field_access(bool is_get, bool is_field) { BasicType basic_type = field->type()->basic_type(); Type *stack_type = SharkType::to_stackType(basic_type); Type *field_type = SharkType::to_arrayType(basic_type); - + Type *type = field_type; + if (field->is_volatile()) { + if (field_type == SharkType::jfloat_type()) { + type = SharkType::jint_type(); + } else if (field_type == SharkType::jdouble_type()) { + type = SharkType::jlong_type(); + } + } Value *addr = builder()->CreateAddressOfStructEntry( object, in_ByteSize(field->offset_in_bytes()), - PointerType::getUnqual(field_type), + PointerType::getUnqual(type), "addr"); // Do the access @@ -1055,6 +1062,7 @@ void SharkBlock::do_field_access(bool is_get, bool is_field) { Value* field_value; if (field->is_volatile()) { field_value = builder()->CreateAtomicLoad(addr); + field_value = builder()->CreateBitCast(field_value, field_type); } else { field_value = builder()->CreateLoad(addr); } @@ -1074,6 +1082,7 @@ void SharkBlock::do_field_access(bool is_get, bool is_field) { } if (field->is_volatile()) { + field_value = builder()->CreateBitCast(field_value, type); builder()->CreateAtomicStore(field_value, addr); } else { builder()->CreateStore(field_value, addr); From a0a0d0b65ef0dc2a055fac34fbad0b44e0a4067a Mon Sep 17 00:00:00 2001 From: Roman Kennke Date: Fri, 11 Jan 2013 16:47:23 -0800 Subject: [PATCH 017/138] 8005817: Shark: implement deoptimization support Reviewed-by: twisti --- hotspot/src/cpu/zero/vm/frame_zero.cpp | 18 ++++++++++++++---- hotspot/src/cpu/zero/vm/frame_zero.inline.hpp | 15 ++++++++++++--- hotspot/src/cpu/zero/vm/sharkFrame_zero.hpp | 4 ++++ hotspot/src/share/vm/shark/sharkInvariants.hpp | 8 +++++--- .../src/share/vm/shark/sharkTopLevelBlock.cpp | 7 ++++--- 5 files changed, 39 insertions(+), 13 deletions(-) diff --git a/hotspot/src/cpu/zero/vm/frame_zero.cpp b/hotspot/src/cpu/zero/vm/frame_zero.cpp index 8643af5953f..56f2a7a1c71 100644 --- a/hotspot/src/cpu/zero/vm/frame_zero.cpp +++ b/hotspot/src/cpu/zero/vm/frame_zero.cpp @@ -98,10 +98,20 @@ BasicObjectLock* frame::interpreter_frame_monitor_end() const { #endif // CC_INTERP void frame::patch_pc(Thread* thread, address pc) { - // We borrow this call to set the thread pointer in the interpreter - // state; the hook to set up deoptimized frames isn't supplied it. - assert(pc == NULL, "should be"); - get_interpreterState()->set_thread((JavaThread *) thread); + + if (pc != NULL) { + _cb = CodeCache::find_blob(pc); + SharkFrame* sharkframe = zeroframe()->as_shark_frame(); + sharkframe->set_pc(pc); + _pc = pc; + _deopt_state = is_deoptimized; + + } else { + // We borrow this call to set the thread pointer in the interpreter + // state; the hook to set up deoptimized frames isn't supplied it. + assert(pc == NULL, "should be"); + get_interpreterState()->set_thread((JavaThread *) thread); + } } bool frame::safe_for_sender(JavaThread *thread) { diff --git a/hotspot/src/cpu/zero/vm/frame_zero.inline.hpp b/hotspot/src/cpu/zero/vm/frame_zero.inline.hpp index 2bc703ae032..f6bd6d3c6be 100644 --- a/hotspot/src/cpu/zero/vm/frame_zero.inline.hpp +++ b/hotspot/src/cpu/zero/vm/frame_zero.inline.hpp @@ -45,27 +45,36 @@ inline frame::frame(ZeroFrame* zf, intptr_t* sp) { case ZeroFrame::ENTRY_FRAME: _pc = StubRoutines::call_stub_return_pc(); _cb = NULL; + _deopt_state = not_deoptimized; break; case ZeroFrame::INTERPRETER_FRAME: _pc = NULL; _cb = NULL; + _deopt_state = not_deoptimized; break; - case ZeroFrame::SHARK_FRAME: + case ZeroFrame::SHARK_FRAME: { _pc = zero_sharkframe()->pc(); _cb = CodeCache::find_blob_unsafe(pc()); + address original_pc = nmethod::get_deopt_original_pc(this); + if (original_pc != NULL) { + _pc = original_pc; + _deopt_state = is_deoptimized; + } else { + _deopt_state = not_deoptimized; + } break; - + } case ZeroFrame::FAKE_STUB_FRAME: _pc = NULL; _cb = NULL; + _deopt_state = not_deoptimized; break; default: ShouldNotReachHere(); } - _deopt_state = not_deoptimized; } // Accessors diff --git a/hotspot/src/cpu/zero/vm/sharkFrame_zero.hpp b/hotspot/src/cpu/zero/vm/sharkFrame_zero.hpp index 6c67d0eb1d8..505241b147f 100644 --- a/hotspot/src/cpu/zero/vm/sharkFrame_zero.hpp +++ b/hotspot/src/cpu/zero/vm/sharkFrame_zero.hpp @@ -68,6 +68,10 @@ class SharkFrame : public ZeroFrame { return (address) value_of_word(pc_off); } + void set_pc(address pc) const { + *((address*) addr_of_word(pc_off)) = pc; + } + intptr_t* unextended_sp() const { return (intptr_t *) value_of_word(unextended_sp_off); } diff --git a/hotspot/src/share/vm/shark/sharkInvariants.hpp b/hotspot/src/share/vm/shark/sharkInvariants.hpp index 50e1be8ea6d..8e9d0aec062 100644 --- a/hotspot/src/share/vm/shark/sharkInvariants.hpp +++ b/hotspot/src/share/vm/shark/sharkInvariants.hpp @@ -99,13 +99,15 @@ class SharkCompileInvariants : public ResourceObj { DebugInformationRecorder* debug_info() const { return env()->debug_info(); } - Dependencies* dependencies() const { - return env()->dependencies(); - } SharkCodeBuffer* code_buffer() const { return builder()->code_buffer(); } + public: + Dependencies* dependencies() const { + return env()->dependencies(); + } + // Commonly used classes protected: ciInstanceKlass* java_lang_Object_klass() const { diff --git a/hotspot/src/share/vm/shark/sharkTopLevelBlock.cpp b/hotspot/src/share/vm/shark/sharkTopLevelBlock.cpp index fcd6906caad..a3f90b058bf 100644 --- a/hotspot/src/share/vm/shark/sharkTopLevelBlock.cpp +++ b/hotspot/src/share/vm/shark/sharkTopLevelBlock.cpp @@ -1030,7 +1030,6 @@ ciMethod* SharkTopLevelBlock::improve_virtual_call(ciMethod* caller, dest_method->holder() == java_lang_Object_klass()) return dest_method; -#ifdef SHARK_CAN_DEOPTIMIZE_ANYWHERE // This code can replace a virtual call with a direct call if this // class is the only one in the entire set of loaded classes that // implements this method. This makes the compiled code dependent @@ -1064,6 +1063,8 @@ ciMethod* SharkTopLevelBlock::improve_virtual_call(ciMethod* caller, if (monomorphic_target != NULL) { assert(!monomorphic_target->is_abstract(), "shouldn't be"); + function()->dependencies()->assert_unique_concrete_method(actual_receiver, monomorphic_target); + // Opto has a bunch of type checking here that I don't // understand. It's to inhibit casting in one direction, // possibly because objects in Opto can have inexact @@ -1097,7 +1098,6 @@ ciMethod* SharkTopLevelBlock::improve_virtual_call(ciMethod* caller, // with non-monomorphic targets if the receiver has an exact // type. We don't mark types this way, so we can't do this. -#endif // SHARK_CAN_DEOPTIMIZE_ANYWHERE return NULL; } @@ -1298,8 +1298,9 @@ void SharkTopLevelBlock::do_call() { // Try to inline the call if (!call_is_virtual) { - if (SharkInliner::attempt_inline(call_method, current_state())) + if (SharkInliner::attempt_inline(call_method, current_state())) { return; + } } // Find the method we are calling From ba649f4203f5d53ce5f0475156c87dc741a7869d Mon Sep 17 00:00:00 2001 From: Roman Kennke Date: Fri, 11 Jan 2013 16:47:23 -0800 Subject: [PATCH 018/138] 8005818: Shark: fix OSR for non-empty incoming stack Reviewed-by: twisti --- hotspot/src/share/vm/shark/sharkCompiler.cpp | 3 +++ hotspot/src/share/vm/shark/sharkFunction.cpp | 4 ++++ hotspot/src/share/vm/shark/sharkInvariants.hpp | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/hotspot/src/share/vm/shark/sharkCompiler.cpp b/hotspot/src/share/vm/shark/sharkCompiler.cpp index 3438240ad00..cde6bc23e76 100644 --- a/hotspot/src/share/vm/shark/sharkCompiler.cpp +++ b/hotspot/src/share/vm/shark/sharkCompiler.cpp @@ -185,6 +185,9 @@ void SharkCompiler::compile_method(ciEnv* env, // Build the LLVM IR for the method Function *function = SharkFunction::build(env, &builder, flow, name); + if (env->failing()) { + return; + } // Generate native code. It's unpleasant that we have to drop into // the VM to do this -- it blocks safepoints -- but I can't see any diff --git a/hotspot/src/share/vm/shark/sharkFunction.cpp b/hotspot/src/share/vm/shark/sharkFunction.cpp index 917ed0109ac..1ec7a371fb4 100644 --- a/hotspot/src/share/vm/shark/sharkFunction.cpp +++ b/hotspot/src/share/vm/shark/sharkFunction.cpp @@ -77,6 +77,10 @@ void SharkFunction::initialize(const char *name) { // Walk the tree from the start block to determine which // blocks are entered and which blocks require phis SharkTopLevelBlock *start_block = block(flow()->start_block_num()); + if (is_osr() && start_block->stack_depth_at_entry() != 0) { + env()->record_method_not_compilable("can't compile OSR block with incoming stack-depth > 0"); + return; + } assert(start_block->start() == flow()->start_bci(), "blocks out of order"); start_block->enter(); diff --git a/hotspot/src/share/vm/shark/sharkInvariants.hpp b/hotspot/src/share/vm/shark/sharkInvariants.hpp index 8e9d0aec062..e6b6399fe26 100644 --- a/hotspot/src/share/vm/shark/sharkInvariants.hpp +++ b/hotspot/src/share/vm/shark/sharkInvariants.hpp @@ -68,7 +68,7 @@ class SharkCompileInvariants : public ResourceObj { // // Accessing this directly is kind of ugly, so it's private. Add // new accessors below if you need something from it. - private: + protected: ciEnv* env() const { assert(_env != NULL, "env not available"); return _env; From b17ebac5b54b3e1be6199cd71c63843c771836dc Mon Sep 17 00:00:00 2001 From: Roman Kennke Date: Fri, 11 Jan 2013 16:47:23 -0800 Subject: [PATCH 019/138] 8005820: Shark: enable JSR292 support Reviewed-by: twisti --- .../share/vm/compiler/abstractCompiler.hpp | 1 + .../src/share/vm/compiler/compileBroker.cpp | 2 +- hotspot/src/share/vm/shark/sharkBlock.cpp | 2 +- hotspot/src/share/vm/shark/sharkCompiler.hpp | 3 +++ hotspot/src/share/vm/shark/sharkConstant.cpp | 7 +++++- hotspot/src/share/vm/shark/sharkInliner.cpp | 2 +- .../src/share/vm/shark/sharkTopLevelBlock.cpp | 24 ++++++++++++++++++- 7 files changed, 36 insertions(+), 5 deletions(-) diff --git a/hotspot/src/share/vm/compiler/abstractCompiler.hpp b/hotspot/src/share/vm/compiler/abstractCompiler.hpp index 9007a82fc49..96453e7d9ba 100644 --- a/hotspot/src/share/vm/compiler/abstractCompiler.hpp +++ b/hotspot/src/share/vm/compiler/abstractCompiler.hpp @@ -50,6 +50,7 @@ class AbstractCompiler : public CHeapObj { // Missing feature tests virtual bool supports_native() { return true; } virtual bool supports_osr () { return true; } + virtual bool can_compile_method(methodHandle method) { return true; } #if defined(TIERED) || ( !defined(COMPILER1) && !defined(COMPILER2) && !defined(SHARK)) virtual bool is_c1 () { return false; } virtual bool is_c2 () { return false; } diff --git a/hotspot/src/share/vm/compiler/compileBroker.cpp b/hotspot/src/share/vm/compiler/compileBroker.cpp index 73ab865dc74..57389701d82 100644 --- a/hotspot/src/share/vm/compiler/compileBroker.cpp +++ b/hotspot/src/share/vm/compiler/compileBroker.cpp @@ -1218,7 +1218,7 @@ nmethod* CompileBroker::compile_method(methodHandle method, int osr_bci, // lock, make sure that the compilation // isn't prohibited in a straightforward way. - if (compiler(comp_level) == NULL || compilation_is_prohibited(method, osr_bci, comp_level)) { + if (compiler(comp_level) == NULL || !compiler(comp_level)->can_compile_method(method) || compilation_is_prohibited(method, osr_bci, comp_level)) { return NULL; } diff --git a/hotspot/src/share/vm/shark/sharkBlock.cpp b/hotspot/src/share/vm/shark/sharkBlock.cpp index 723b2d1dbc6..6b1b5fbe89c 100644 --- a/hotspot/src/share/vm/shark/sharkBlock.cpp +++ b/hotspot/src/share/vm/shark/sharkBlock.cpp @@ -1032,7 +1032,7 @@ void SharkBlock::do_field_access(bool is_get, bool is_field) { check_null(value); object = value->generic_value(); } - if (is_get && field->is_constant()) { + if (is_get && field->is_constant() && field->is_static()) { SharkConstant *constant = SharkConstant::for_field(iter()); if (constant->is_loaded()) value = constant->value(builder()); diff --git a/hotspot/src/share/vm/shark/sharkCompiler.hpp b/hotspot/src/share/vm/shark/sharkCompiler.hpp index 828a783a80d..7e530c142aa 100644 --- a/hotspot/src/share/vm/shark/sharkCompiler.hpp +++ b/hotspot/src/share/vm/shark/sharkCompiler.hpp @@ -46,6 +46,9 @@ class SharkCompiler : public AbstractCompiler { // Missing feature tests bool supports_native() { return true; } bool supports_osr() { return true; } + bool can_compile_method(methodHandle method) { + return ! (method->is_method_handle_intrinsic() || method->is_compiled_lambda_form()); + } // Customization bool needs_adapters() { return false; } diff --git a/hotspot/src/share/vm/shark/sharkConstant.cpp b/hotspot/src/share/vm/shark/sharkConstant.cpp index b45ec136e77..cd6e1152d0f 100644 --- a/hotspot/src/share/vm/shark/sharkConstant.cpp +++ b/hotspot/src/share/vm/shark/sharkConstant.cpp @@ -37,7 +37,12 @@ SharkConstant* SharkConstant::for_ldc(ciBytecodeStream *iter) { ciType *type = NULL; if (constant.basic_type() == T_OBJECT) { ciEnv *env = ciEnv::current(); - assert(constant.as_object()->klass() == env->String_klass() || constant.as_object()->klass() == env->Class_klass(), "should be"); + + assert(constant.as_object()->klass() == env->String_klass() + || constant.as_object()->klass() == env->Class_klass() + || constant.as_object()->klass()->is_subtype_of(env->MethodType_klass()) + || constant.as_object()->klass()->is_subtype_of(env->MethodHandle_klass()), "should be"); + type = constant.as_object()->klass(); } return new SharkConstant(constant, type); diff --git a/hotspot/src/share/vm/shark/sharkInliner.cpp b/hotspot/src/share/vm/shark/sharkInliner.cpp index c9e895a9c9b..1f4ea829fb3 100644 --- a/hotspot/src/share/vm/shark/sharkInliner.cpp +++ b/hotspot/src/share/vm/shark/sharkInliner.cpp @@ -725,7 +725,7 @@ bool SharkInlinerHelper::do_field_access(bool is_get, bool is_field) { // Push the result if necessary if (is_get) { bool result_pushed = false; - if (field->is_constant()) { + if (field->is_constant() && field->is_static()) { SharkConstant *sc = SharkConstant::for_field(iter()); if (sc->is_loaded()) { push(sc->is_nonzero()); diff --git a/hotspot/src/share/vm/shark/sharkTopLevelBlock.cpp b/hotspot/src/share/vm/shark/sharkTopLevelBlock.cpp index a3f90b058bf..6614146bb42 100644 --- a/hotspot/src/share/vm/shark/sharkTopLevelBlock.cpp +++ b/hotspot/src/share/vm/shark/sharkTopLevelBlock.cpp @@ -113,7 +113,19 @@ void SharkTopLevelBlock::scan_for_traps() { ciSignature* sig; method = iter()->get_method(will_link, &sig); assert(will_link, "typeflow responsibility"); - + // We can't compile calls to method handle intrinsics, because we use + // the interpreter entry points and they expect the top frame to be an + // interpreter frame. We need to implement the intrinsics for Shark. + if (method->is_method_handle_intrinsic() || method->is_compiled_lambda_form()) { + if (SharkPerformanceWarnings) { + warning("JSR292 optimization not yet implemented in Shark"); + } + set_trap( + Deoptimization::make_trap_request( + Deoptimization::Reason_unhandled, + Deoptimization::Action_make_not_compilable), bci()); + return; + } if (!method->holder()->is_linked()) { set_trap( Deoptimization::make_trap_request( @@ -158,6 +170,16 @@ void SharkTopLevelBlock::scan_for_traps() { return; } break; + case Bytecodes::_invokedynamic: + case Bytecodes::_invokehandle: + if (SharkPerformanceWarnings) { + warning("JSR292 optimization not yet implemented in Shark"); + } + set_trap( + Deoptimization::make_trap_request( + Deoptimization::Reason_unhandled, + Deoptimization::Action_make_not_compilable), bci()); + return; } } From fb60d01e3632b5f81633db04881c64bcea809c19 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Fri, 11 Jan 2013 16:50:34 -0800 Subject: [PATCH 020/138] 8006123: Support @Contended Annotation - JEP 142 (jdk part) Jdk changes for 8003895. Reviewed-by: darcy, jrose, coleenp, dholmes, kvn --- jdk/src/share/classes/sun/misc/Contended.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 jdk/src/share/classes/sun/misc/Contended.java diff --git a/jdk/src/share/classes/sun/misc/Contended.java b/jdk/src/share/classes/sun/misc/Contended.java new file mode 100644 index 00000000000..6925b4242d6 --- /dev/null +++ b/jdk/src/share/classes/sun/misc/Contended.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation marks classes and fields as considered to be contended. + * @since 1.8 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.TYPE}) +public @interface Contended { + + /** + Defines the contention group tag. + */ + String value() default ""; +} From 827930b510dd8ae13b0dd42f97f499c4772384f6 Mon Sep 17 00:00:00 2001 From: Christian Thalinger Date: Fri, 11 Jan 2013 20:01:16 -0800 Subject: [PATCH 021/138] 8006127: remove printing code added with 8006031 Reviewed-by: kvn --- hotspot/src/share/vm/opto/library_call.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/hotspot/src/share/vm/opto/library_call.cpp b/hotspot/src/share/vm/opto/library_call.cpp index b2138abe931..406f605853d 100644 --- a/hotspot/src/share/vm/opto/library_call.cpp +++ b/hotspot/src/share/vm/opto/library_call.cpp @@ -3559,7 +3559,6 @@ bool LibraryCallKit::inline_native_getLength() { // public static T[] java.util.Arrays.copyOf( U[] original, int newLength, Class newType); // public static T[] java.util.Arrays.copyOfRange(U[] original, int from, int to, Class newType); bool LibraryCallKit::inline_array_copyOf(bool is_copyOfRange) { - tty->print_cr("LibraryCallKit::inline_array_copyOf: %d", is_copyOfRange); if (too_many_traps(Deoptimization::Reason_intrinsic)) return false; // Get the arguments. From 7428b2f902dd6ed825aff65c1c73d07239c0d9ba Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Fri, 11 Jan 2013 22:43:29 -0800 Subject: [PATCH 022/138] 8005466: JAR file entry hash table uses too much memory (zlib_util.c) Realign the fields of jzcell struct Reviewed-by: sherman --- jdk/src/share/native/java/util/zip/zip_util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jdk/src/share/native/java/util/zip/zip_util.h b/jdk/src/share/native/java/util/zip/zip_util.h index 5771e86320f..5782eaacf40 100644 --- a/jdk/src/share/native/java/util/zip/zip_util.h +++ b/jdk/src/share/native/java/util/zip/zip_util.h @@ -177,8 +177,8 @@ typedef struct jzentry { /* Zip file entry */ */ typedef struct jzcell { unsigned int hash; /* 32 bit hashcode on name */ - jlong cenpos; /* Offset of central directory file header */ unsigned int next; /* hash chain: index into jzfile->entries */ + jlong cenpos; /* Offset of central directory file header */ } jzcell; typedef struct cencache { From 7a8ddfee995e3e914bab20e21e75e6e043f11a1e Mon Sep 17 00:00:00 2001 From: Mark Sheppard Date: Sun, 13 Jan 2013 22:09:50 +0000 Subject: [PATCH 023/138] 8006153: HTTP protocol handler authenication should use Base64 API Reviewed-by: chegar, alanb --- .../www/protocol/http/BasicAuthentication.java | 16 +++++----------- .../protocol/http/NegotiateAuthentication.java | 13 +++---------- 2 files changed, 8 insertions(+), 21 deletions(-) diff --git a/jdk/src/share/classes/sun/net/www/protocol/http/BasicAuthentication.java b/jdk/src/share/classes/sun/net/www/protocol/http/BasicAuthentication.java index 706037223fd..50de0383952 100644 --- a/jdk/src/share/classes/sun/net/www/protocol/http/BasicAuthentication.java +++ b/jdk/src/share/classes/sun/net/www/protocol/http/BasicAuthentication.java @@ -31,8 +31,9 @@ import java.net.URISyntaxException; import java.net.PasswordAuthentication; import java.io.IOException; import java.io.OutputStream; +import java.util.Base64; +import java.util.Base64.Encoder; import sun.net.www.HeaderParser; -import sun.misc.BASE64Encoder; /** * BasicAuthentication: Encapsulate an http server authentication using @@ -76,7 +77,7 @@ class BasicAuthentication extends AuthenticationInfo { System.arraycopy(nameBytes, 0, concat, 0, nameBytes.length); System.arraycopy(passwdBytes, 0, concat, nameBytes.length, passwdBytes.length); - this.auth = "Basic " + (new BasicBASE64Encoder()).encode(concat); + this.auth = "Basic " + Base64.getEncoder().encodeToString(concat); this.pw = pw; } @@ -116,7 +117,7 @@ class BasicAuthentication extends AuthenticationInfo { System.arraycopy(nameBytes, 0, concat, 0, nameBytes.length); System.arraycopy(passwdBytes, 0, concat, nameBytes.length, passwdBytes.length); - this.auth = "Basic " + (new BasicBASE64Encoder()).encode(concat); + this.auth = "Basic " + Base64.getEncoder().encodeToString(concat); this.pw = pw; } @@ -201,12 +202,5 @@ class BasicAuthentication extends AuthenticationInfo { /*should not reach here. If we do simply return npath*/ return npath; } - - /* It is never expected that the header value will exceed the bytesPerLine */ - private class BasicBASE64Encoder extends BASE64Encoder { - @Override - protected int bytesPerLine() { - return (10000); - } - } } + diff --git a/jdk/src/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java b/jdk/src/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java index 23a8a83ca94..24e99044c42 100644 --- a/jdk/src/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java +++ b/jdk/src/share/classes/sun/net/www/protocol/http/NegotiateAuthentication.java @@ -28,10 +28,9 @@ package sun.net.www.protocol.http; import java.net.URL; import java.io.IOException; import java.net.Authenticator.RequestorType; +import java.util.Base64; import java.util.HashMap; import sun.net.www.HeaderParser; -import sun.misc.BASE64Decoder; -import sun.misc.BASE64Encoder; import static sun.net.www.protocol.http.AuthScheme.NEGOTIATE; import static sun.net.www.protocol.http.AuthScheme.KERBEROS; @@ -151,9 +150,9 @@ class NegotiateAuthentication extends AuthenticationInfo { byte[] incoming = null; String[] parts = raw.split("\\s+"); if (parts.length > 1) { - incoming = new BASE64Decoder().decodeBuffer(parts[1]); + incoming = Base64.getDecoder().decode(parts[1]); } - response = hci.scheme + " " + new B64Encoder().encode( + response = hci.scheme + " " + Base64.getEncoder().encodeToString( incoming==null?firstToken():nextToken(incoming)); conn.setAuthenticationProperty(getHeaderName(), response); @@ -201,12 +200,6 @@ class NegotiateAuthentication extends AuthenticationInfo { return negotiator.nextToken(token); } - class B64Encoder extends BASE64Encoder { - protected int bytesPerLine () { - return 100000; // as big as it can be, maybe INT_MAX - } - } - // MS will send a final WWW-Authenticate even if the status is already // 200 OK. The token can be fed into initSecContext() again to determine // if the server can be trusted. This is not the same concept as Digest's From e9127734518e07e781ddd5cc9d638c3661ff89e0 Mon Sep 17 00:00:00 2001 From: Peter Levart Date: Sun, 13 Jan 2013 19:57:06 -0500 Subject: [PATCH 024/138] 8005232: (JEP-149) Class Instance size reduction Moved the fields for cached reflection objects into a seperate ReflectionData object to reduce dynamic footprint. Reviewed-by: dholmes, mchung, shade --- jdk/src/share/classes/java/lang/Class.java | 219 ++++++++++++--------- 1 file changed, 126 insertions(+), 93 deletions(-) diff --git a/jdk/src/share/classes/java/lang/Class.java b/jdk/src/share/classes/java/lang/Class.java index e1ac4029985..094f0cf26d6 100644 --- a/jdk/src/share/classes/java/lang/Class.java +++ b/jdk/src/share/classes/java/lang/Class.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2214,39 +2214,89 @@ public final // Caches for certain reflective results private static boolean useCaches = true; - private volatile transient SoftReference declaredFields; - private volatile transient SoftReference publicFields; - private volatile transient SoftReference declaredMethods; - private volatile transient SoftReference publicMethods; - private volatile transient SoftReference[]> declaredConstructors; - private volatile transient SoftReference[]> publicConstructors; - // Intermediate results for getFields and getMethods - private volatile transient SoftReference declaredPublicFields; - private volatile transient SoftReference declaredPublicMethods; + + // reflection data that might get invalidated when JVM TI RedefineClasses() is called + static class ReflectionData { + volatile Field[] declaredFields; + volatile Field[] publicFields; + volatile Method[] declaredMethods; + volatile Method[] publicMethods; + volatile Constructor[] declaredConstructors; + volatile Constructor[] publicConstructors; + // Intermediate results for getFields and getMethods + volatile Field[] declaredPublicFields; + volatile Method[] declaredPublicMethods; + // Value of classRedefinedCount when we created this ReflectionData instance + final int redefinedCount; + + ReflectionData(int redefinedCount) { + this.redefinedCount = redefinedCount; + } + + // initialize Unsafe machinery here, since we need to call Class.class instance method + // and have to avoid calling it in the static initializer of the Class class... + private static final Unsafe unsafe; + // offset of Class.reflectionData instance field + private static final long reflectionDataOffset; + + static { + unsafe = Unsafe.getUnsafe(); + // bypass caches + Field reflectionDataField = searchFields(Class.class.getDeclaredFields0(false), + "reflectionData"); + if (reflectionDataField == null) { + throw new Error("No reflectionData field found in java.lang.Class"); + } + reflectionDataOffset = unsafe.objectFieldOffset(reflectionDataField); + } + + static boolean compareAndSwap(Class clazz, + SoftReference> oldData, + SoftReference> newData) { + return unsafe.compareAndSwapObject(clazz, reflectionDataOffset, oldData, newData); + } + } + + private volatile transient SoftReference> reflectionData; // Incremented by the VM on each call to JVM TI RedefineClasses() // that redefines this class or a superclass. private volatile transient int classRedefinedCount = 0; - // Value of classRedefinedCount when we last cleared the cached values - // that are sensitive to class redefinition. - private volatile transient int lastRedefinedCount = 0; + // Lazily create and cache ReflectionData + private ReflectionData reflectionData() { + SoftReference> reflectionData = this.reflectionData; + int classRedefinedCount = this.classRedefinedCount; + ReflectionData rd; + if (useCaches && + reflectionData != null && + (rd = reflectionData.get()) != null && + rd.redefinedCount == classRedefinedCount) { + return rd; + } + // else no SoftReference or cleared SoftReference or stale ReflectionData + // -> create and replace new instance + return newReflectionData(reflectionData, classRedefinedCount); + } - // Clears cached values that might possibly have been obsoleted by - // a class redefinition. - private void clearCachesOnClassRedefinition() { - if (lastRedefinedCount != classRedefinedCount) { - declaredFields = publicFields = declaredPublicFields = null; - declaredMethods = publicMethods = declaredPublicMethods = null; - declaredConstructors = publicConstructors = null; - annotations = declaredAnnotations = null; + private ReflectionData newReflectionData(SoftReference> oldReflectionData, + int classRedefinedCount) { + if (!useCaches) return null; - // Use of "volatile" (and synchronization by caller in the case - // of annotations) ensures that no thread sees the update to - // lastRedefinedCount before seeing the caches cleared. - // We do not guard against brief windows during which multiple - // threads might redundantly work to fill an empty cache. - lastRedefinedCount = classRedefinedCount; + while (true) { + ReflectionData rd = new ReflectionData<>(classRedefinedCount); + // try to CAS it... + if (ReflectionData.compareAndSwap(this, oldReflectionData, new SoftReference<>(rd))) { + return rd; + } + // else retry + oldReflectionData = this.reflectionData; + classRedefinedCount = this.classRedefinedCount; + if (oldReflectionData != null && + (rd = oldReflectionData.get()) != null && + rd.redefinedCount == classRedefinedCount) { + return rd; + } } } @@ -2289,27 +2339,19 @@ public final // via ReflectionFactory.copyField. private Field[] privateGetDeclaredFields(boolean publicOnly) { checkInitted(); - Field[] res = null; - if (useCaches) { - clearCachesOnClassRedefinition(); - if (publicOnly) { - if (declaredPublicFields != null) { - res = declaredPublicFields.get(); - } - } else { - if (declaredFields != null) { - res = declaredFields.get(); - } - } + Field[] res; + ReflectionData rd = reflectionData(); + if (rd != null) { + res = publicOnly ? rd.declaredPublicFields : rd.declaredFields; if (res != null) return res; } // No cached value available; request value from VM res = Reflection.filterFields(this, getDeclaredFields0(publicOnly)); - if (useCaches) { + if (rd != null) { if (publicOnly) { - declaredPublicFields = new SoftReference<>(res); + rd.declaredPublicFields = res; } else { - declaredFields = new SoftReference<>(res); + rd.declaredFields = res; } } return res; @@ -2320,12 +2362,10 @@ public final // via ReflectionFactory.copyField. private Field[] privateGetPublicFields(Set> traversedInterfaces) { checkInitted(); - Field[] res = null; - if (useCaches) { - clearCachesOnClassRedefinition(); - if (publicFields != null) { - res = publicFields.get(); - } + Field[] res; + ReflectionData rd = reflectionData(); + if (rd != null) { + res = rd.publicFields; if (res != null) return res; } @@ -2358,8 +2398,8 @@ public final res = new Field[fields.size()]; fields.toArray(res); - if (useCaches) { - publicFields = new SoftReference<>(res); + if (rd != null) { + rd.publicFields = res; } return res; } @@ -2382,18 +2422,10 @@ public final // instead be copied via ReflectionFactory.copyConstructor. private Constructor[] privateGetDeclaredConstructors(boolean publicOnly) { checkInitted(); - Constructor[] res = null; - if (useCaches) { - clearCachesOnClassRedefinition(); - if (publicOnly) { - if (publicConstructors != null) { - res = publicConstructors.get(); - } - } else { - if (declaredConstructors != null) { - res = declaredConstructors.get(); - } - } + Constructor[] res; + ReflectionData rd = reflectionData(); + if (rd != null) { + res = publicOnly ? rd.publicConstructors : rd.declaredConstructors; if (res != null) return res; } // No cached value available; request value from VM @@ -2404,11 +2436,11 @@ public final } else { res = getDeclaredConstructors0(publicOnly); } - if (useCaches) { + if (rd != null) { if (publicOnly) { - publicConstructors = new SoftReference<>(res); + rd.publicConstructors = res; } else { - declaredConstructors = new SoftReference<>(res); + rd.declaredConstructors = res; } } return res; @@ -2425,27 +2457,19 @@ public final // via ReflectionFactory.copyMethod. private Method[] privateGetDeclaredMethods(boolean publicOnly) { checkInitted(); - Method[] res = null; - if (useCaches) { - clearCachesOnClassRedefinition(); - if (publicOnly) { - if (declaredPublicMethods != null) { - res = declaredPublicMethods.get(); - } - } else { - if (declaredMethods != null) { - res = declaredMethods.get(); - } - } + Method[] res; + ReflectionData rd = reflectionData(); + if (rd != null) { + res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods; if (res != null) return res; } // No cached value available; request value from VM res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly)); - if (useCaches) { + if (rd != null) { if (publicOnly) { - declaredPublicMethods = new SoftReference<>(res); + rd.declaredPublicMethods = res; } else { - declaredMethods = new SoftReference<>(res); + rd.declaredMethods = res; } } return res; @@ -2547,12 +2571,10 @@ public final // via ReflectionFactory.copyMethod. private Method[] privateGetPublicMethods() { checkInitted(); - Method[] res = null; - if (useCaches) { - clearCachesOnClassRedefinition(); - if (publicMethods != null) { - res = publicMethods.get(); - } + Method[] res; + ReflectionData rd = reflectionData(); + if (rd != null) { + res = rd.publicMethods; if (res != null) return res; } @@ -2560,7 +2582,7 @@ public final // Start by fetching public declared methods MethodArray methods = new MethodArray(); { - Method[] tmp = privateGetDeclaredMethods(true); + Method[] tmp = privateGetDeclaredMethods(true); methods.addAll(tmp); } // Now recur over superclass and direct superinterfaces. @@ -2600,8 +2622,8 @@ public final methods.addAllIfNotPresent(inheritedMethods); methods.compactAndTrim(); res = methods.getArray(); - if (useCaches) { - publicMethods = new SoftReference<>(res); + if (rd != null) { + rd.publicMethods = res; } return res; } @@ -2611,7 +2633,7 @@ public final // Helpers for fetchers of one field, method, or constructor // - private Field searchFields(Field[] fields, String name) { + private static Field searchFields(Field[] fields, String name) { String internedName = name.intern(); for (int i = 0; i < fields.length; i++) { if (fields[i].getName() == internedName) { @@ -2629,7 +2651,7 @@ public final // of Field objects which have to be created for the common // case where the field being requested is declared in the // class which is being queried. - Field res = null; + Field res; // Search declared public fields if ((res = searchFields(privateGetDeclaredFields(true), name)) != null) { return res; @@ -2681,7 +2703,7 @@ public final // number of Method objects which have to be created for the // common case where the method being requested is declared in // the class which is being queried. - Method res = null; + Method res; // Search declared public methods if ((res = searchMethods(privateGetDeclaredMethods(true), name, @@ -3125,9 +3147,20 @@ public final // Annotations cache private transient Map, Annotation> annotations; private transient Map, Annotation> declaredAnnotations; + // Value of classRedefinedCount when we last cleared the cached annotations and declaredAnnotations fields + private transient int lastAnnotationsRedefinedCount = 0; + + // Clears cached values that might possibly have been obsoleted by + // a class redefinition. + private void clearAnnotationCachesOnClassRedefinition() { + if (lastAnnotationsRedefinedCount != classRedefinedCount) { + annotations = declaredAnnotations = null; + lastAnnotationsRedefinedCount = classRedefinedCount; + } + } private synchronized void initAnnotationsIfNecessary() { - clearCachesOnClassRedefinition(); + clearAnnotationCachesOnClassRedefinition(); if (annotations != null) return; declaredAnnotations = AnnotationParser.parseAnnotations( From a3639fdea176da5d87615a58ef294d5f5cb0bc5c Mon Sep 17 00:00:00 2001 From: Erik Helin Date: Mon, 14 Jan 2013 09:58:52 +0100 Subject: [PATCH 025/138] 8004018: Remove old initialization flags Reviewed-by: dholmes, stefank --- hotspot/src/share/vm/runtime/globals.hpp | 12 ---- hotspot/src/share/vm/runtime/thread.cpp | 77 +++++++++--------------- 2 files changed, 29 insertions(+), 60 deletions(-) diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp index 7ea96627e82..61aee4a9060 100644 --- a/hotspot/src/share/vm/runtime/globals.hpp +++ b/hotspot/src/share/vm/runtime/globals.hpp @@ -969,18 +969,6 @@ class CommandLineFlags { notproduct(uintx, WarnOnStalledSpinLock, 0, \ "Prints warnings for stalled SpinLocks") \ \ - develop(bool, InitializeJavaLangSystem, true, \ - "Initialize java.lang.System - turn off for individual " \ - "method debugging") \ - \ - develop(bool, InitializeJavaLangString, true, \ - "Initialize java.lang.String - turn off for individual " \ - "method debugging") \ - \ - develop(bool, InitializeJavaLangExceptionsErrors, true, \ - "Initialize various error and exception classes - turn off for " \ - "individual method debugging") \ - \ product(bool, RegisterFinalizersAtInit, true, \ "Register finalizable objects at end of Object. or " \ "after allocation") \ diff --git a/hotspot/src/share/vm/runtime/thread.cpp b/hotspot/src/share/vm/runtime/thread.cpp index db3cace3d64..5be1615e1be 100644 --- a/hotspot/src/share/vm/runtime/thread.cpp +++ b/hotspot/src/share/vm/runtime/thread.cpp @@ -1716,7 +1716,6 @@ static void ensure_join(JavaThread* thread) { // cleanup_failed_attach_current_thread as well. void JavaThread::exit(bool destroy_vm, ExitType exit_type) { assert(this == JavaThread::current(), "thread consistency check"); - if (!InitializeJavaLangSystem) return; HandleMark hm(this); Handle uncaught_exception(this, this->pending_exception()); @@ -3469,11 +3468,7 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { create_vm_init_libraries(); } - if (InitializeJavaLangString) { - initialize_class(vmSymbols::java_lang_String(), CHECK_0); - } else { - warning("java.lang.String not initialized"); - } + initialize_class(vmSymbols::java_lang_String(), CHECK_0); if (AggressiveOpts) { { @@ -3514,53 +3509,39 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { } // Initialize java_lang.System (needed before creating the thread) - if (InitializeJavaLangSystem) { - initialize_class(vmSymbols::java_lang_System(), CHECK_0); - initialize_class(vmSymbols::java_lang_ThreadGroup(), CHECK_0); - Handle thread_group = create_initial_thread_group(CHECK_0); - Universe::set_main_thread_group(thread_group()); - initialize_class(vmSymbols::java_lang_Thread(), CHECK_0); - oop thread_object = create_initial_thread(thread_group, main_thread, CHECK_0); - main_thread->set_threadObj(thread_object); - // Set thread status to running since main thread has - // been started and running. - java_lang_Thread::set_thread_status(thread_object, - java_lang_Thread::RUNNABLE); + initialize_class(vmSymbols::java_lang_System(), CHECK_0); + initialize_class(vmSymbols::java_lang_ThreadGroup(), CHECK_0); + Handle thread_group = create_initial_thread_group(CHECK_0); + Universe::set_main_thread_group(thread_group()); + initialize_class(vmSymbols::java_lang_Thread(), CHECK_0); + oop thread_object = create_initial_thread(thread_group, main_thread, CHECK_0); + main_thread->set_threadObj(thread_object); + // Set thread status to running since main thread has + // been started and running. + java_lang_Thread::set_thread_status(thread_object, + java_lang_Thread::RUNNABLE); - // The VM creates & returns objects of this class. Make sure it's initialized. - initialize_class(vmSymbols::java_lang_Class(), CHECK_0); + // The VM creates & returns objects of this class. Make sure it's initialized. + initialize_class(vmSymbols::java_lang_Class(), CHECK_0); - // The VM preresolves methods to these classes. Make sure that they get initialized - initialize_class(vmSymbols::java_lang_reflect_Method(), CHECK_0); - initialize_class(vmSymbols::java_lang_ref_Finalizer(), CHECK_0); - call_initializeSystemClass(CHECK_0); + // The VM preresolves methods to these classes. Make sure that they get initialized + initialize_class(vmSymbols::java_lang_reflect_Method(), CHECK_0); + initialize_class(vmSymbols::java_lang_ref_Finalizer(), CHECK_0); + call_initializeSystemClass(CHECK_0); - // get the Java runtime name after java.lang.System is initialized - JDK_Version::set_runtime_name(get_java_runtime_name(THREAD)); - JDK_Version::set_runtime_version(get_java_runtime_version(THREAD)); - } else { - warning("java.lang.System not initialized"); - } + // get the Java runtime name after java.lang.System is initialized + JDK_Version::set_runtime_name(get_java_runtime_name(THREAD)); + JDK_Version::set_runtime_version(get_java_runtime_version(THREAD)); // an instance of OutOfMemory exception has been allocated earlier - if (InitializeJavaLangExceptionsErrors) { - initialize_class(vmSymbols::java_lang_OutOfMemoryError(), CHECK_0); - initialize_class(vmSymbols::java_lang_NullPointerException(), CHECK_0); - initialize_class(vmSymbols::java_lang_ClassCastException(), CHECK_0); - initialize_class(vmSymbols::java_lang_ArrayStoreException(), CHECK_0); - initialize_class(vmSymbols::java_lang_ArithmeticException(), CHECK_0); - initialize_class(vmSymbols::java_lang_StackOverflowError(), CHECK_0); - initialize_class(vmSymbols::java_lang_IllegalMonitorStateException(), CHECK_0); - initialize_class(vmSymbols::java_lang_IllegalArgumentException(), CHECK_0); - } else { - warning("java.lang.OutOfMemoryError has not been initialized"); - warning("java.lang.NullPointerException has not been initialized"); - warning("java.lang.ClassCastException has not been initialized"); - warning("java.lang.ArrayStoreException has not been initialized"); - warning("java.lang.ArithmeticException has not been initialized"); - warning("java.lang.StackOverflowError has not been initialized"); - warning("java.lang.IllegalArgumentException has not been initialized"); - } + initialize_class(vmSymbols::java_lang_OutOfMemoryError(), CHECK_0); + initialize_class(vmSymbols::java_lang_NullPointerException(), CHECK_0); + initialize_class(vmSymbols::java_lang_ClassCastException(), CHECK_0); + initialize_class(vmSymbols::java_lang_ArrayStoreException(), CHECK_0); + initialize_class(vmSymbols::java_lang_ArithmeticException(), CHECK_0); + initialize_class(vmSymbols::java_lang_StackOverflowError(), CHECK_0); + initialize_class(vmSymbols::java_lang_IllegalMonitorStateException(), CHECK_0); + initialize_class(vmSymbols::java_lang_IllegalArgumentException(), CHECK_0); } // See : bugid 4211085. From 73b452e94143914226bafc2ac86f16c276aa1dae Mon Sep 17 00:00:00 2001 From: Alexander Scherbatiy Date: Mon, 14 Jan 2013 08:32:29 -0500 Subject: [PATCH 026/138] 7166409: bug4331515.java fail with NullPointerException on ubuntu10.04-x86 for JDK8 Reviewed-by: serb --- .../com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java b/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java index 7d5e237948c..6213ac1e0c0 100644 --- a/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java +++ b/jdk/src/share/classes/com/sun/java/swing/plaf/windows/WindowsLookAndFeel.java @@ -2619,13 +2619,15 @@ public class WindowsLookAndFeel extends BasicLookAndFeel private static class FocusColorProperty extends DesktopProperty { public FocusColorProperty () { - // Fallback value is never used bacause of the configureValue method doesn't return null + // Fallback value is never used because of the configureValue method doesn't return null super("win.3d.backgroundColor", Color.BLACK); } @Override protected Object configureValue(Object value) { - if (! ((Boolean)Toolkit.getDefaultToolkit().getDesktopProperty("win.highContrast.on")).booleanValue()){ + Object highContrastOn = Toolkit.getDefaultToolkit(). + getDesktopProperty("win.highContrast.on"); + if (highContrastOn == null || !((Boolean) highContrastOn).booleanValue()) { return Color.BLACK; } return Color.BLACK.equals(value) ? Color.WHITE : Color.BLACK; From 0614ed6542613326b7f46121e3ef1be8e7a01c59 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Mon, 14 Jan 2013 15:17:47 +0100 Subject: [PATCH 027/138] 8003985: Support @Contended Annotation - JEP 142 HotSpot changes to support @Contended annotation. Reviewed-by: coleenp, kvn, jrose --- .../sun/jvm/hotspot/oops/InstanceKlass.java | 17 +- hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp | 7 + hotspot/src/cpu/x86/vm/vm_version_x86.cpp | 7 + .../share/vm/classfile/classFileParser.cpp | 451 +++++++++++++++--- .../share/vm/classfile/classFileParser.hpp | 22 +- hotspot/src/share/vm/classfile/vmSymbols.hpp | 5 +- hotspot/src/share/vm/oops/fieldInfo.hpp | 144 +++++- hotspot/src/share/vm/oops/fieldStreams.hpp | 17 + hotspot/src/share/vm/oops/instanceKlass.hpp | 14 +- hotspot/src/share/vm/runtime/globals.hpp | 14 +- hotspot/src/share/vm/runtime/vmStructs.cpp | 11 +- 11 files changed, 617 insertions(+), 92 deletions(-) diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java index ba07fa34800..7052f8a2d18 100644 --- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java @@ -52,6 +52,8 @@ public class InstanceKlass extends Klass { private static int LOW_OFFSET; private static int HIGH_OFFSET; private static int FIELD_SLOTS; + private static short FIELDINFO_TAG_SIZE; + private static short FIELDINFO_TAG_OFFSET; // ClassState constants private static int CLASS_STATE_ALLOCATED; @@ -96,9 +98,12 @@ public class InstanceKlass extends Klass { NAME_INDEX_OFFSET = db.lookupIntConstant("FieldInfo::name_index_offset").intValue(); SIGNATURE_INDEX_OFFSET = db.lookupIntConstant("FieldInfo::signature_index_offset").intValue(); INITVAL_INDEX_OFFSET = db.lookupIntConstant("FieldInfo::initval_index_offset").intValue(); - LOW_OFFSET = db.lookupIntConstant("FieldInfo::low_offset").intValue(); - HIGH_OFFSET = db.lookupIntConstant("FieldInfo::high_offset").intValue(); + LOW_OFFSET = db.lookupIntConstant("FieldInfo::low_packed_offset").intValue(); + HIGH_OFFSET = db.lookupIntConstant("FieldInfo::high_packed_offset").intValue(); FIELD_SLOTS = db.lookupIntConstant("FieldInfo::field_slots").intValue(); + FIELDINFO_TAG_SIZE = db.lookupIntConstant("FIELDINFO_TAG_SIZE").shortValue(); + FIELDINFO_TAG_OFFSET = db.lookupIntConstant("FIELDINFO_TAG_OFFSET").shortValue(); + // read ClassState constants CLASS_STATE_ALLOCATED = db.lookupIntConstant("InstanceKlass::allocated").intValue(); CLASS_STATE_LOADED = db.lookupIntConstant("InstanceKlass::loaded").intValue(); @@ -314,8 +319,12 @@ public class InstanceKlass extends Klass { public int getFieldOffset(int index) { U2Array fields = getFields(); - return VM.getVM().buildIntFromShorts(fields.at(index * FIELD_SLOTS + LOW_OFFSET), - fields.at(index * FIELD_SLOTS + HIGH_OFFSET)); + short lo = fields.at(index * FIELD_SLOTS + LOW_OFFSET); + short hi = fields.at(index * FIELD_SLOTS + HIGH_OFFSET); + if ((lo & FIELDINFO_TAG_SIZE) == FIELDINFO_TAG_OFFSET) { + return VM.getVM().buildIntFromShorts(lo, hi) >> FIELDINFO_TAG_SIZE; + } + throw new RuntimeException("should not reach here"); } // Accessors for declared fields diff --git a/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp b/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp index 7d3becef336..03670106924 100644 --- a/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp @@ -259,6 +259,10 @@ void VM_Version::initialize() { if (!has_vis1()) // Drop to 0 if no VIS1 support UseVIS = 0; + if (FLAG_IS_DEFAULT(ContendedPaddingWidth) && + (cache_line_size > ContendedPaddingWidth)) + ContendedPaddingWidth = cache_line_size; + #ifndef PRODUCT if (PrintMiscellaneous && Verbose) { tty->print("Allocation"); @@ -286,6 +290,9 @@ void VM_Version::initialize() { if (PrefetchFieldsAhead > 0) { tty->print_cr("PrefetchFieldsAhead %d", PrefetchFieldsAhead); } + if (ContendedPaddingWidth > 0) { + tty->print_cr("ContendedPaddingWidth %d", ContendedPaddingWidth); + } } #endif // PRODUCT } diff --git a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp index fc046676739..41f5ec2cdc7 100644 --- a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp +++ b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp @@ -734,6 +734,10 @@ void VM_Version::get_processor_features() { PrefetchFieldsAhead = prefetch_fields_ahead(); #endif + if (FLAG_IS_DEFAULT(ContendedPaddingWidth) && + (cache_line_size > ContendedPaddingWidth)) + ContendedPaddingWidth = cache_line_size; + #ifndef PRODUCT if (PrintMiscellaneous && Verbose) { tty->print_cr("Logical CPUs per core: %u", @@ -780,6 +784,9 @@ void VM_Version::get_processor_features() { if (PrefetchFieldsAhead > 0) { tty->print_cr("PrefetchFieldsAhead %d", PrefetchFieldsAhead); } + if (ContendedPaddingWidth > 0) { + tty->print_cr("ContendedPaddingWidth %d", ContendedPaddingWidth); + } } #endif // !PRODUCT } diff --git a/hotspot/src/share/vm/classfile/classFileParser.cpp b/hotspot/src/share/vm/classfile/classFileParser.cpp index efb6138e583..af9700f25cb 100644 --- a/hotspot/src/share/vm/classfile/classFileParser.cpp +++ b/hotspot/src/share/vm/classfile/classFileParser.cpp @@ -970,6 +970,12 @@ void ClassFileParser::parse_field_attributes(ClassLoaderData* loader_data, runtime_visible_annotations_length = attribute_length; runtime_visible_annotations = cfs->get_u1_buffer(); assert(runtime_visible_annotations != NULL, "null visible annotations"); + parse_annotations(loader_data, + runtime_visible_annotations, + runtime_visible_annotations_length, + cp, + parsed_annotations, + CHECK); cfs->skip_u1(runtime_visible_annotations_length, CHECK); } else if (PreserveAllAnnotations && attribute_name == vmSymbols::tag_runtime_invisible_annotations()) { runtime_invisible_annotations_length = attribute_length; @@ -1216,19 +1222,16 @@ Array* ClassFileParser::parse_fields(ClassLoaderData* loader_data, field->initialize(access_flags.as_short(), name_index, signature_index, - constantvalue_index, - 0); - if (parsed_annotations.has_any_annotations()) - parsed_annotations.apply_to(field); - + constantvalue_index); BasicType type = cp->basic_type_for_signature_at(signature_index); // Remember how many oops we encountered and compute allocation type FieldAllocationType atype = fac->update(is_static, type); + field->set_allocation_type(atype); - // The correct offset is computed later (all oop fields will be located together) - // We temporarily store the allocation type in the offset field - field->set_offset(atype); + // After field is initialized with type, we can augment it with aux info + if (parsed_annotations.has_any_annotations()) + parsed_annotations.apply_to(field); } int index = length; @@ -1259,17 +1262,13 @@ Array* ClassFileParser::parse_fields(ClassLoaderData* loader_data, field->initialize(JVM_ACC_FIELD_INTERNAL, injected[n].name_index, injected[n].signature_index, - 0, 0); BasicType type = FieldType::basic_type(injected[n].signature()); // Remember how many oops we encountered and compute allocation type FieldAllocationType atype = fac->update(false, type); - - // The correct offset is computed later (all oop fields will be located together) - // We temporarily store the allocation type in the offset field - field->set_offset(atype); + field->set_allocation_type(atype); index++; } } @@ -1735,7 +1734,8 @@ int ClassFileParser::skip_annotation_value(u1* buffer, int limit, int index) { } // Sift through annotations, looking for those significant to the VM: -void ClassFileParser::parse_annotations(u1* buffer, int limit, +void ClassFileParser::parse_annotations(ClassLoaderData* loader_data, + u1* buffer, int limit, constantPoolHandle cp, ClassFileParser::AnnotationCollector* coll, TRAPS) { @@ -1752,9 +1752,12 @@ void ClassFileParser::parse_annotations(u1* buffer, int limit, e_type_off = 7, // utf8 such as 'Ljava/lang/annotation/RetentionPolicy;' e_con_off = 9, // utf8 payload, such as 'SOURCE', 'CLASS', 'RUNTIME' e_size = 11, // end of 'e' annotation - c_tag_val = 'c', - c_con_off = 7, // utf8 payload, such as 'I' or 'Ljava/lang/String;' + c_tag_val = 'c', // payload is type + c_con_off = 7, // utf8 payload, such as 'I' c_size = 9, // end of 'c' annotation + s_tag_val = 's', // payload is String + s_con_off = 7, // utf8 payload, such as 'Ljava/lang/String;' + s_size = 9, min_size = 6 // smallest possible size (zero members) }; while ((--nann) >= 0 && (index-2 + min_size <= limit)) { @@ -1773,57 +1776,65 @@ void ClassFileParser::parse_annotations(u1* buffer, int limit, } // Here is where parsing particular annotations will take place. - AnnotationCollector::ID id = coll->annotation_index(aname); + AnnotationCollector::ID id = coll->annotation_index(loader_data, aname); if (id == AnnotationCollector::_unknown) continue; coll->set_annotation(id); - // If there are no values, just set the bit and move on: - if (count == 0) continue; - // For the record, here is how annotation payloads can be collected. - // Suppose we want to capture @Retention.value. Here is how: - //if (id == AnnotationCollector::_class_Retention) { - // Symbol* payload = NULL; - // if (count == 1 - // && e_size == (index0 - index) // match size - // && e_tag_val == *(abase + tag_off) - // && (check_symbol_at(cp, Bytes::get_Java_u2(abase + e_type_off)) - // == vmSymbols::RetentionPolicy_signature()) - // && member == vmSymbols::value_name()) { - // payload = check_symbol_at(cp, Bytes::get_Java_u2(abase + e_con_off)); - // } - // check_property(payload != NULL, - // "Invalid @Retention annotation at offset %u in class file %s", - // index0, CHECK); - // if (payload != NULL) { - // payload->increment_refcount(); - // coll->_class_RetentionPolicy = payload; - // } - //} + if (id == AnnotationCollector::_sun_misc_Contended) { + if (count == 1 + && s_size == (index - index0) // match size + && s_tag_val == *(abase + tag_off) + && member == vmSymbols::value_name()) { + u2 group_index = Bytes::get_Java_u2(abase + s_con_off); + coll->set_contended_group(group_index); + } else { + coll->set_contended_group(0); // default contended group + } + coll->set_contended(true); + } else { + coll->set_contended(false); + } } } -ClassFileParser::AnnotationCollector::ID ClassFileParser::AnnotationCollector::annotation_index(Symbol* name) { +ClassFileParser::AnnotationCollector::ID +ClassFileParser::AnnotationCollector::annotation_index(ClassLoaderData* loader_data, + Symbol* name) { vmSymbols::SID sid = vmSymbols::find_sid(name); + bool privileged = false; + if (loader_data->is_the_null_class_loader_data()) { + // Privileged code can use all annotations. Other code silently drops some. + privileged = true; + } switch (sid) { case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_ForceInline_signature): if (_location != _in_method) break; // only allow for methods + if (!privileged) break; // only allow in privileged code return _method_ForceInline; case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_DontInline_signature): if (_location != _in_method) break; // only allow for methods + if (!privileged) break; // only allow in privileged code return _method_DontInline; case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_LambdaForm_Compiled_signature): if (_location != _in_method) break; // only allow for methods + if (!privileged) break; // only allow in privileged code return _method_LambdaForm_Compiled; case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_LambdaForm_Hidden_signature): if (_location != _in_method) break; // only allow for methods + if (!privileged) break; // only allow in privileged code return _method_LambdaForm_Hidden; + case vmSymbols::VM_SYMBOL_ENUM_NAME(sun_misc_Contended_signature): + if (_location != _in_field && _location != _in_class) break; // only allow for fields and classes + if (!EnableContended || (RestrictContended && !privileged)) break; // honor privileges + return _sun_misc_Contended; default: break; } return AnnotationCollector::_unknown; } void ClassFileParser::FieldAnnotationCollector::apply_to(FieldInfo* f) { - fatal("no field annotations yet"); + if (is_contended()) + f->set_contended_group(contended_group()); } void ClassFileParser::MethodAnnotationCollector::apply_to(methodHandle m) { @@ -1838,7 +1849,7 @@ void ClassFileParser::MethodAnnotationCollector::apply_to(methodHandle m) { } void ClassFileParser::ClassAnnotationCollector::apply_to(instanceKlassHandle k) { - fatal("no class annotations yet"); + k->set_is_contended(is_contended()); } @@ -2181,7 +2192,8 @@ methodHandle ClassFileParser::parse_method(ClassLoaderData* loader_data, runtime_visible_annotations_length = method_attribute_length; runtime_visible_annotations = cfs->get_u1_buffer(); assert(runtime_visible_annotations != NULL, "null visible annotations"); - parse_annotations(runtime_visible_annotations, + parse_annotations(loader_data, + runtime_visible_annotations, runtime_visible_annotations_length, cp, &parsed_annotations, CHECK_(nullHandle)); cfs->skip_u1(runtime_visible_annotations_length, CHECK_(nullHandle)); @@ -2886,7 +2898,8 @@ void ClassFileParser::parse_classfile_attributes(ClassLoaderData* loader_data, runtime_visible_annotations_length = attribute_length; runtime_visible_annotations = cfs->get_u1_buffer(); assert(runtime_visible_annotations != NULL, "null visible annotations"); - parse_annotations(runtime_visible_annotations, + parse_annotations(loader_data, + runtime_visible_annotations, runtime_visible_annotations_length, cp, parsed_annotations, @@ -3405,18 +3418,21 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, // Size of Java itable (in words) itable_size = access_flags.is_interface() ? 0 : klassItable::compute_itable_size(transitive_interfaces); + // get the padding width from the option + // TODO: Ask VM about specific CPU we are running on + int pad_size = ContendedPaddingWidth; + // Field size and offset computation int nonstatic_field_size = super_klass() == NULL ? 0 : super_klass->nonstatic_field_size(); #ifndef PRODUCT int orig_nonstatic_field_size = 0; #endif - int static_field_size = 0; int next_static_oop_offset; int next_static_double_offset; int next_static_word_offset; int next_static_short_offset; int next_static_byte_offset; - int next_static_type_offset; + int next_static_padded_offset; int next_nonstatic_oop_offset; int next_nonstatic_double_offset; int next_nonstatic_word_offset; @@ -3426,11 +3442,36 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, int first_nonstatic_oop_offset; int first_nonstatic_field_offset; int next_nonstatic_field_offset; + int next_nonstatic_padded_offset; + + // Count the contended fields by type. + int static_contended_count = 0; + int nonstatic_contended_count = 0; + FieldAllocationCount fac_contended; + for (AllFieldStream fs(fields, cp); !fs.done(); fs.next()) { + FieldAllocationType atype = (FieldAllocationType) fs.allocation_type(); + if (fs.is_contended()) { + fac_contended.count[atype]++; + if (fs.access_flags().is_static()) { + static_contended_count++; + } else { + nonstatic_contended_count++; + } + } + } + int contended_count = static_contended_count + nonstatic_contended_count; + // Calculate the starting byte offsets next_static_oop_offset = InstanceMirrorKlass::offset_of_static_fields(); + + // class is contended, pad before all the fields + if (parsed_annotations.is_contended()) { + next_static_oop_offset += pad_size; + } + next_static_double_offset = next_static_oop_offset + - (fac.count[STATIC_OOP] * heapOopSize); + ((fac.count[STATIC_OOP] - fac_contended.count[STATIC_OOP]) * heapOopSize); if ( fac.count[STATIC_DOUBLE] && (Universe::field_type_should_be_aligned(T_DOUBLE) || Universe::field_type_should_be_aligned(T_LONG)) ) { @@ -3438,25 +3479,29 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, } next_static_word_offset = next_static_double_offset + - (fac.count[STATIC_DOUBLE] * BytesPerLong); + ((fac.count[STATIC_DOUBLE] - fac_contended.count[STATIC_DOUBLE]) * BytesPerLong); next_static_short_offset = next_static_word_offset + - (fac.count[STATIC_WORD] * BytesPerInt); + ((fac.count[STATIC_WORD] - fac_contended.count[STATIC_WORD]) * BytesPerInt); next_static_byte_offset = next_static_short_offset + - (fac.count[STATIC_SHORT] * BytesPerShort); - next_static_type_offset = align_size_up((next_static_byte_offset + - fac.count[STATIC_BYTE] ), wordSize ); - static_field_size = (next_static_type_offset - - next_static_oop_offset) / wordSize; + ((fac.count[STATIC_SHORT] - fac_contended.count[STATIC_SHORT]) * BytesPerShort); + next_static_padded_offset = next_static_byte_offset + + ((fac.count[STATIC_BYTE] - fac_contended.count[STATIC_BYTE]) * 1); first_nonstatic_field_offset = instanceOopDesc::base_offset_in_bytes() + nonstatic_field_size * heapOopSize; + + // class is contended, pad before all the fields + if (parsed_annotations.is_contended()) { + first_nonstatic_field_offset += pad_size; + } + next_nonstatic_field_offset = first_nonstatic_field_offset; - unsigned int nonstatic_double_count = fac.count[NONSTATIC_DOUBLE]; - unsigned int nonstatic_word_count = fac.count[NONSTATIC_WORD]; - unsigned int nonstatic_short_count = fac.count[NONSTATIC_SHORT]; - unsigned int nonstatic_byte_count = fac.count[NONSTATIC_BYTE]; - unsigned int nonstatic_oop_count = fac.count[NONSTATIC_OOP]; + unsigned int nonstatic_double_count = fac.count[NONSTATIC_DOUBLE] - fac_contended.count[NONSTATIC_DOUBLE]; + unsigned int nonstatic_word_count = fac.count[NONSTATIC_WORD] - fac_contended.count[NONSTATIC_WORD]; + unsigned int nonstatic_short_count = fac.count[NONSTATIC_SHORT] - fac_contended.count[NONSTATIC_SHORT]; + unsigned int nonstatic_byte_count = fac.count[NONSTATIC_BYTE] - fac_contended.count[NONSTATIC_BYTE]; + unsigned int nonstatic_oop_count = fac.count[NONSTATIC_OOP] - fac_contended.count[NONSTATIC_OOP]; bool super_has_nonstatic_fields = (super_klass() != NULL && super_klass->has_nonstatic_fields()); @@ -3529,12 +3574,12 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, } if( allocation_style == 0 ) { - // Fields order: oops, longs/doubles, ints, shorts/chars, bytes + // Fields order: oops, longs/doubles, ints, shorts/chars, bytes, padded fields next_nonstatic_oop_offset = next_nonstatic_field_offset; next_nonstatic_double_offset = next_nonstatic_oop_offset + (nonstatic_oop_count * heapOopSize); } else if( allocation_style == 1 ) { - // Fields order: longs/doubles, ints, shorts/chars, bytes, oops + // Fields order: longs/doubles, ints, shorts/chars, bytes, oops, padded fields next_nonstatic_double_offset = next_nonstatic_field_offset; } else if( allocation_style == 2 ) { // Fields allocation: oops fields in super and sub classes are together. @@ -3613,27 +3658,33 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, (nonstatic_word_count * BytesPerInt); next_nonstatic_byte_offset = next_nonstatic_short_offset + (nonstatic_short_count * BytesPerShort); + next_nonstatic_padded_offset = next_nonstatic_byte_offset + + nonstatic_byte_count; - int notaligned_offset; - if( allocation_style == 0 ) { - notaligned_offset = next_nonstatic_byte_offset + nonstatic_byte_count; - } else { // allocation_style == 1 - next_nonstatic_oop_offset = next_nonstatic_byte_offset + nonstatic_byte_count; + // let oops jump before padding with this allocation style + if( allocation_style == 1 ) { + next_nonstatic_oop_offset = next_nonstatic_padded_offset; if( nonstatic_oop_count > 0 ) { next_nonstatic_oop_offset = align_size_up(next_nonstatic_oop_offset, heapOopSize); } - notaligned_offset = next_nonstatic_oop_offset + (nonstatic_oop_count * heapOopSize); + next_nonstatic_padded_offset = next_nonstatic_oop_offset + (nonstatic_oop_count * heapOopSize); } - next_nonstatic_type_offset = align_size_up(notaligned_offset, heapOopSize ); - nonstatic_field_size = nonstatic_field_size + ((next_nonstatic_type_offset - - first_nonstatic_field_offset)/heapOopSize); // Iterate over fields again and compute correct offsets. // The field allocation type was temporarily stored in the offset slot. // oop fields are located before non-oop fields (static and non-static). for (AllFieldStream fs(fields, cp); !fs.done(); fs.next()) { + + // skip already laid out fields + if (fs.is_offset_set()) continue; + + // contended fields are handled below + if (fs.is_contended()) continue; + int real_offset; - FieldAllocationType atype = (FieldAllocationType) fs.offset(); + FieldAllocationType atype = (FieldAllocationType) fs.allocation_type(); + + // pack the rest of the fields switch (atype) { case STATIC_OOP: real_offset = next_static_oop_offset; @@ -3722,13 +3773,225 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, fs.set_offset(real_offset); } + + // Handle the contended cases. + // + // Each contended field should not intersect the cache line with another contended field. + // In the absence of alignment information, we end up with pessimistically separating + // the fields with full-width padding. + // + // Additionally, this should not break alignment for the fields, so we round the alignment up + // for each field. + if (contended_count > 0) { + + // if there is at least one contended field, we need to have pre-padding for them + if (nonstatic_contended_count > 0) { + next_nonstatic_padded_offset += pad_size; + } + + // collect all contended groups + BitMap bm(cp->size()); + for (AllFieldStream fs(fields, cp); !fs.done(); fs.next()) { + // skip already laid out fields + if (fs.is_offset_set()) continue; + + if (fs.is_contended()) { + bm.set_bit(fs.contended_group()); + } + } + + int current_group = -1; + while ((current_group = (int)bm.get_next_one_offset(current_group + 1)) != (int)bm.size()) { + + for (AllFieldStream fs(fields, cp); !fs.done(); fs.next()) { + + // skip already laid out fields + if (fs.is_offset_set()) continue; + + // skip non-contended fields and fields from different group + if (!fs.is_contended() || (fs.contended_group() != current_group)) continue; + + // handle statics below + if (fs.access_flags().is_static()) continue; + + int real_offset; + FieldAllocationType atype = (FieldAllocationType) fs.allocation_type(); + + switch (atype) { + case NONSTATIC_BYTE: + next_nonstatic_padded_offset = align_size_up(next_nonstatic_padded_offset, 1); + real_offset = next_nonstatic_padded_offset; + next_nonstatic_padded_offset += 1; + break; + + case NONSTATIC_SHORT: + next_nonstatic_padded_offset = align_size_up(next_nonstatic_padded_offset, BytesPerShort); + real_offset = next_nonstatic_padded_offset; + next_nonstatic_padded_offset += BytesPerShort; + break; + + case NONSTATIC_WORD: + next_nonstatic_padded_offset = align_size_up(next_nonstatic_padded_offset, BytesPerInt); + real_offset = next_nonstatic_padded_offset; + next_nonstatic_padded_offset += BytesPerInt; + break; + + case NONSTATIC_DOUBLE: + next_nonstatic_padded_offset = align_size_up(next_nonstatic_padded_offset, BytesPerLong); + real_offset = next_nonstatic_padded_offset; + next_nonstatic_padded_offset += BytesPerLong; + break; + + case NONSTATIC_OOP: + next_nonstatic_padded_offset = align_size_up(next_nonstatic_padded_offset, heapOopSize); + real_offset = next_nonstatic_padded_offset; + next_nonstatic_padded_offset += heapOopSize; + + // Create new oop map + nonstatic_oop_offsets[nonstatic_oop_map_count] = real_offset; + nonstatic_oop_counts [nonstatic_oop_map_count] = 1; + nonstatic_oop_map_count += 1; + if( first_nonstatic_oop_offset == 0 ) { // Undefined + first_nonstatic_oop_offset = real_offset; + } + break; + + default: + ShouldNotReachHere(); + } + + if (fs.contended_group() == 0) { + // Contended group defines the equivalence class over the fields: + // the fields within the same contended group are not inter-padded. + // The only exception is default group, which does not incur the + // equivalence, and so requires intra-padding. + next_nonstatic_padded_offset += pad_size; + } + + fs.set_offset(real_offset); + } // for + + // Start laying out the next group. + // Note that this will effectively pad the last group in the back; + // this is expected to alleviate memory contention effects for + // subclass fields and/or adjacent object. + // If this was the default group, the padding is already in place. + if (current_group != 0) { + next_nonstatic_padded_offset += pad_size; + } + } + + // handle static fields + + // if there is at least one contended field, we need to have pre-padding for them + if (static_contended_count > 0) { + next_static_padded_offset += pad_size; + } + + current_group = -1; + while ((current_group = (int)bm.get_next_one_offset(current_group + 1)) != (int)bm.size()) { + + for (AllFieldStream fs(fields, cp); !fs.done(); fs.next()) { + + // skip already laid out fields + if (fs.is_offset_set()) continue; + + // skip non-contended fields and fields from different group + if (!fs.is_contended() || (fs.contended_group() != current_group)) continue; + + // non-statics already handled above + if (!fs.access_flags().is_static()) continue; + + int real_offset; + FieldAllocationType atype = (FieldAllocationType) fs.allocation_type(); + + switch (atype) { + + case STATIC_BYTE: + next_static_padded_offset = align_size_up(next_static_padded_offset, 1); + real_offset = next_static_padded_offset; + next_static_padded_offset += 1; + break; + + case STATIC_SHORT: + next_static_padded_offset = align_size_up(next_static_padded_offset, BytesPerShort); + real_offset = next_static_padded_offset; + next_static_padded_offset += BytesPerShort; + break; + + case STATIC_WORD: + next_static_padded_offset = align_size_up(next_static_padded_offset, BytesPerInt); + real_offset = next_static_padded_offset; + next_static_padded_offset += BytesPerInt; + break; + + case STATIC_DOUBLE: + next_static_padded_offset = align_size_up(next_static_padded_offset, BytesPerLong); + real_offset = next_static_padded_offset; + next_static_padded_offset += BytesPerLong; + break; + + case STATIC_OOP: + next_static_padded_offset = align_size_up(next_static_padded_offset, heapOopSize); + real_offset = next_static_padded_offset; + next_static_padded_offset += heapOopSize; + break; + + default: + ShouldNotReachHere(); + } + + if (fs.contended_group() == 0) { + // Contended group defines the equivalence class over the fields: + // the fields within the same contended group are not inter-padded. + // The only exception is default group, which does not incur the + // equivalence, and so requires intra-padding. + next_static_padded_offset += pad_size; + } + + fs.set_offset(real_offset); + } // for + + // Start laying out the next group. + // Note that this will effectively pad the last group in the back; + // this is expected to alleviate memory contention effects for + // subclass fields and/or adjacent object. + // If this was the default group, the padding is already in place. + if (current_group != 0) { + next_static_padded_offset += pad_size; + } + + } + + } // handle contended + // Size of instances int instance_size; + int notaligned_offset = next_nonstatic_padded_offset; + + // Entire class is contended, pad in the back. + // This helps to alleviate memory contention effects for subclass fields + // and/or adjacent object. + if (parsed_annotations.is_contended()) { + notaligned_offset += pad_size; + next_static_padded_offset += pad_size; + } + + int next_static_type_offset = align_size_up(next_static_padded_offset, wordSize); + int static_field_size = (next_static_type_offset - + InstanceMirrorKlass::offset_of_static_fields()) / wordSize; + + next_nonstatic_type_offset = align_size_up(notaligned_offset, heapOopSize ); + nonstatic_field_size = nonstatic_field_size + ((next_nonstatic_type_offset + - first_nonstatic_field_offset)/heapOopSize); + next_nonstatic_type_offset = align_size_up(notaligned_offset, wordSize ); instance_size = align_object_size(next_nonstatic_type_offset / wordSize); - assert(instance_size == align_object_size(align_size_up((instanceOopDesc::base_offset_in_bytes() + nonstatic_field_size*heapOopSize), wordSize) / wordSize), "consistent layout helper value"); + assert(instance_size == align_object_size(align_size_up( + (instanceOopDesc::base_offset_in_bytes() + nonstatic_field_size*heapOopSize + ((parsed_annotations.is_contended()) ? pad_size : 0)), + wordSize) / wordSize), "consistent layout helper value"); // Number of non-static oop map blocks allocated at end of klass. const unsigned int total_oop_map_count = @@ -4008,6 +4271,18 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, } #endif +#ifndef PRODUCT + if (PrintFieldLayout) { + print_field_layout(name, + fields, + cp, + instance_size, + first_nonstatic_field_offset, + next_nonstatic_field_offset, + next_static_type_offset); + } +#endif + // preserve result across HandleMark preserve_this_klass = this_klass(); } @@ -4020,6 +4295,38 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, return this_klass; } +void ClassFileParser::print_field_layout(Symbol* name, + Array* fields, + constantPoolHandle cp, + int instance_size, + int instance_fields_start, + int instance_fields_end, + int static_fields_end) { + tty->print("%s: field layout\n", name->as_klass_external_name()); + tty->print(" @%3d %s\n", instance_fields_start, "--- instance fields start ---"); + for (AllFieldStream fs(fields, cp); !fs.done(); fs.next()) { + if (!fs.access_flags().is_static()) { + tty->print(" @%3d \"%s\" %s\n", + fs.offset(), + fs.name()->as_klass_external_name(), + fs.signature()->as_klass_external_name()); + } + } + tty->print(" @%3d %s\n", instance_fields_end, "--- instance fields end ---"); + tty->print(" @%3d %s\n", instance_size * wordSize, "--- instance ends ---"); + tty->print(" @%3d %s\n", InstanceMirrorKlass::offset_of_static_fields(), "--- static fields start ---"); + for (AllFieldStream fs(fields, cp); !fs.done(); fs.next()) { + if (fs.access_flags().is_static()) { + tty->print(" @%3d \"%s\" %s\n", + fs.offset(), + fs.name()->as_klass_external_name(), + fs.signature()->as_klass_external_name()); + } + } + tty->print(" @%3d %s\n", static_fields_end, "--- static fields end ---"); + tty->print("\n"); +} + unsigned int ClassFileParser::compute_oop_map_count(instanceKlassHandle super, unsigned int nonstatic_oop_map_count, diff --git a/hotspot/src/share/vm/classfile/classFileParser.hpp b/hotspot/src/share/vm/classfile/classFileParser.hpp index 5a5d3fc397a..cc0193a15f5 100644 --- a/hotspot/src/share/vm/classfile/classFileParser.hpp +++ b/hotspot/src/share/vm/classfile/classFileParser.hpp @@ -95,17 +95,20 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC { _method_DontInline, _method_LambdaForm_Compiled, _method_LambdaForm_Hidden, + _sun_misc_Contended, _annotation_LIMIT }; const Location _location; int _annotations_present; + u2 _contended_group; + AnnotationCollector(Location location) : _location(location), _annotations_present(0) { assert((int)_annotation_LIMIT <= (int)sizeof(_annotations_present) * BitsPerByte, ""); } // If this annotation name has an ID, report it (or _none). - ID annotation_index(Symbol* name); + ID annotation_index(ClassLoaderData* loader_data, Symbol* name); // Set the annotation name: void set_annotation(ID id) { assert((int)id >= 0 && (int)id < (int)_annotation_LIMIT, "oob"); @@ -114,6 +117,12 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC { // Report if the annotation is present. bool has_any_annotations() { return _annotations_present != 0; } bool has_annotation(ID id) { return (nth_bit((int)id) & _annotations_present) != 0; } + + void set_contended_group(u2 group) { _contended_group = group; } + u2 contended_group() { return _contended_group; } + + void set_contended(bool contended) { set_annotation(_sun_misc_Contended); } + bool is_contended() { return has_annotation(_sun_misc_Contended); } }; class FieldAnnotationCollector: public AnnotationCollector { public: @@ -177,6 +186,14 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC { Array** fields_type_annotations, u2* java_fields_count_ptr, TRAPS); + void print_field_layout(Symbol* name, + Array* fields, + constantPoolHandle cp, + int instance_size, + int instance_fields_start, + int instance_fields_end, + int static_fields_end); + // Method parsing methodHandle parse_method(ClassLoaderData* loader_data, constantPoolHandle cp, @@ -247,7 +264,8 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC { int runtime_invisible_annotations_length, TRAPS); int skip_annotation(u1* buffer, int limit, int index); int skip_annotation_value(u1* buffer, int limit, int index); - void parse_annotations(u1* buffer, int limit, constantPoolHandle cp, + void parse_annotations(ClassLoaderData* loader_data, + u1* buffer, int limit, constantPoolHandle cp, /* Results (currently, only one result is supported): */ AnnotationCollector* result, TRAPS); diff --git a/hotspot/src/share/vm/classfile/vmSymbols.hpp b/hotspot/src/share/vm/classfile/vmSymbols.hpp index e6168ceba2c..e77e5cea2db 100644 --- a/hotspot/src/share/vm/classfile/vmSymbols.hpp +++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp @@ -194,7 +194,10 @@ template(java_lang_VirtualMachineError, "java/lang/VirtualMachineError") \ template(java_lang_StackOverflowError, "java/lang/StackOverflowError") \ template(java_lang_StackTraceElement, "java/lang/StackTraceElement") \ + \ + /* Concurrency support */ \ template(java_util_concurrent_locks_AbstractOwnableSynchronizer, "java/util/concurrent/locks/AbstractOwnableSynchronizer") \ + template(sun_misc_Contended_signature, "Lsun/misc/Contended;") \ \ /* class symbols needed by intrinsics */ \ VM_INTRINSICS_DO(VM_INTRINSIC_IGNORE, template, VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, VM_ALIAS_IGNORE) \ @@ -284,7 +287,7 @@ NOT_LP64( do_alias(intptr_signature, int_signature) ) \ LP64_ONLY( do_alias(intptr_signature, long_signature) ) \ template(selectAlternative_signature, "(ZLjava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/MethodHandle;") \ - \ + \ /* common method and field names */ \ template(object_initializer_name, "") \ template(class_initializer_name, "") \ diff --git a/hotspot/src/share/vm/oops/fieldInfo.hpp b/hotspot/src/share/vm/oops/fieldInfo.hpp index ee4af47204d..331dc05f37c 100644 --- a/hotspot/src/share/vm/oops/fieldInfo.hpp +++ b/hotspot/src/share/vm/oops/fieldInfo.hpp @@ -43,14 +43,29 @@ class FieldInfo VALUE_OBJ_CLASS_SPEC { public: // fields // Field info extracted from the class file and stored - // as an array of 7 shorts + // as an array of 6 shorts. + +#define FIELDINFO_TAG_SIZE 2 +#define FIELDINFO_TAG_BLANK 0 +#define FIELDINFO_TAG_OFFSET 1 +#define FIELDINFO_TAG_TYPE_PLAIN 2 +#define FIELDINFO_TAG_TYPE_CONTENDED 3 +#define FIELDINFO_TAG_MASK 3 + + // Packed field has the tag, and can be either of: + // hi bits <--------------------------- lo bits + // |---------high---------|---------low---------| + // ..........................................00 - blank + // [------------------offset----------------]01 - real field offset + // ......................[-------type-------]10 - plain field with type + // [--contention_group--][-------type-------]11 - contended field with type and contention group enum FieldOffset { access_flags_offset = 0, name_index_offset = 1, signature_index_offset = 2, initval_index_offset = 3, - low_offset = 4, - high_offset = 5, + low_packed_offset = 4, + high_packed_offset = 5, field_slots = 6 }; @@ -76,17 +91,90 @@ class FieldInfo VALUE_OBJ_CLASS_SPEC { void initialize(u2 access_flags, u2 name_index, u2 signature_index, - u2 initval_index, - u4 offset) { + u2 initval_index) { _shorts[access_flags_offset] = access_flags; _shorts[name_index_offset] = name_index; _shorts[signature_index_offset] = signature_index; _shorts[initval_index_offset] = initval_index; - set_offset(offset); + _shorts[low_packed_offset] = 0; + _shorts[high_packed_offset] = 0; } u2 access_flags() const { return _shorts[access_flags_offset]; } - u4 offset() const { return build_int_from_shorts(_shorts[low_offset], _shorts[high_offset]); } + u4 offset() const { + u2 lo = _shorts[low_packed_offset]; + switch(lo & FIELDINFO_TAG_MASK) { + case FIELDINFO_TAG_OFFSET: + return build_int_from_shorts(_shorts[low_packed_offset], _shorts[high_packed_offset]) >> FIELDINFO_TAG_SIZE; +#ifndef PRODUCT + case FIELDINFO_TAG_TYPE_PLAIN: + ShouldNotReachHere2("Asking offset for the plain type field"); + case FIELDINFO_TAG_TYPE_CONTENDED: + ShouldNotReachHere2("Asking offset for the contended type field"); + case FIELDINFO_TAG_BLANK: + ShouldNotReachHere2("Asking offset for the blank field"); +#endif + } + ShouldNotReachHere(); + return 0; + } + + bool is_contended() const { + u2 lo = _shorts[low_packed_offset]; + switch(lo & FIELDINFO_TAG_MASK) { + case FIELDINFO_TAG_TYPE_PLAIN: + return false; + case FIELDINFO_TAG_TYPE_CONTENDED: + return true; +#ifndef PRODUCT + case FIELDINFO_TAG_OFFSET: + ShouldNotReachHere2("Asking contended flag for the field with offset"); + case FIELDINFO_TAG_BLANK: + ShouldNotReachHere2("Asking contended flag for the blank field"); +#endif + } + ShouldNotReachHere(); + return false; + } + + u2 contended_group() const { + u2 lo = _shorts[low_packed_offset]; + switch(lo & FIELDINFO_TAG_MASK) { + case FIELDINFO_TAG_TYPE_PLAIN: + return 0; + case FIELDINFO_TAG_TYPE_CONTENDED: + return _shorts[high_packed_offset]; +#ifndef PRODUCT + case FIELDINFO_TAG_OFFSET: + ShouldNotReachHere2("Asking the contended group for the field with offset"); + case FIELDINFO_TAG_BLANK: + ShouldNotReachHere2("Asking the contended group for the blank field"); +#endif + } + ShouldNotReachHere(); + return 0; + } + + u2 allocation_type() const { + u2 lo = _shorts[low_packed_offset]; + switch(lo & FIELDINFO_TAG_MASK) { + case FIELDINFO_TAG_TYPE_PLAIN: + case FIELDINFO_TAG_TYPE_CONTENDED: + return (lo >> FIELDINFO_TAG_SIZE); +#ifndef PRODUCT + case FIELDINFO_TAG_OFFSET: + ShouldNotReachHere2("Asking the field type for field with offset"); + case FIELDINFO_TAG_BLANK: + ShouldNotReachHere2("Asking the field type for the blank field"); +#endif + } + ShouldNotReachHere(); + return 0; + } + + bool is_offset_set() const { + return (_shorts[low_packed_offset] & FIELDINFO_TAG_MASK) == FIELDINFO_TAG_OFFSET; + } Symbol* name(constantPoolHandle cp) const { int index = name_index(); @@ -106,8 +194,46 @@ class FieldInfo VALUE_OBJ_CLASS_SPEC { void set_access_flags(u2 val) { _shorts[access_flags_offset] = val; } void set_offset(u4 val) { - _shorts[low_offset] = extract_low_short_from_int(val); - _shorts[high_offset] = extract_high_short_from_int(val); + val = val << FIELDINFO_TAG_SIZE; // make room for tag + _shorts[low_packed_offset] = extract_low_short_from_int(val) | FIELDINFO_TAG_OFFSET; + _shorts[high_packed_offset] = extract_high_short_from_int(val); + } + + void set_allocation_type(int type) { + u2 lo = _shorts[low_packed_offset]; + switch(lo & FIELDINFO_TAG_MASK) { + case FIELDINFO_TAG_BLANK: + _shorts[low_packed_offset] = ((type << FIELDINFO_TAG_SIZE)) & 0xFFFF; + _shorts[low_packed_offset] &= ~FIELDINFO_TAG_MASK; + _shorts[low_packed_offset] |= FIELDINFO_TAG_TYPE_PLAIN; + return; +#ifndef PRODUCT + case FIELDINFO_TAG_TYPE_PLAIN: + case FIELDINFO_TAG_TYPE_CONTENDED: + case FIELDINFO_TAG_OFFSET: + ShouldNotReachHere2("Setting the field type with overwriting"); +#endif + } + ShouldNotReachHere(); + } + + void set_contended_group(u2 val) { + u2 lo = _shorts[low_packed_offset]; + switch(lo & FIELDINFO_TAG_MASK) { + case FIELDINFO_TAG_TYPE_PLAIN: + _shorts[low_packed_offset] |= FIELDINFO_TAG_TYPE_CONTENDED; + _shorts[high_packed_offset] = val; + return; +#ifndef PRODUCT + case FIELDINFO_TAG_TYPE_CONTENDED: + ShouldNotReachHere2("Overwriting contended group"); + case FIELDINFO_TAG_BLANK: + ShouldNotReachHere2("Setting contended group for the blank field"); + case FIELDINFO_TAG_OFFSET: + ShouldNotReachHere2("Setting contended group for field with offset"); +#endif + } + ShouldNotReachHere(); } bool is_internal() const { diff --git a/hotspot/src/share/vm/oops/fieldStreams.hpp b/hotspot/src/share/vm/oops/fieldStreams.hpp index adde764127b..acc590c970c 100644 --- a/hotspot/src/share/vm/oops/fieldStreams.hpp +++ b/hotspot/src/share/vm/oops/fieldStreams.hpp @@ -160,9 +160,26 @@ class FieldStreamBase : public StackObj { return field()->offset(); } + int allocation_type() const { + return field()->allocation_type(); + } + void set_offset(int offset) { field()->set_offset(offset); } + + bool is_offset_set() const { + return field()->is_offset_set(); + } + + bool is_contended() const { + return field()->is_contended(); + } + + int contended_group() const { + return field()->contended_group(); + } + }; // Iterate over only the internal fields diff --git a/hotspot/src/share/vm/oops/instanceKlass.hpp b/hotspot/src/share/vm/oops/instanceKlass.hpp index 0d173faa510..31ba745c19d 100644 --- a/hotspot/src/share/vm/oops/instanceKlass.hpp +++ b/hotspot/src/share/vm/oops/instanceKlass.hpp @@ -230,7 +230,8 @@ class InstanceKlass: public Klass { _misc_rewritten = 1 << 0, // methods rewritten. _misc_has_nonstatic_fields = 1 << 1, // for sizing with UseCompressedOops _misc_should_verify_class = 1 << 2, // allow caching of preverification - _misc_is_anonymous = 1 << 3 // has embedded _inner_classes field + _misc_is_anonymous = 1 << 3, // has embedded _inner_classes field + _misc_is_contended = 1 << 4 // marked with contended annotation }; u2 _misc_flags; u2 _minor_version; // minor version number of class file @@ -550,6 +551,17 @@ class InstanceKlass: public Klass { return is_anonymous() ? java_mirror() : class_loader(); } + bool is_contended() const { + return (_misc_flags & _misc_is_contended) != 0; + } + void set_is_contended(bool value) { + if (value) { + _misc_flags |= _misc_is_contended; + } else { + _misc_flags &= ~_misc_is_contended; + } + } + // signers objArrayOop signers() const { return _signers; } void set_signers(objArrayOop s) { klass_oop_store((oop*)&_signers, s); } diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp index abdb42a51e2..52c9e7afad5 100644 --- a/hotspot/src/share/vm/runtime/globals.hpp +++ b/hotspot/src/share/vm/runtime/globals.hpp @@ -1075,7 +1075,7 @@ class CommandLineFlags { \ product(intx, ClearFPUAtPark, 0, "(Unsafe,Unstable)" ) \ \ - product(intx, hashCode, 0, \ + product(intx, hashCode, 5, \ "(Unstable) select hashCode generation algorithm" ) \ \ product(intx, WorkAroundNPTLTimedWaitHang, 1, \ @@ -1173,6 +1173,18 @@ class CommandLineFlags { notproduct(bool, PrintCompactFieldsSavings, false, \ "Print how many words were saved with CompactFields") \ \ + notproduct(bool, PrintFieldLayout, false, \ + "Print field layout for each class") \ + \ + product(intx, ContendedPaddingWidth, 128, \ + "How many bytes to pad the fields/classes marked @Contended with")\ + \ + product(bool, EnableContended, true, \ + "Enable @Contended annotation support") \ + \ + product(bool, RestrictContended, true, \ + "Restrict @Contended to trusted classes") \ + \ product(bool, UseBiasedLocking, true, \ "Enable biased locking in JVM") \ \ diff --git a/hotspot/src/share/vm/runtime/vmStructs.cpp b/hotspot/src/share/vm/runtime/vmStructs.cpp index 09d557cdbf9..e4f9cbb3ee0 100644 --- a/hotspot/src/share/vm/runtime/vmStructs.cpp +++ b/hotspot/src/share/vm/runtime/vmStructs.cpp @@ -2284,10 +2284,17 @@ typedef BinaryTreeDictionary MetablockTreeDictionary; declare_constant(FieldInfo::name_index_offset) \ declare_constant(FieldInfo::signature_index_offset) \ declare_constant(FieldInfo::initval_index_offset) \ - declare_constant(FieldInfo::low_offset) \ - declare_constant(FieldInfo::high_offset) \ + declare_constant(FieldInfo::low_packed_offset) \ + declare_constant(FieldInfo::high_packed_offset) \ declare_constant(FieldInfo::field_slots) \ \ + /*************************************/ \ + /* FieldInfo tag constants */ \ + /*************************************/ \ + \ + declare_preprocessor_constant("FIELDINFO_TAG_SIZE", FIELDINFO_TAG_SIZE) \ + declare_preprocessor_constant("FIELDINFO_TAG_OFFSET", FIELDINFO_TAG_OFFSET) \ + \ /************************************************/ \ /* InstanceKlass InnerClassAttributeOffset enum */ \ /************************************************/ \ From be0c8e9f08c42bcd5543214f5c2868bf036d469b Mon Sep 17 00:00:00 2001 From: Eric Mccorkle Date: Mon, 14 Jan 2013 11:01:39 -0500 Subject: [PATCH 028/138] 8006005: Fix constant pool index validation and alignment trap for method parameter reflection This patch addresses an alignment trap due to the storage format of method parameters data in constMethod. It also adds code to validate constant pool indexes for method parameters data. Reviewed-by: jrose, dholmes --- .../share/vm/classfile/classFileParser.cpp | 18 ++++++++- hotspot/src/share/vm/oops/constMethod.hpp | 7 +++- hotspot/src/share/vm/prims/jvm.cpp | 38 +++++++++++++------ hotspot/src/share/vm/runtime/reflection.cpp | 10 ++++- 4 files changed, 58 insertions(+), 15 deletions(-) diff --git a/hotspot/src/share/vm/classfile/classFileParser.cpp b/hotspot/src/share/vm/classfile/classFileParser.cpp index efb6138e583..a2fec400d94 100644 --- a/hotspot/src/share/vm/classfile/classFileParser.cpp +++ b/hotspot/src/share/vm/classfile/classFileParser.cpp @@ -59,6 +59,7 @@ #include "services/classLoadingService.hpp" #include "services/threadService.hpp" #include "utilities/array.hpp" +#include "utilities/globalDefinitions.hpp" // We generally try to create the oops directly when parsing, rather than // allocating temporary data structures and copying the bytes twice. A @@ -2148,9 +2149,21 @@ methodHandle ClassFileParser::parse_method(ClassLoaderData* loader_data, cp, CHECK_(nullHandle)); } else if (method_attribute_name == vmSymbols::tag_method_parameters()) { method_parameters_length = cfs->get_u1_fast(); + // Track the actual size (note: this is written for clarity; a + // decent compiler will CSE and constant-fold this into a single + // expression) + u2 actual_size = 1; method_parameters_data = cfs->get_u1_buffer(); + actual_size += 2 * method_parameters_length; cfs->skip_u2_fast(method_parameters_length); + actual_size += 4 * method_parameters_length; cfs->skip_u4_fast(method_parameters_length); + // Enforce attribute length + if (method_attribute_length != actual_size) { + classfile_parse_error( + "Invalid MethodParameters method attribute length %u in class file %s", + method_attribute_length, CHECK_(nullHandle)); + } // ignore this attribute if it cannot be reflected if (!SystemDictionary::Parameter_klass_loaded()) method_parameters_length = 0; @@ -2297,7 +2310,10 @@ methodHandle ClassFileParser::parse_method(ClassLoaderData* loader_data, elem[i].name_cp_index = Bytes::get_Java_u2(method_parameters_data); method_parameters_data += 2; - elem[i].flags = Bytes::get_Java_u4(method_parameters_data); + u4 flags = Bytes::get_Java_u4(method_parameters_data); + // This caused an alignment fault on Sparc, if flags was a u4 + elem[i].flags_lo = extract_low_short_from_int(flags); + elem[i].flags_hi = extract_high_short_from_int(flags); method_parameters_data += 4; } } diff --git a/hotspot/src/share/vm/oops/constMethod.hpp b/hotspot/src/share/vm/oops/constMethod.hpp index 08c650278e6..8b593982140 100644 --- a/hotspot/src/share/vm/oops/constMethod.hpp +++ b/hotspot/src/share/vm/oops/constMethod.hpp @@ -122,7 +122,12 @@ class ExceptionTableElement VALUE_OBJ_CLASS_SPEC { class MethodParametersElement VALUE_OBJ_CLASS_SPEC { public: u2 name_cp_index; - u4 flags; + // This has to happen, otherwise it will cause SIGBUS from a + // misaligned u4 on some architectures (ie SPARC) + // because MethodParametersElements are only aligned mod 2 + // within the ConstMethod container u2 flags_hi; + u2 flags_hi; + u2 flags_lo; }; diff --git a/hotspot/src/share/vm/prims/jvm.cpp b/hotspot/src/share/vm/prims/jvm.cpp index f899aac02e7..8b7b2583c5f 100644 --- a/hotspot/src/share/vm/prims/jvm.cpp +++ b/hotspot/src/share/vm/prims/jvm.cpp @@ -1589,6 +1589,12 @@ JVM_ENTRY(jbyteArray, JVM_GetClassTypeAnnotations(JNIEnv *env, jclass cls)) return NULL; JVM_END +static void bounds_check(constantPoolHandle cp, jint index, TRAPS) { + if (!cp->is_within_bounds(index)) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "Constant pool index out of bounds"); + } +} + JVM_ENTRY(jobjectArray, JVM_GetMethodParameters(JNIEnv *env, jobject method)) { JVMWrapper("JVM_GetMethodParameters"); @@ -1598,15 +1604,31 @@ JVM_ENTRY(jobjectArray, JVM_GetMethodParameters(JNIEnv *env, jobject method)) Handle reflected_method (THREAD, JNIHandles::resolve_non_null(method)); const int num_params = mh->method_parameters_length(); - if(0 != num_params) { + if (0 != num_params) { + // make sure all the symbols are properly formatted + for (int i = 0; i < num_params; i++) { + MethodParametersElement* params = mh->method_parameters_start(); + int index = params[i].name_cp_index; + bounds_check(mh->constants(), index, CHECK_NULL); + + if (0 != index && !mh->constants()->tag_at(index).is_utf8()) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), + "Wrong type at constant pool index"); + } + + } + objArrayOop result_oop = oopFactory::new_objArray(SystemDictionary::reflect_Parameter_klass(), num_params, CHECK_NULL); objArrayHandle result (THREAD, result_oop); - for(int i = 0; i < num_params; i++) { + for (int i = 0; i < num_params; i++) { MethodParametersElement* params = mh->method_parameters_start(); - Symbol* const sym = mh->constants()->symbol_at(params[i].name_cp_index); + // For a 0 index, give a NULL symbol + Symbol* const sym = 0 != params[i].name_cp_index ? + mh->constants()->symbol_at(params[i].name_cp_index) : NULL; + int flags = build_int_from_shorts(params[i].flags_lo, params[i].flags_hi); oop param = Reflection::new_parameter(reflected_method, i, sym, - params[i].flags, CHECK_NULL); + flags, CHECK_NULL); result->obj_at_put(i, param); } return (jobjectArray)JNIHandles::make_local(env, result()); @@ -1830,13 +1852,6 @@ JVM_ENTRY(jint, JVM_ConstantPoolGetSize(JNIEnv *env, jobject obj, jobject unused JVM_END -static void bounds_check(constantPoolHandle cp, jint index, TRAPS) { - if (!cp->is_within_bounds(index)) { - THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "Constant pool index out of bounds"); - } -} - - JVM_ENTRY(jclass, JVM_ConstantPoolGetClassAt(JNIEnv *env, jobject obj, jobject unused, jint index)) { JVMWrapper("JVM_ConstantPoolGetClassAt"); @@ -1851,7 +1866,6 @@ JVM_ENTRY(jclass, JVM_ConstantPoolGetClassAt(JNIEnv *env, jobject obj, jobject u } JVM_END - JVM_ENTRY(jclass, JVM_ConstantPoolGetClassAtIfLoaded(JNIEnv *env, jobject obj, jobject unused, jint index)) { JVMWrapper("JVM_ConstantPoolGetClassAtIfLoaded"); diff --git a/hotspot/src/share/vm/runtime/reflection.cpp b/hotspot/src/share/vm/runtime/reflection.cpp index 76a72e4e9b7..8d0cab2a441 100644 --- a/hotspot/src/share/vm/runtime/reflection.cpp +++ b/hotspot/src/share/vm/runtime/reflection.cpp @@ -862,7 +862,15 @@ oop Reflection::new_field(fieldDescriptor* fd, bool intern_name, TRAPS) { oop Reflection::new_parameter(Handle method, int index, Symbol* sym, int flags, TRAPS) { - Handle name = java_lang_String::create_from_symbol(sym, CHECK_NULL); + Handle name; + + // A null symbol here translates to the empty string + if(NULL != sym) { + name = java_lang_String::create_from_symbol(sym, CHECK_NULL); + } else { + name = java_lang_String::create_from_str("", CHECK_NULL); + } + Handle rh = java_lang_reflect_Parameter::create(CHECK_NULL); java_lang_reflect_Parameter::set_name(rh(), name()); java_lang_reflect_Parameter::set_modifiers(rh(), flags); From f8b9f3900cadebd35e5fc1082c069cd94e6436c0 Mon Sep 17 00:00:00 2001 From: Vladimir Ivanov Date: Mon, 14 Jan 2013 08:22:32 -0800 Subject: [PATCH 029/138] 8006095: C1: SIGSEGV w/ -XX:+LogCompilation Avoid printing inlining decision when compilation fails Reviewed-by: kvn, roland --- hotspot/src/share/vm/c1/c1_GraphBuilder.cpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp b/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp index 85c3aa1c9a9..cbaf83b8fea 100644 --- a/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp +++ b/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp @@ -3223,7 +3223,12 @@ bool GraphBuilder::try_inline(ciMethod* callee, bool holder_known, Bytecodes::Co } if (try_inline_full(callee, holder_known, bc, receiver)) return true; - print_inlining(callee, _inline_bailout_msg, /*success*/ false); + + // Entire compilation could fail during try_inline_full call. + // In that case printing inlining decision info is useless. + if (!bailed_out()) + print_inlining(callee, _inline_bailout_msg, /*success*/ false); + return false; } @@ -3753,7 +3758,8 @@ bool GraphBuilder::try_inline_full(ciMethod* callee, bool holder_known, Bytecode push_scope(callee, cont); // the BlockListBuilder for the callee could have bailed out - CHECK_BAILOUT_(false); + if (bailed_out()) + return false; // Temporarily set up bytecode stream so we can append instructions // (only using the bci of this stream) @@ -3819,7 +3825,8 @@ bool GraphBuilder::try_inline_full(ciMethod* callee, bool holder_known, Bytecode iterate_all_blocks(callee_start_block == NULL); // If we bailed out during parsing, return immediately (this is bad news) - if (bailed_out()) return false; + if (bailed_out()) + return false; // iterate_all_blocks theoretically traverses in random order; in // practice, we have only traversed the continuation if we are @@ -3828,9 +3835,6 @@ bool GraphBuilder::try_inline_full(ciMethod* callee, bool holder_known, Bytecode !continuation()->is_set(BlockBegin::was_visited_flag), "continuation should not have been parsed yet if we created it"); - // If we bailed out during parsing, return immediately (this is bad news) - CHECK_BAILOUT_(false); - // At this point we are almost ready to return and resume parsing of // the caller back in the GraphBuilder. The only thing we want to do // first is an optimization: during parsing of the callee we @@ -4171,7 +4175,10 @@ void GraphBuilder::print_inlining(ciMethod* callee, const char* msg, bool succes else log->inline_success("receiver is statically known"); } else { - log->inline_fail(msg); + if (msg != NULL) + log->inline_fail(msg); + else + log->inline_fail("reason unknown"); } } From 561384762672523b6d9757401803dacbbffef2dc Mon Sep 17 00:00:00 2001 From: Alexander Harlap Date: Mon, 14 Jan 2013 13:44:49 -0500 Subject: [PATCH 030/138] 8005639: Move InlineSynchronizedMethods flag from develop to product Move InlineSynchronizedMethods flag from develop to product Reviewed-by: kvn, vladidan --- hotspot/src/share/vm/c1/c1_globals.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hotspot/src/share/vm/c1/c1_globals.hpp b/hotspot/src/share/vm/c1/c1_globals.hpp index e81636c524a..16451f6d526 100644 --- a/hotspot/src/share/vm/c1/c1_globals.hpp +++ b/hotspot/src/share/vm/c1/c1_globals.hpp @@ -147,7 +147,7 @@ "Inline methods containing exception handlers " \ "(NOTE: does not work with current backend)") \ \ - develop(bool, InlineSynchronizedMethods, true, \ + product(bool, InlineSynchronizedMethods, true, \ "Inline synchronized methods") \ \ develop(bool, InlineNIOCheckIndex, true, \ From 95cbed6639bff77ca7b1b5c8233e4e5fa87f0909 Mon Sep 17 00:00:00 2001 From: Alexander Harlap Date: Mon, 14 Jan 2013 13:52:08 -0500 Subject: [PATCH 031/138] 8005204: Code Cache Reduction: command line options implementation Adding more detailed output on CodeCache usage Reviewed-by: kvn, vladidan --- hotspot/src/share/vm/code/codeCache.cpp | 47 ++++++++++++++----- hotspot/src/share/vm/code/codeCache.hpp | 4 +- .../src/share/vm/compiler/compileBroker.cpp | 21 ++++++++- hotspot/src/share/vm/runtime/globals.hpp | 9 ++-- hotspot/src/share/vm/runtime/java.cpp | 6 +++ hotspot/src/share/vm/utilities/vmError.cpp | 2 +- 6 files changed, 70 insertions(+), 19 deletions(-) diff --git a/hotspot/src/share/vm/code/codeCache.cpp b/hotspot/src/share/vm/code/codeCache.cpp index c40a377ebb3..d5b8f8f9a78 100644 --- a/hotspot/src/share/vm/code/codeCache.cpp +++ b/hotspot/src/share/vm/code/codeCache.cpp @@ -30,6 +30,7 @@ #include "code/icBuffer.hpp" #include "code/nmethod.hpp" #include "code/pcDesc.hpp" +#include "compiler/compileBroker.hpp" #include "gc_implementation/shared/markSweep.hpp" #include "memory/allocation.inline.hpp" #include "memory/gcLocker.hpp" @@ -39,6 +40,7 @@ #include "oops/objArrayOop.hpp" #include "oops/oop.inline.hpp" #include "runtime/handles.inline.hpp" +#include "runtime/arguments.hpp" #include "runtime/icache.hpp" #include "runtime/java.hpp" #include "runtime/mutexLocker.hpp" @@ -168,6 +170,8 @@ nmethod* CodeCache::next_nmethod (CodeBlob* cb) { return (nmethod*)cb; } +static size_t maxCodeCacheUsed = 0; + CodeBlob* CodeCache::allocate(int size) { // Do not seize the CodeCache lock here--if the caller has not // already done so, we are going to lose bigtime, since the code @@ -192,6 +196,8 @@ CodeBlob* CodeCache::allocate(int size) { (address)_heap->end() - (address)_heap->begin()); } } + maxCodeCacheUsed = MAX2(maxCodeCacheUsed, ((address)_heap->high_boundary() - + (address)_heap->low_boundary()) - unallocated_capacity()); verify_if_often(); print_trace("allocation", cb, size); return cb; @@ -928,7 +934,14 @@ void CodeCache::print_internals() { FREE_C_HEAP_ARRAY(int, buckets, mtCode); } +#endif // !PRODUCT + void CodeCache::print() { + print_summary(tty); + +#ifndef PRODUCT + if (!Verbose) return; + CodeBlob_sizes live; CodeBlob_sizes dead; @@ -953,7 +966,7 @@ void CodeCache::print() { } - if (Verbose) { + if (WizardMode) { // print the oop_map usage int code_size = 0; int number_of_blobs = 0; @@ -977,20 +990,30 @@ void CodeCache::print() { tty->print_cr(" map size = %d", map_size); } +#endif // !PRODUCT } -#endif // PRODUCT +void CodeCache::print_summary(outputStream* st, bool detailed) { + size_t total = (_heap->high_boundary() - _heap->low_boundary()); + st->print_cr("CodeCache: size=" SIZE_FORMAT "Kb used=" SIZE_FORMAT + "Kb max_used=" SIZE_FORMAT "Kb free=" SIZE_FORMAT + "Kb max_free_chunk=" SIZE_FORMAT "Kb", + total/K, (total - unallocated_capacity())/K, + maxCodeCacheUsed/K, unallocated_capacity()/K, largest_free_block()/K); -void CodeCache::print_bounds(outputStream* st) { - st->print_cr("Code Cache [" INTPTR_FORMAT ", " INTPTR_FORMAT ", " INTPTR_FORMAT ")", - _heap->low_boundary(), - _heap->high(), - _heap->high_boundary()); - st->print_cr(" total_blobs=" UINT32_FORMAT " nmethods=" UINT32_FORMAT - " adapters=" UINT32_FORMAT " free_code_cache=" SIZE_FORMAT "Kb" - " largest_free_block=" SIZE_FORMAT, - nof_blobs(), nof_nmethods(), nof_adapters(), - unallocated_capacity()/K, largest_free_block()); + if (detailed) { + st->print_cr(" bounds [" INTPTR_FORMAT ", " INTPTR_FORMAT ", " INTPTR_FORMAT "]", + _heap->low_boundary(), + _heap->high(), + _heap->high_boundary()); + st->print_cr(" total_blobs=" UINT32_FORMAT " nmethods=" UINT32_FORMAT + " adapters=" UINT32_FORMAT, + nof_blobs(), nof_nmethods(), nof_adapters()); + st->print_cr(" compilation: %s", CompileBroker::should_compile_new_jobs() ? + "enabled" : Arguments::mode() == Arguments::_int ? + "disabled (interpreter mode)" : + "disabled (not enough contiguous free space left)"); + } } void CodeCache::log_state(outputStream* st) { diff --git a/hotspot/src/share/vm/code/codeCache.hpp b/hotspot/src/share/vm/code/codeCache.hpp index 6187ba9a2a7..92ce241b938 100644 --- a/hotspot/src/share/vm/code/codeCache.hpp +++ b/hotspot/src/share/vm/code/codeCache.hpp @@ -145,11 +145,11 @@ class CodeCache : AllStatic { static void prune_scavenge_root_nmethods(); // Printing/debugging - static void print() PRODUCT_RETURN; // prints summary + static void print(); // prints summary static void print_internals(); static void verify(); // verifies the code cache static void print_trace(const char* event, CodeBlob* cb, int size = 0) PRODUCT_RETURN; - static void print_bounds(outputStream* st); // Prints a summary of the bounds of the code cache + static void print_summary(outputStream* st, bool detailed = true); // Prints a summary of the code cache usage static void log_state(outputStream* st); // The full limits of the codeCache diff --git a/hotspot/src/share/vm/compiler/compileBroker.cpp b/hotspot/src/share/vm/compiler/compileBroker.cpp index 73ab865dc74..94c042d32e3 100644 --- a/hotspot/src/share/vm/compiler/compileBroker.cpp +++ b/hotspot/src/share/vm/compiler/compileBroker.cpp @@ -1714,6 +1714,20 @@ void CompileBroker::maybe_block() { } } +// wrapper for CodeCache::print_summary() +static void codecache_print(bool detailed) +{ + ResourceMark rm; + stringStream s; + // Dump code cache into a buffer before locking the tty, + { + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + CodeCache::print_summary(&s, detailed); + } + ttyLocker ttyl; + tty->print_cr(s.as_string()); +} + // ------------------------------------------------------------------ // CompileBroker::invoke_compiler_on_method // @@ -1841,6 +1855,9 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) { tty->print_cr("size: %d time: %d inlined: %d bytes", code_size, (int)time.milliseconds(), task->num_inlined_bytecodes()); } + if (PrintCodeCacheOnCompilation) + codecache_print(/* detailed= */ false); + // Disable compilation, if required. switch (compilable) { case ciEnv::MethodCompilable_never: @@ -1885,6 +1902,7 @@ void CompileBroker::handle_full_code_cache() { UseInterpreter = true; if (UseCompiler || AlwaysCompileLoopMethods ) { if (xtty != NULL) { + ResourceMark rm; stringStream s; // Dump code cache state into a buffer before locking the tty, // because log_state() will use locks causing lock conflicts. @@ -1898,9 +1916,9 @@ void CompileBroker::handle_full_code_cache() { } warning("CodeCache is full. Compiler has been disabled."); warning("Try increasing the code cache size using -XX:ReservedCodeCacheSize="); - CodeCache::print_bounds(tty); #ifndef PRODUCT if (CompileTheWorld || ExitOnFullCodeCache) { + codecache_print(/* detailed= */ true); before_exit(JavaThread::current()); exit_globals(); // will delete tty vm_direct_exit(CompileTheWorld ? 0 : 1); @@ -1913,6 +1931,7 @@ void CompileBroker::handle_full_code_cache() { AlwaysCompileLoopMethods = false; } } + codecache_print(/* detailed= */ true); } // ------------------------------------------------------------------ diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp index 7ea96627e82..0856bd30367 100644 --- a/hotspot/src/share/vm/runtime/globals.hpp +++ b/hotspot/src/share/vm/runtime/globals.hpp @@ -929,11 +929,14 @@ class CommandLineFlags { "Starts debugger when an implicit OS (e.g., NULL) " \ "exception happens") \ \ - notproduct(bool, PrintCodeCache, false, \ - "Print the compiled_code cache when exiting") \ + product(bool, PrintCodeCache, false, \ + "Print the code cache memory usage when exiting") \ \ develop(bool, PrintCodeCache2, false, \ - "Print detailed info on the compiled_code cache when exiting") \ + "Print detailed usage info on the code cache when exiting") \ + \ + product(bool, PrintCodeCacheOnCompilation, false, \ + "Print the code cache memory usage each time a method is compiled") \ \ diagnostic(bool, PrintStubCode, false, \ "Print generated stub code") \ diff --git a/hotspot/src/share/vm/runtime/java.cpp b/hotspot/src/share/vm/runtime/java.cpp index ad6399495b0..61ad935018c 100644 --- a/hotspot/src/share/vm/runtime/java.cpp +++ b/hotspot/src/share/vm/runtime/java.cpp @@ -368,6 +368,12 @@ void print_statistics() { if (CITime) { CompileBroker::print_times(); } + + if (PrintCodeCache) { + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + CodeCache::print(); + } + #ifdef COMPILER2 if (PrintPreciseBiasedLockingStatistics) { OptoRuntime::print_named_counters(); diff --git a/hotspot/src/share/vm/utilities/vmError.cpp b/hotspot/src/share/vm/utilities/vmError.cpp index ddb6e1cf882..d8fe93b64f3 100644 --- a/hotspot/src/share/vm/utilities/vmError.cpp +++ b/hotspot/src/share/vm/utilities/vmError.cpp @@ -702,7 +702,7 @@ void VMError::report(outputStream* st) { if (_verbose && Universe::is_fully_initialized()) { // print code cache information before vm abort - CodeCache::print_bounds(st); + CodeCache::print_summary(st); st->cr(); } From b4546eb428eeecd4c764029150a24b2edea7e429 Mon Sep 17 00:00:00 2001 From: Mikael Vidstedt Date: Mon, 14 Jan 2013 11:00:56 -0800 Subject: [PATCH 032/138] 8005592: ClassLoaderDataGraph::_unloading incorrectly defined as nonstatic in vmStructs Added assertion to catch problem earlier and removed the unused field Reviewed-by: dholmes, acorn --- hotspot/src/share/vm/runtime/vmStructs.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hotspot/src/share/vm/runtime/vmStructs.cpp b/hotspot/src/share/vm/runtime/vmStructs.cpp index e4f9cbb3ee0..8d454992cf8 100644 --- a/hotspot/src/share/vm/runtime/vmStructs.cpp +++ b/hotspot/src/share/vm/runtime/vmStructs.cpp @@ -717,7 +717,6 @@ typedef BinaryTreeDictionary MetablockTreeDictionary; nonstatic_field(ClassLoaderData, _next, ClassLoaderData*) \ \ static_field(ClassLoaderDataGraph, _head, ClassLoaderData*) \ - nonstatic_field(ClassLoaderDataGraph, _unloading, ClassLoaderData*) \ \ /*******************/ \ /* GrowableArrays */ \ @@ -2575,7 +2574,8 @@ typedef BinaryTreeDictionary MetablockTreeDictionary; // This macro checks the type of a VMStructEntry by comparing pointer types #define CHECK_NONSTATIC_VM_STRUCT_ENTRY(typeName, fieldName, type) \ - {typeName *dummyObj = NULL; type* dummy = &dummyObj->fieldName; } + {typeName *dummyObj = NULL; type* dummy = &dummyObj->fieldName; \ + assert(offset_of(typeName, fieldName) < sizeof(typeName), "Illegal nonstatic struct entry, field offset too large"); } // This macro checks the type of a volatile VMStructEntry by comparing pointer types #define CHECK_VOLATILE_NONSTATIC_VM_STRUCT_ENTRY(typeName, fieldName, type) \ From 9fa23770540715b66da85689de27fead41f86d5b Mon Sep 17 00:00:00 2001 From: Naoto Sato Date: Mon, 14 Jan 2013 11:09:53 -0800 Subject: [PATCH 033/138] 7162007: Clean up i18n related caches Reviewed-by: okutsu, ohair --- jdk/make/java/java/FILES_java.gmk | 1 + .../classes/java/text/DateFormatSymbols.java | 8 +- .../classes/java/text/DecimalFormat.java | 30 +- .../java/text/DecimalFormatSymbols.java | 60 +--- .../share/classes/java/text/NumberFormat.java | 1 - jdk/src/share/classes/java/util/Locale.java | 38 +- jdk/src/share/classes/java/util/TimeZone.java | 27 +- .../resources/zh/CollationData_zh_HK.java | 3 +- .../text/resources/zh/FormatData_zh_HK.java | 4 +- .../provider/AuxLocaleProviderAdapter.java | 6 - .../provider/BreakIteratorProviderImpl.java | 16 +- .../provider/CalendarDataProviderImpl.java | 18 +- .../provider/CalendarNameProviderImpl.java | 35 +- .../locale/provider/CollatorProviderImpl.java | 11 +- .../provider/CurrencyNameProviderImpl.java | 8 +- .../provider/JRELocaleProviderAdapter.java | 3 +- .../provider/LocaleNameProviderImpl.java | 8 +- .../provider/LocaleProviderAdapter.java | 29 +- .../util/locale/provider/LocaleResources.java | 324 ++++++++++++++++-- .../provider/ResourceBundleBasedAdapter.java | 37 ++ .../provider/TimeZoneNameProviderImpl.java | 34 +- .../locale/provider/TimeZoneNameUtility.java | 54 +-- .../sun/util/resources/LocaleData.java | 8 + .../resources/zh/CurrencyNames_zh_HK.java | 3 +- .../resources/zh/CurrencyNames_zh_SG.java | 3 +- .../util/resources/zh/LocaleNames_zh_HK.java | 3 +- .../resources/zh/TimeZoneNames_zh_HK.java | 3 +- .../BreakIteratorProviderTest.java | 3 +- .../PluggableLocale/CollatorProviderTest.java | 2 +- .../CurrencyNameProviderTest.java | 2 +- .../DateFormatProviderTest.java | 2 +- .../DateFormatSymbolsProviderTest.java | 2 +- .../DecimalFormatSymbolsProviderTest.java | 10 +- .../LocaleNameProviderTest.java | 2 +- .../NumberFormatProviderTest.java | 6 +- .../TimeZoneNameProviderTest.java | 2 +- 36 files changed, 493 insertions(+), 313 deletions(-) create mode 100644 jdk/src/share/classes/sun/util/locale/provider/ResourceBundleBasedAdapter.java diff --git a/jdk/make/java/java/FILES_java.gmk b/jdk/make/java/java/FILES_java.gmk index f6affbf987b..28894f24abe 100644 --- a/jdk/make/java/java/FILES_java.gmk +++ b/jdk/make/java/java/FILES_java.gmk @@ -227,6 +227,7 @@ JAVA_JAVA_java = \ sun/util/locale/provider/LocaleResources.java \ sun/util/locale/provider/NumberFormatProviderImpl.java \ sun/util/locale/provider/RuleBasedBreakIterator.java \ + sun/util/locale/provider/ResourceBundleBasedAdapter.java \ sun/util/locale/provider/SPILocaleProviderAdapter.java \ sun/util/locale/provider/TimeZoneNameProviderImpl.java \ sun/util/locale/provider/TimeZoneNameUtility.java \ diff --git a/jdk/src/share/classes/java/text/DateFormatSymbols.java b/jdk/src/share/classes/java/text/DateFormatSymbols.java index bde39a66ed1..b4c380d2c05 100644 --- a/jdk/src/share/classes/java/text/DateFormatSymbols.java +++ b/jdk/src/share/classes/java/text/DateFormatSymbols.java @@ -52,6 +52,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import sun.util.locale.provider.LocaleProviderAdapter; import sun.util.locale.provider.LocaleServiceProviderPool; +import sun.util.locale.provider.ResourceBundleBasedAdapter; import sun.util.locale.provider.TimeZoneNameUtility; /** @@ -680,13 +681,10 @@ public class DateFormatSymbols implements Serializable, Cloneable { // Initialize the fields from the ResourceBundle for locale. LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(DateFormatSymbolsProvider.class, locale); // Avoid any potential recursions - switch (adapter.getAdapterType()) { - case HOST: - case SPI: + if (!(adapter instanceof ResourceBundleBasedAdapter)) { adapter = LocaleProviderAdapter.getResourceBundleBased(); - break; } - ResourceBundle resource = adapter.getLocaleData().getDateFormatData(locale); + ResourceBundle resource = ((ResourceBundleBasedAdapter)adapter).getLocaleData().getDateFormatData(locale); // JRE and CLDR use different keys // JRE: Eras, short.Eras and narrow.Eras diff --git a/jdk/src/share/classes/java/text/DecimalFormat.java b/jdk/src/share/classes/java/text/DecimalFormat.java index 6d19bedcbf6..38c930372c1 100644 --- a/jdk/src/share/classes/java/text/DecimalFormat.java +++ b/jdk/src/share/classes/java/text/DecimalFormat.java @@ -54,6 +54,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import sun.util.locale.provider.LocaleProviderAdapter; +import sun.util.locale.provider.ResourceBundleBasedAdapter; /** * DecimalFormat is a concrete subclass of @@ -394,28 +395,17 @@ public class DecimalFormat extends NumberFormat { * @see java.text.NumberFormat#getPercentInstance */ public DecimalFormat() { + // Get the pattern for the default locale. Locale def = Locale.getDefault(Locale.Category.FORMAT); - // try to get the pattern from the cache - String pattern = cachedLocaleData.get(def); - if (pattern == null) { /* cache miss */ - // Get the pattern for the default locale. - LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(NumberFormatProvider.class, def); - switch (adapter.getAdapterType()) { - case HOST: - case SPI: - adapter = LocaleProviderAdapter.getResourceBundleBased(); - break; - } - ResourceBundle rb = adapter.getLocaleData().getNumberFormatData(def); - String[] all = rb.getStringArray("NumberPatterns"); - pattern = all[0]; - /* update cache */ - cachedLocaleData.putIfAbsent(def, pattern); + LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(NumberFormatProvider.class, def); + if (!(adapter instanceof ResourceBundleBasedAdapter)) { + adapter = LocaleProviderAdapter.getResourceBundleBased(); } + String[] all = adapter.getLocaleResources(def).getNumberPatterns(); // Always applyPattern after the symbols are set this.symbols = DecimalFormatSymbols.getInstance(def); - applyPattern(pattern, false); + applyPattern(all[0], false); } @@ -4154,10 +4144,4 @@ public class DecimalFormat extends NumberFormat { // Proclaim JDK 1.1 serial compatibility. static final long serialVersionUID = 864413376551465018L; - - /** - * Cache to hold the NumberPattern of a Locale. - */ - private static final ConcurrentMap cachedLocaleData - = new ConcurrentHashMap<>(3); } diff --git a/jdk/src/share/classes/java/text/DecimalFormatSymbols.java b/jdk/src/share/classes/java/text/DecimalFormatSymbols.java index 81ab956a437..9274208fe8f 100644 --- a/jdk/src/share/classes/java/text/DecimalFormatSymbols.java +++ b/jdk/src/share/classes/java/text/DecimalFormatSymbols.java @@ -52,6 +52,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import sun.util.locale.provider.LocaleProviderAdapter; import sun.util.locale.provider.LocaleServiceProviderPool; +import sun.util.locale.provider.ResourceBundleBasedAdapter; /** * This class represents the set of symbols (such as the decimal separator, @@ -542,48 +543,13 @@ public class DecimalFormatSymbols implements Cloneable, Serializable { private void initialize( Locale locale ) { this.locale = locale; - // get resource bundle data - try the cache first - boolean needCacheUpdate = false; - Object[] data = cachedLocaleData.get(locale); - if (data == null) { /* cache miss */ - LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(DecimalFormatSymbolsProvider.class, locale); - // Avoid potential recursions - switch (adapter.getAdapterType()) { - case HOST: - case SPI: - adapter = LocaleProviderAdapter.getResourceBundleBased(); - break; - } - ResourceBundle rb = adapter.getLocaleData().getNumberFormatData(locale); - data = new Object[3]; - - // NumberElements look up. First, try the Unicode extension - String numElemKey; - String numberType = locale.getUnicodeLocaleType("nu"); - if (numberType != null) { - numElemKey = numberType + ".NumberElements"; - if (rb.containsKey(numElemKey)) { - data[0] = rb.getStringArray(numElemKey); - } - } - - // Next, try DefaultNumberingSystem value - if (data[0] == null && rb.containsKey("DefaultNumberingSystem")) { - numElemKey = rb.getString("DefaultNumberingSystem") + ".NumberElements"; - if (rb.containsKey(numElemKey)) { - data[0] = rb.getStringArray(numElemKey); - } - } - - // Last resort. No need to check the availability. - // Just let it throw MissingResourceException when needed. - if (data[0] == null) { - data[0] = rb.getStringArray("NumberElements"); - } - - needCacheUpdate = true; + // get resource bundle data + LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(DecimalFormatSymbolsProvider.class, locale); + // Avoid potential recursions + if (!(adapter instanceof ResourceBundleBasedAdapter)) { + adapter = LocaleProviderAdapter.getResourceBundleBased(); } - + Object[] data = adapter.getLocaleResources(locale).getDecimalFormatSymbolsData(); String[] numberElements = (String[]) data[0]; decimalSeparator = numberElements[0].charAt(0); @@ -618,7 +584,6 @@ public class DecimalFormatSymbols implements Cloneable, Serializable { currencySymbol = currency.getSymbol(locale); data[1] = intlCurrencySymbol; data[2] = currencySymbol; - needCacheUpdate = true; } } else { // default values @@ -633,10 +598,6 @@ public class DecimalFormatSymbols implements Cloneable, Serializable { // standard decimal separator for all locales that we support. // If that changes, add a new entry to NumberElements. monetarySeparator = decimalSeparator; - - if (needCacheUpdate) { - cachedLocaleData.putIfAbsent(locale, data); - } } /** @@ -850,11 +811,4 @@ public class DecimalFormatSymbols implements Cloneable, Serializable { * @since JDK 1.1.6 */ private int serialVersionOnStream = currentSerialVersion; - - /** - * cache to hold the NumberElements and the Currency - * of a Locale. - */ - private static final ConcurrentMap cachedLocaleData - = new ConcurrentHashMap<>(3); } diff --git a/jdk/src/share/classes/java/text/NumberFormat.java b/jdk/src/share/classes/java/text/NumberFormat.java index e155b81b60c..eaed665e54c 100644 --- a/jdk/src/share/classes/java/text/NumberFormat.java +++ b/jdk/src/share/classes/java/text/NumberFormat.java @@ -56,7 +56,6 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.spi.LocaleServiceProvider; import sun.util.locale.provider.LocaleProviderAdapter; import sun.util.locale.provider.LocaleServiceProviderPool; -import sun.util.resources.LocaleData; /** * NumberFormat is the abstract base class for all number diff --git a/jdk/src/share/classes/java/util/Locale.java b/jdk/src/share/classes/java/util/Locale.java index 9d469e4660c..198148206d1 100644 --- a/jdk/src/share/classes/java/util/Locale.java +++ b/jdk/src/share/classes/java/util/Locale.java @@ -50,7 +50,6 @@ import java.text.MessageFormat; import java.util.spi.LocaleNameProvider; import sun.security.action.GetPropertyAction; -import sun.util.locale.provider.LocaleServiceProviderPool; import sun.util.locale.BaseLocale; import sun.util.locale.InternalLocaleBuilder; import sun.util.locale.LanguageTag; @@ -61,7 +60,9 @@ import sun.util.locale.LocaleSyntaxException; import sun.util.locale.LocaleUtils; import sun.util.locale.ParseStatus; import sun.util.locale.provider.LocaleProviderAdapter; -import sun.util.resources.OpenListResourceBundle; +import sun.util.locale.provider.LocaleResources; +import sun.util.locale.provider.LocaleServiceProviderPool; +import sun.util.locale.provider.ResourceBundleBasedAdapter; /** * A Locale object represents a specific geographical, political, @@ -1779,20 +1780,15 @@ public final class Locale implements Cloneable, Serializable { if (baseLocale.getVariant().length() == 0) return ""; - OpenListResourceBundle bundle = LocaleProviderAdapter.forJRE().getLocaleData().getLocaleNames(inLocale); + LocaleResources lr = LocaleProviderAdapter.forJRE().getLocaleResources(inLocale); - String names[] = getDisplayVariantArray(bundle, inLocale); + String names[] = getDisplayVariantArray(inLocale); // Get the localized patterns for formatting a list, and use // them to format the list. - String listPattern = null; - String listCompositionPattern = null; - try { - listPattern = bundle.getString("ListPattern"); - listCompositionPattern = bundle.getString("ListCompositionPattern"); - } catch (MissingResourceException e) { - } - return formatList(names, listPattern, listCompositionPattern); + return formatList(names, + lr.getLocaleName("ListPattern"), + lr.getLocaleName("ListCompositionPattern")); } /** @@ -1837,23 +1833,17 @@ public final class Locale implements Cloneable, Serializable { * @throws NullPointerException if inLocale is null */ public String getDisplayName(Locale inLocale) { - OpenListResourceBundle bundle = LocaleProviderAdapter.forJRE().getLocaleData().getLocaleNames(inLocale); + LocaleResources lr = LocaleProviderAdapter.forJRE().getLocaleResources(inLocale); String languageName = getDisplayLanguage(inLocale); String scriptName = getDisplayScript(inLocale); String countryName = getDisplayCountry(inLocale); - String[] variantNames = getDisplayVariantArray(bundle, inLocale); + String[] variantNames = getDisplayVariantArray(inLocale); // Get the localized patterns for formatting a display name. - String displayNamePattern = null; - String listPattern = null; - String listCompositionPattern = null; - try { - displayNamePattern = bundle.getString("DisplayNamePattern"); - listPattern = bundle.getString("ListPattern"); - listCompositionPattern = bundle.getString("ListCompositionPattern"); - } catch (MissingResourceException e) { - } + String displayNamePattern = lr.getLocaleName("DisplayNamePattern"); + String listPattern = lr.getLocaleName("ListPattern"); + String listCompositionPattern = lr.getLocaleName("ListCompositionPattern"); // The display name consists of a main name, followed by qualifiers. // Typically, the format is "MainName (Qualifier, Qualifier)" but this @@ -2005,7 +1995,7 @@ public final class Locale implements Cloneable, Serializable { * @param bundle the ResourceBundle to use to get the display names * @return an array of display names, possible of zero length. */ - private String[] getDisplayVariantArray(OpenListResourceBundle bundle, Locale inLocale) { + private String[] getDisplayVariantArray(Locale inLocale) { // Split the variant name into tokens separated by '_'. StringTokenizer tokenizer = new StringTokenizer(baseLocale.getVariant(), "_"); String[] names = new String[tokenizer.countTokens()]; diff --git a/jdk/src/share/classes/java/util/TimeZone.java b/jdk/src/share/classes/java/util/TimeZone.java index 9d9fbcc8fd1..d79e201ce7a 100644 --- a/jdk/src/share/classes/java/util/TimeZone.java +++ b/jdk/src/share/classes/java/util/TimeZone.java @@ -430,32 +430,7 @@ abstract public class TimeZone implements Serializable, Cloneable { } private static String[] getDisplayNames(String id, Locale locale) { - Map>> displayNames = DisplayNames.CACHE; - - SoftReference> ref = displayNames.get(id); - if (ref != null) { - Map perLocale = ref.get(); - if (perLocale != null) { - String[] names = perLocale.get(locale); - if (names != null) { - return names; - } - names = TimeZoneNameUtility.retrieveDisplayNames(id, locale); - if (names != null) { - perLocale.put(locale, names); - } - return names; - } - } - - String[] names = TimeZoneNameUtility.retrieveDisplayNames(id, locale); - if (names != null) { - Map perLocale = new ConcurrentHashMap<>(); - perLocale.put(locale, names); - ref = new SoftReference<>(perLocale); - displayNames.put(id, ref); - } - return names; + return TimeZoneNameUtility.retrieveDisplayNames(id, locale); } /** diff --git a/jdk/src/share/classes/sun/text/resources/zh/CollationData_zh_HK.java b/jdk/src/share/classes/sun/text/resources/zh/CollationData_zh_HK.java index c8eeba7e1ec..6cf5b0dd62b 100644 --- a/jdk/src/share/classes/sun/text/resources/zh/CollationData_zh_HK.java +++ b/jdk/src/share/classes/sun/text/resources/zh/CollationData_zh_HK.java @@ -47,12 +47,13 @@ import java.util.ListResourceBundle; import java.util.Locale; import java.util.ResourceBundle; import sun.util.locale.provider.LocaleProviderAdapter; +import sun.util.locale.provider.ResourceBundleBasedAdapter; public class CollationData_zh_HK extends ListResourceBundle { // reparent to zh_TW for traditional Chinese collation sequence public CollationData_zh_HK() { - ResourceBundle bundle = LocaleProviderAdapter.forJRE().getLocaleData().getCollationData(Locale.TAIWAN); + ResourceBundle bundle = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getCollationData(Locale.TAIWAN); setParent(bundle); } diff --git a/jdk/src/share/classes/sun/text/resources/zh/FormatData_zh_HK.java b/jdk/src/share/classes/sun/text/resources/zh/FormatData_zh_HK.java index 9e2fd3383c8..23c3f6e43fe 100644 --- a/jdk/src/share/classes/sun/text/resources/zh/FormatData_zh_HK.java +++ b/jdk/src/share/classes/sun/text/resources/zh/FormatData_zh_HK.java @@ -44,12 +44,14 @@ import java.util.ListResourceBundle; import java.util.Locale; import java.util.ResourceBundle; import sun.util.locale.provider.LocaleProviderAdapter; +import sun.util.locale.provider.ResourceBundleBasedAdapter; public class FormatData_zh_HK extends ListResourceBundle { // reparent to zh_TW for traditional Chinese names public FormatData_zh_HK() { - ResourceBundle bundle = LocaleProviderAdapter.forJRE().getLocaleData().getDateFormatData(Locale.TAIWAN); + ResourceBundle bundle = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()) + .getLocaleData().getDateFormatData(Locale.TAIWAN); setParent(bundle); } diff --git a/jdk/src/share/classes/sun/util/locale/provider/AuxLocaleProviderAdapter.java b/jdk/src/share/classes/sun/util/locale/provider/AuxLocaleProviderAdapter.java index a68a8ba956d..78edef1b7c0 100644 --- a/jdk/src/share/classes/sun/util/locale/provider/AuxLocaleProviderAdapter.java +++ b/jdk/src/share/classes/sun/util/locale/provider/AuxLocaleProviderAdapter.java @@ -43,7 +43,6 @@ import java.util.spi.CurrencyNameProvider; import java.util.spi.LocaleNameProvider; import java.util.spi.LocaleServiceProvider; import java.util.spi.TimeZoneNameProvider; -import sun.util.resources.LocaleData; /** * An abstract parent class for the @@ -146,11 +145,6 @@ public abstract class AuxLocaleProviderAdapter extends LocaleProviderAdapter { return null; } - @Override - public LocaleData getLocaleData() { - return null; - } - private static Locale[] availableLocales = null; @Override diff --git a/jdk/src/share/classes/sun/util/locale/provider/BreakIteratorProviderImpl.java b/jdk/src/share/classes/sun/util/locale/provider/BreakIteratorProviderImpl.java index 35a8a7c1f33..1a611a087e1 100644 --- a/jdk/src/share/classes/sun/util/locale/provider/BreakIteratorProviderImpl.java +++ b/jdk/src/share/classes/sun/util/locale/provider/BreakIteratorProviderImpl.java @@ -25,12 +25,12 @@ package sun.util.locale.provider; +import java.io.IOException; import java.text.BreakIterator; import java.text.spi.BreakIteratorProvider; import java.util.Locale; -import java.util.ResourceBundle; +import java.util.MissingResourceException; import java.util.Set; -import sun.util.resources.LocaleData; /** * Concrete implementation of the {@link java.text.spi.BreakIteratorProvider @@ -159,24 +159,22 @@ public class BreakIteratorProviderImpl extends BreakIteratorProvider throw new NullPointerException(); } - ResourceBundle bundle = LocaleData.getBundle( - LocaleProviderAdapter.Type.JRE.getTextResourcesPackage() + ".BreakIteratorInfo", locale); - String[] classNames = bundle.getStringArray("BreakIteratorClasses"); - - String dataFile = bundle.getString(dataName); + LocaleResources lr = LocaleProviderAdapter.forJRE().getLocaleResources(locale); + String[] classNames = (String[]) lr.getBreakIteratorInfo("BreakIteratorClasses"); + String dataFile = (String) lr.getBreakIteratorInfo(dataName); try { switch (classNames[type]) { case "RuleBasedBreakIterator": return new RuleBasedBreakIterator(dataFile); case "DictionaryBasedBreakIterator": - String dictionaryFile = bundle.getString(dictionaryName); + String dictionaryFile = (String) lr.getBreakIteratorInfo(dictionaryName); return new DictionaryBasedBreakIterator(dataFile, dictionaryFile); default: throw new IllegalArgumentException("Invalid break iterator class \"" + classNames[type] + "\""); } - } catch (Exception e) { + } catch (IOException | MissingResourceException | IllegalArgumentException e) { throw new InternalError(e.toString(), e); } } diff --git a/jdk/src/share/classes/sun/util/locale/provider/CalendarDataProviderImpl.java b/jdk/src/share/classes/sun/util/locale/provider/CalendarDataProviderImpl.java index 16d08b3cbe3..136889fbd58 100644 --- a/jdk/src/share/classes/sun/util/locale/provider/CalendarDataProviderImpl.java +++ b/jdk/src/share/classes/sun/util/locale/provider/CalendarDataProviderImpl.java @@ -24,10 +24,7 @@ */ package sun.util.locale.provider; -import java.util.Calendar; -import static java.util.Calendar.*; import java.util.Locale; -import java.util.ResourceBundle; import java.util.Set; import java.util.spi.CalendarDataProvider; @@ -49,12 +46,14 @@ public class CalendarDataProviderImpl extends CalendarDataProvider implements Av @Override public int getFirstDayOfWeek(Locale locale) { - return getIntData(CalendarDataUtility.FIRST_DAY_OF_WEEK, locale); + return LocaleProviderAdapter.forType(type).getLocaleResources(locale) + .getCalendarData(CalendarDataUtility.FIRST_DAY_OF_WEEK); } @Override public int getMinimalDaysInFirstWeek(Locale locale) { - return getIntData(CalendarDataUtility.MINIMAL_DAYS_IN_FIRST_WEEK, locale); + return LocaleProviderAdapter.forType(type).getLocaleResources(locale) + .getCalendarData(CalendarDataUtility.MINIMAL_DAYS_IN_FIRST_WEEK); } @Override @@ -66,13 +65,4 @@ public class CalendarDataProviderImpl extends CalendarDataProvider implements Av public Set getAvailableLanguageTags() { return langtags; } - - private int getIntData(String key, Locale locale) { - ResourceBundle rb = LocaleProviderAdapter.forType(type).getLocaleData().getCalendarData(locale); - if (rb.containsKey(key)) { - String firstday = rb.getString(key); - return Integer.parseInt(firstday); - } - return 0; - } } diff --git a/jdk/src/share/classes/sun/util/locale/provider/CalendarNameProviderImpl.java b/jdk/src/share/classes/sun/util/locale/provider/CalendarNameProviderImpl.java index 20be38607f8..3fe97ddf778 100644 --- a/jdk/src/share/classes/sun/util/locale/provider/CalendarNameProviderImpl.java +++ b/jdk/src/share/classes/sun/util/locale/provider/CalendarNameProviderImpl.java @@ -28,7 +28,6 @@ import static java.util.Calendar.*; import java.util.Comparator; import java.util.Locale; import java.util.Map; -import java.util.ResourceBundle; import java.util.Set; import java.util.TreeMap; import java.util.spi.CalendarNameProvider; @@ -54,22 +53,19 @@ public class CalendarNameProviderImpl extends CalendarNameProvider implements Av String name = null; String key = getResourceKey(calendarType, field, style); if (key != null) { - ResourceBundle rb = LocaleProviderAdapter.forType(type).getLocaleData().getDateFormatData(locale); - if (rb.containsKey(key)) { - String[] strings = rb.getStringArray(key); - if (strings.length > 0) { - if (field == DAY_OF_WEEK || field == YEAR) { - --value; - } - name = strings[value]; - // If name is empty in standalone, try its `format' style. - if (name.length() == 0 - && (style == SHORT_STANDALONE || style == LONG_STANDALONE - || style == NARROW_STANDALONE)) { - name = getDisplayName(calendarType, field, value, - getBaseStyle(style), - locale); - } + String[] strings = LocaleProviderAdapter.forType(type).getLocaleResources(locale).getCalendarNames(key); + if (strings != null && strings.length > 0) { + if (field == DAY_OF_WEEK || field == YEAR) { + --value; + } + name = strings[value]; + // If name is empty in standalone, try its `format' style. + if (name.length() == 0 + && (style == SHORT_STANDALONE || style == LONG_STANDALONE + || style == NARROW_STANDALONE)) { + name = getDisplayName(calendarType, field, value, + getBaseStyle(style), + locale); } } } @@ -100,9 +96,8 @@ public class CalendarNameProviderImpl extends CalendarNameProvider implements Av String key = getResourceKey(calendarType, field, style); Map map = new TreeMap<>(LengthBasedComparator.INSTANCE); if (key != null) { - ResourceBundle rb = LocaleProviderAdapter.forType(type).getLocaleData().getDateFormatData(locale); - if (rb.containsKey(key)) { - String[] strings = rb.getStringArray(key); + String[] strings = LocaleProviderAdapter.forType(type).getLocaleResources(locale).getCalendarNames(key); + if (strings != null) { if (!hasDuplicates(strings)) { if (field == YEAR) { if (strings.length > 0) { diff --git a/jdk/src/share/classes/sun/util/locale/provider/CollatorProviderImpl.java b/jdk/src/share/classes/sun/util/locale/provider/CollatorProviderImpl.java index ba9ba7be644..3f997cf9b40 100644 --- a/jdk/src/share/classes/sun/util/locale/provider/CollatorProviderImpl.java +++ b/jdk/src/share/classes/sun/util/locale/provider/CollatorProviderImpl.java @@ -45,8 +45,6 @@ import java.text.ParseException; import java.text.RuleBasedCollator; import java.text.spi.CollatorProvider; import java.util.Locale; -import java.util.MissingResourceException; -import java.util.ResourceBundle; import java.util.Set; /** @@ -102,14 +100,7 @@ public class CollatorProviderImpl extends CollatorProvider implements AvailableL // Load the resource of the desired locale from resource // manager. - String colString = ""; - try { - ResourceBundle resource = LocaleProviderAdapter.forType(type).getLocaleData().getCollationData(locale); - - colString = resource.getString("Rule"); - } catch (MissingResourceException e) { - // Use default values - } + String colString = LocaleProviderAdapter.forType(type).getLocaleResources(locale).getCollationData(); try { result = new RuleBasedCollator(CollationRules.DEFAULTRULES + diff --git a/jdk/src/share/classes/sun/util/locale/provider/CurrencyNameProviderImpl.java b/jdk/src/share/classes/sun/util/locale/provider/CurrencyNameProviderImpl.java index cfa805fddfc..6e368bca85f 100644 --- a/jdk/src/share/classes/sun/util/locale/provider/CurrencyNameProviderImpl.java +++ b/jdk/src/share/classes/sun/util/locale/provider/CurrencyNameProviderImpl.java @@ -26,7 +26,6 @@ package sun.util.locale.provider; import java.util.Locale; -import java.util.ResourceBundle; import java.util.Set; import java.util.spi.CurrencyNameProvider; @@ -120,11 +119,6 @@ public class CurrencyNameProviderImpl extends CurrencyNameProvider throw new NullPointerException(); } - ResourceBundle bundle = LocaleProviderAdapter.forType(type).getLocaleData().getCurrencyNames(locale); - if (bundle.containsKey(key)) { - return bundle.getString(key); - } - - return null; + return LocaleProviderAdapter.forType(type).getLocaleResources(locale).getCurrencyName(key); } } diff --git a/jdk/src/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java b/jdk/src/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java index 9f86f7a35ab..cd33bc2140d 100644 --- a/jdk/src/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java +++ b/jdk/src/share/classes/sun/util/locale/provider/JRELocaleProviderAdapter.java @@ -54,7 +54,7 @@ import sun.util.resources.LocaleData; * @author Naoto Sato * @author Masayoshi Okutsu */ -public class JRELocaleProviderAdapter extends LocaleProviderAdapter { +public class JRELocaleProviderAdapter extends LocaleProviderAdapter implements ResourceBundleBasedAdapter { private static final String LOCALE_DATA_JAR_NAME = "localedata.jar"; @@ -296,6 +296,7 @@ public class JRELocaleProviderAdapter extends LocaleProviderAdapter { return lr; } + // ResourceBundleBasedAdapter method implementation @Override public LocaleData getLocaleData() { if (localeData == null) { diff --git a/jdk/src/share/classes/sun/util/locale/provider/LocaleNameProviderImpl.java b/jdk/src/share/classes/sun/util/locale/provider/LocaleNameProviderImpl.java index 952078fd5c3..16f71b1158e 100644 --- a/jdk/src/share/classes/sun/util/locale/provider/LocaleNameProviderImpl.java +++ b/jdk/src/share/classes/sun/util/locale/provider/LocaleNameProviderImpl.java @@ -26,7 +26,6 @@ package sun.util.locale.provider; import java.util.Locale; -import java.util.ResourceBundle; import java.util.Set; import java.util.spi.LocaleNameProvider; @@ -174,12 +173,7 @@ public class LocaleNameProviderImpl extends LocaleNameProvider implements Availa throw new NullPointerException(); } - ResourceBundle rb = LocaleProviderAdapter.forType(type).getLocaleData().getLocaleNames(locale); - if (rb.containsKey(key)) { - return rb.getString(key); - } - - return null; + return LocaleProviderAdapter.forType(type).getLocaleResources(locale).getLocaleName(key); } @Override diff --git a/jdk/src/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java b/jdk/src/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java index 17f5c0999d5..2f12f6540ce 100644 --- a/jdk/src/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java +++ b/jdk/src/share/classes/sun/util/locale/provider/LocaleProviderAdapter.java @@ -37,6 +37,8 @@ import java.util.List; import java.util.Locale; import java.util.ResourceBundle; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.spi.CalendarDataProvider; import java.util.spi.CalendarNameProvider; import java.util.spi.CurrencyNameProvider; @@ -44,7 +46,6 @@ import java.util.spi.LocaleNameProvider; import java.util.spi.LocaleServiceProvider; import java.util.spi.TimeZoneNameProvider; import sun.util.cldr.CLDRLocaleProviderAdapter; -import sun.util.resources.LocaleData; /** * The LocaleProviderAdapter abstract class. @@ -119,6 +120,12 @@ public abstract class LocaleProviderAdapter { */ private static LocaleProviderAdapter fallbackLocaleProviderAdapter = null; + /** + * Adapter lookup cache. + */ + private static ConcurrentMap, ConcurrentMap> + adapterCache = new ConcurrentHashMap<>(); + static { String order = AccessController.doPrivileged( new sun.security.action.GetPropertyAction("java.locale.providers")); @@ -210,9 +217,23 @@ public abstract class LocaleProviderAdapter { */ public static LocaleProviderAdapter getAdapter(Class providerClass, Locale locale) { + LocaleProviderAdapter adapter; + + // cache lookup + ConcurrentMap adapterMap = adapterCache.get(providerClass); + if (adapterMap != null) { + if ((adapter = adapterMap.get(locale)) != null) { + return adapter; + } + } else { + adapterMap = new ConcurrentHashMap<>(); + adapterCache.putIfAbsent(providerClass, adapterMap); + } + // Fast look-up for the given locale - LocaleProviderAdapter adapter = findAdapter(providerClass, locale); + adapter = findAdapter(providerClass, locale); if (adapter != null) { + adapterMap.putIfAbsent(locale, adapter); return adapter; } @@ -226,11 +247,13 @@ public abstract class LocaleProviderAdapter { } adapter = findAdapter(providerClass, loc); if (adapter != null) { + adapterMap.putIfAbsent(locale, adapter); return adapter; } } // returns the adapter for FALLBACK as the last resort + adapterMap.putIfAbsent(locale, fallbackLocaleProviderAdapter); return fallbackLocaleProviderAdapter; } @@ -398,7 +421,5 @@ public abstract class LocaleProviderAdapter { public abstract LocaleResources getLocaleResources(Locale locale); - public abstract LocaleData getLocaleData(); - public abstract Locale[] getAvailableLocales(); } diff --git a/jdk/src/share/classes/sun/util/locale/provider/LocaleResources.java b/jdk/src/share/classes/sun/util/locale/provider/LocaleResources.java index ff389f61b35..fa7d499676e 100644 --- a/jdk/src/share/classes/sun/util/locale/provider/LocaleResources.java +++ b/jdk/src/share/classes/sun/util/locale/provider/LocaleResources.java @@ -40,43 +40,295 @@ package sun.util.locale.provider; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; import java.text.MessageFormat; import java.util.Calendar; +import java.util.LinkedHashSet; import java.util.Locale; +import java.util.Map; import java.util.ResourceBundle; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import sun.util.calendar.ZoneInfo; +import sun.util.resources.LocaleData; +import sun.util.resources.OpenListResourceBundle; import sun.util.resources.TimeZoneNamesBundle; /** - * Central accessor to locale-dependent resources. + * Central accessor to locale-dependent resources for JRE/CLDR provider adapters. * * @author Masayoshi Okutsu + * @author Naoto Sato */ public class LocaleResources { - private final LocaleProviderAdapter adapter; private final Locale locale; + private final LocaleData localeData; + private final LocaleProviderAdapter.Type type; // Resource cache - private ConcurrentMap cache = new ConcurrentHashMap<>(); + private ConcurrentMap cache = new ConcurrentHashMap<>(); + private ReferenceQueue referenceQueue = new ReferenceQueue<>(); + // cache key prefixes + private static final String BREAK_ITERATOR_INFO = "BII."; + private static final String CALENDAR_DATA = "CALD."; + private static final String COLLATION_DATA_CACHEKEY = "COLD"; + private static final String DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY = "DFSD"; + private static final String CURRENCY_NAMES = "CN."; + private static final String LOCALE_NAMES = "LN."; + private static final String TIME_ZONE_NAMES = "TZN."; + private static final String ZONE_IDS_CACHEKEY = "ZID"; + private static final String CALENDAR_NAMES = "CALN."; + private static final String NUMBER_PATTERNS_CACHEKEY = "NP"; + private static final String DATE_TIME_PATTERN = "DTP."; - LocaleResources(LocaleProviderAdapter adapter, Locale locale) { - this.adapter = adapter; + // null singleton cache value + private static final Object NULLOBJECT = new Object(); + + LocaleResources(ResourceBundleBasedAdapter adapter, Locale locale) { this.locale = locale; + this.localeData = adapter.getLocaleData(); + type = ((LocaleProviderAdapter)adapter).getAdapterType(); } - public TimeZoneNamesBundle getTimeZoneNames() { - TimeZoneNamesBundle tznames = (TimeZoneNamesBundle) cache.get("TimeZoneNames"); - if (tznames == null) { - tznames = adapter.getLocaleData().getTimeZoneNames(locale); - TimeZoneNamesBundle tznb = (TimeZoneNamesBundle) cache.putIfAbsent("TimeZoneNames", tznames); - if (tznb != null) { - tznames = tznb; + private void removeEmptyReferences() { + Object ref; + while ((ref = referenceQueue.poll()) != null) { + cache.remove(((ResourceReference)ref).getCacheKey()); + } + } + + Object getBreakIteratorInfo(String key) { + Object biInfo; + String cacheKey = BREAK_ITERATOR_INFO + key; + + removeEmptyReferences(); + ResourceReference data = cache.get(cacheKey); + if (data == null || ((biInfo = data.get()) == null)) { + biInfo = localeData.getBreakIteratorInfo(locale).getObject(key); + cache.put(cacheKey, new ResourceReference(cacheKey, biInfo, referenceQueue)); + } + + return biInfo; + } + + int getCalendarData(String key) { + Integer caldata; + String cacheKey = CALENDAR_DATA + key; + + removeEmptyReferences(); + + ResourceReference data = cache.get(cacheKey); + if (data == null || ((caldata = (Integer) data.get()) == null)) { + ResourceBundle rb = localeData.getCalendarData(locale); + if (rb.containsKey(key)) { + caldata = Integer.parseInt(rb.getString(key)); + } else { + caldata = 0; + } + + cache.put(cacheKey, + new ResourceReference(cacheKey, (Object) caldata, referenceQueue)); + } + + return caldata; + } + + public String getCollationData() { + String key = "Rule"; + String coldata = ""; + + removeEmptyReferences(); + ResourceReference data = cache.get(COLLATION_DATA_CACHEKEY); + if (data == null || ((coldata = (String) data.get()) == null)) { + ResourceBundle rb = localeData.getCollationData(locale); + if (rb.containsKey(key)) { + coldata = rb.getString(key); + } + cache.put(COLLATION_DATA_CACHEKEY, + new ResourceReference(COLLATION_DATA_CACHEKEY, (Object) coldata, referenceQueue)); + } + + return coldata; + } + + public Object[] getDecimalFormatSymbolsData() { + Object[] dfsdata; + + removeEmptyReferences(); + ResourceReference data = cache.get(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY); + if (data == null || ((dfsdata = (Object[]) data.get()) == null)) { + // Note that only dfsdata[0] is prepared here in this method. Other + // elements are provided by the caller, yet they are cached here. + ResourceBundle rb = localeData.getNumberFormatData(locale); + dfsdata = new Object[3]; + + // NumberElements look up. First, try the Unicode extension + String numElemKey; + String numberType = locale.getUnicodeLocaleType("nu"); + if (numberType != null) { + numElemKey = numberType + ".NumberElements"; + if (rb.containsKey(numElemKey)) { + dfsdata[0] = rb.getStringArray(numElemKey); + } + } + + // Next, try DefaultNumberingSystem value + if (dfsdata[0] == null && rb.containsKey("DefaultNumberingSystem")) { + numElemKey = rb.getString("DefaultNumberingSystem") + ".NumberElements"; + if (rb.containsKey(numElemKey)) { + dfsdata[0] = rb.getStringArray(numElemKey); + } + } + + // Last resort. No need to check the availability. + // Just let it throw MissingResourceException when needed. + if (dfsdata[0] == null) { + dfsdata[0] = rb.getStringArray("NumberElements"); + } + + cache.put(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY, + new ResourceReference(DECIMAL_FORMAT_SYMBOLS_DATA_CACHEKEY, (Object) dfsdata, referenceQueue)); + } + + return dfsdata; + } + + public String getCurrencyName(String key) { + Object currencyName = null; + String cacheKey = CURRENCY_NAMES + key; + + removeEmptyReferences(); + ResourceReference data = cache.get(cacheKey); + + if (data != null && ((currencyName = data.get()) != null)) { + if (currencyName.equals(NULLOBJECT)) { + currencyName = null; + } + + return (String) currencyName; + } + + OpenListResourceBundle olrb = localeData.getCurrencyNames(locale); + + if (olrb.containsKey(key)) { + currencyName = olrb.getObject(key); + cache.put(cacheKey, + new ResourceReference(cacheKey, currencyName, referenceQueue)); + } + + return (String) currencyName; + } + + public String getLocaleName(String key) { + Object localeName = null; + String cacheKey = LOCALE_NAMES + key; + + removeEmptyReferences(); + ResourceReference data = cache.get(cacheKey); + + if (data != null && ((localeName = data.get()) != null)) { + if (localeName.equals(NULLOBJECT)) { + localeName = null; + } + + return (String) localeName; + } + + OpenListResourceBundle olrb = localeData.getLocaleNames(locale); + + if (olrb.containsKey(key)) { + localeName = olrb.getObject(key); + cache.put(cacheKey, + new ResourceReference(cacheKey, localeName, referenceQueue)); + } + + return (String) localeName; + } + + String[] getTimeZoneNames(String key, int size) { + String[] names = null; + String cacheKey = TIME_ZONE_NAMES + key; + + removeEmptyReferences(); + ResourceReference data = cache.get(cacheKey); + + if (data == null || ((names = (String[]) data.get()) == null)) { + TimeZoneNamesBundle tznb = localeData.getTimeZoneNames(locale); + if (tznb.containsKey(key)) { + names = tznb.getStringArray(key, size); + cache.put(cacheKey, + new ResourceReference(cacheKey, (Object) names, referenceQueue)); } } - return tznames; + + return names; + } + + @SuppressWarnings("unchecked") + Set getZoneIDs() { + Set zoneIDs = null; + + removeEmptyReferences(); + ResourceReference data = cache.get(ZONE_IDS_CACHEKEY); + if (data == null || ((zoneIDs = (Set) data.get()) == null)) { + TimeZoneNamesBundle rb = localeData.getTimeZoneNames(locale); + zoneIDs = rb.keySet(); + cache.put(ZONE_IDS_CACHEKEY, + new ResourceReference(ZONE_IDS_CACHEKEY, (Object) zoneIDs, referenceQueue)); + } + + return zoneIDs; + } + + // zoneStrings are cached separately in TimeZoneNameUtility. + String[][] getZoneStrings() { + TimeZoneNamesBundle rb = localeData.getTimeZoneNames(locale); + Set keyset = getZoneIDs(); + // Use a LinkedHashSet to preseve the order + Set value = new LinkedHashSet<>(); + for (String key : keyset) { + value.add(rb.getStringArray(key)); + } + + // Add aliases data for CLDR + if (type == LocaleProviderAdapter.Type.CLDR) { + // Note: TimeZoneNamesBundle creates a String[] on each getStringArray call. + Map aliases = ZoneInfo.getAliasTable(); + for (String alias : aliases.keySet()) { + if (!keyset.contains(alias)) { + String tzid = aliases.get(alias); + if (keyset.contains(tzid)) { + String[] val = rb.getStringArray(tzid); + val[0] = alias; + value.add(val); + } + } + } + } + return value.toArray(new String[0][]); + } + + String[] getCalendarNames(String key) { + String[] names = null; + String cacheKey = CALENDAR_NAMES + key; + + removeEmptyReferences(); + ResourceReference data = cache.get(cacheKey); + + if (data == null || ((names = (String[]) data.get()) == null)) { + ResourceBundle rb = localeData.getDateFormatData(locale); + if (rb.containsKey(key)) { + names = rb.getStringArray(key); + cache.put(cacheKey, + new ResourceReference(cacheKey, (Object) names, referenceQueue)); + } + } + + return names; } public String getDateTimePattern(int timeStyle, int dateStyle, Calendar cal) { @@ -120,32 +372,54 @@ public class LocaleResources { } public String[] getNumberPatterns() { - /* try the cache first */ - String[] numberPatterns = (String[]) cache.get("NumberPatterns"); - if (numberPatterns == null) { /* cache miss */ - ResourceBundle resource = adapter.getLocaleData().getNumberFormatData(locale); + String[] numberPatterns = null; + + removeEmptyReferences(); + ResourceReference data = cache.get(NUMBER_PATTERNS_CACHEKEY); + + if (data == null || ((numberPatterns = (String[]) data.get()) == null)) { + ResourceBundle resource = localeData.getNumberFormatData(locale); numberPatterns = resource.getStringArray("NumberPatterns"); - /* update cache */ - cache.put("NumberPatterns", numberPatterns); + cache.put(NUMBER_PATTERNS_CACHEKEY, + new ResourceReference(NUMBER_PATTERNS_CACHEKEY, (Object) numberPatterns, referenceQueue)); } + return numberPatterns; } private String getDateTimePattern(String key, int styleIndex, String calendarType) { String resourceKey = "gregory".equals(calendarType) ? key : calendarType + "." + key; - /* try the cache first */ - String[] patterns = (String[]) cache.get(resourceKey); - if (patterns == null) { /* cache miss */ - ResourceBundle r = adapter.getLocaleData().getDateFormatData(locale); + String cacheKey = DATE_TIME_PATTERN + resourceKey; + String[] patterns = null; + + removeEmptyReferences(); + ResourceReference data = cache.get(cacheKey); + + if (data == null || ((patterns = (String[]) data.get()) == null)) { + ResourceBundle r = localeData.getDateFormatData(locale); if (r.containsKey(resourceKey)) { patterns = r.getStringArray(resourceKey); } else { assert !resourceKey.equals(key); patterns = r.getStringArray(key); } - /* update cache */ - cache.putIfAbsent(resourceKey, patterns); + cache.put(cacheKey, + new ResourceReference(cacheKey, (Object) patterns, referenceQueue)); } + return patterns[styleIndex]; } + + private static class ResourceReference extends SoftReference { + private final String cacheKey; + + ResourceReference(String cacheKey, Object o, ReferenceQueue q) { + super(o, q); + this.cacheKey = cacheKey; + } + + String getCacheKey() { + return cacheKey; + } + } } diff --git a/jdk/src/share/classes/sun/util/locale/provider/ResourceBundleBasedAdapter.java b/jdk/src/share/classes/sun/util/locale/provider/ResourceBundleBasedAdapter.java new file mode 100644 index 00000000000..7046c2e0aed --- /dev/null +++ b/jdk/src/share/classes/sun/util/locale/provider/ResourceBundleBasedAdapter.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.util.locale.provider; + +import sun.util.resources.LocaleData; + +/** + * Accessor for LocaleData + * + * @author Naoto Sato + */ +public interface ResourceBundleBasedAdapter { + public LocaleData getLocaleData(); +} diff --git a/jdk/src/share/classes/sun/util/locale/provider/TimeZoneNameProviderImpl.java b/jdk/src/share/classes/sun/util/locale/provider/TimeZoneNameProviderImpl.java index 28fd7af5a90..96cb5ef7ba3 100644 --- a/jdk/src/share/classes/sun/util/locale/provider/TimeZoneNameProviderImpl.java +++ b/jdk/src/share/classes/sun/util/locale/provider/TimeZoneNameProviderImpl.java @@ -25,14 +25,10 @@ package sun.util.locale.provider; -import java.util.LinkedHashSet; import java.util.Locale; -import java.util.Map; import java.util.Set; import java.util.TimeZone; import java.util.spi.TimeZoneNameProvider; -import sun.util.calendar.ZoneInfo; -import sun.util.resources.TimeZoneNamesBundle; /** * Concrete implementation of the @@ -123,9 +119,7 @@ public class TimeZoneNameProviderImpl extends TimeZoneNameProvider { if (id == null || locale == null) { throw new NullPointerException(); } - LocaleProviderAdapter adapter = LocaleProviderAdapter.forType(type); - TimeZoneNamesBundle rb = adapter.getLocaleResources(locale).getTimeZoneNames(); - return rb.containsKey(id) ? rb.getStringArray(id, n) : null; + return LocaleProviderAdapter.forType(type).getLocaleResources(locale).getTimeZoneNames(id, n); } /** @@ -136,30 +130,6 @@ public class TimeZoneNameProviderImpl extends TimeZoneNameProvider { * @return an array of time zone names arrays */ String[][] getZoneStrings(Locale locale) { - LocaleProviderAdapter adapter = LocaleProviderAdapter.forType(type); - TimeZoneNamesBundle rb = adapter.getLocaleResources(locale).getTimeZoneNames(); - Set keyset = rb.keySet(); - // Use a LinkedHashSet to preseve the order - Set value = new LinkedHashSet<>(); - for (String key : keyset) { - value.add(rb.getStringArray(key)); - } - - // Add aliases data for CLDR - if (type == LocaleProviderAdapter.Type.CLDR) { - // Note: TimeZoneNamesBundle creates a String[] on each getStringArray call. - Map aliases = ZoneInfo.getAliasTable(); - for (String alias : aliases.keySet()) { - if (!keyset.contains(alias)) { - String tzid = aliases.get(alias); - if (keyset.contains(tzid)) { - String[] val = rb.getStringArray(tzid); - val[0] = alias; - value.add(val); - } - } - } - } - return value.toArray(new String[0][]); + return LocaleProviderAdapter.forType(type).getLocaleResources(locale).getZoneStrings(); } } diff --git a/jdk/src/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java b/jdk/src/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java index a8e09a0d6cf..8c279c1d9e6 100644 --- a/jdk/src/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java +++ b/jdk/src/share/classes/sun/util/locale/provider/TimeZoneNameUtility.java @@ -30,11 +30,10 @@ import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.spi.TimeZoneNameProvider; import sun.util.calendar.ZoneInfo; -import sun.util.resources.OpenListResourceBundle; -import sun.util.resources.TimeZoneNamesBundle; /** * Utility class that deals with the localized time zone names @@ -44,18 +43,20 @@ import sun.util.resources.TimeZoneNamesBundle; */ public final class TimeZoneNameUtility { - /** - * cache to hold time zone resource bundles. Keyed by Locale - */ - private static ConcurrentHashMap> cachedBundles = - new ConcurrentHashMap<>(); - /** * cache to hold time zone localized strings. Keyed by Locale */ private static ConcurrentHashMap> cachedZoneData = new ConcurrentHashMap<>(); + /** + * Cache for managing display names per timezone per locale + * The structure is: + * Map(key=id, value=SoftReference(Map(key=locale, value=displaynames))) + */ + private static final Map>> cachedDisplayNames = + new ConcurrentHashMap<>(); + /** * get time zone localized strings. Enumerate all keys. */ @@ -82,9 +83,9 @@ public final class TimeZoneNameUtility { } // Performs per-ID retrieval. + Set zoneIDs = LocaleProviderAdapter.forJRE().getLocaleResources(locale).getZoneIDs(); List zones = new LinkedList<>(); - OpenListResourceBundle rb = getBundle(locale); - for (String key : rb.keySet()) { + for (String key : zoneIDs) { String[] names = retrieveDisplayNamesImpl(key, locale); if (names != null) { zones.add(names); @@ -137,20 +138,31 @@ public final class TimeZoneNameUtility { private static String[] retrieveDisplayNamesImpl(String id, Locale locale) { LocaleServiceProviderPool pool = LocaleServiceProviderPool.getPool(TimeZoneNameProvider.class); - return pool.getLocalizedObject(TimeZoneNameArrayGetter.INSTANCE, locale, id); - } - private static TimeZoneNamesBundle getBundle(Locale locale) { - TimeZoneNamesBundle rb; - SoftReference data = cachedBundles.get(locale); - - if (data == null || ((rb = data.get()) == null)) { - rb = LocaleProviderAdapter.forJRE().getLocaleData().getTimeZoneNames(locale); - data = new SoftReference<>(rb); - cachedBundles.put(locale, data); + SoftReference> ref = cachedDisplayNames.get(id); + if (ref != null) { + Map perLocale = ref.get(); + if (perLocale != null) { + String[] names = perLocale.get(locale); + if (names != null) { + return names; + } + names = pool.getLocalizedObject(TimeZoneNameArrayGetter.INSTANCE, locale, id); + if (names != null) { + perLocale.put(locale, names); + } + return names; + } } - return rb; + String[] names = pool.getLocalizedObject(TimeZoneNameArrayGetter.INSTANCE, locale, id); + if (names != null) { + Map perLocale = new ConcurrentHashMap<>(); + perLocale.put(locale, names); + ref = new SoftReference<>(perLocale); + cachedDisplayNames.put(id, ref); + } + return names; } /** diff --git a/jdk/src/share/classes/sun/util/resources/LocaleData.java b/jdk/src/share/classes/sun/util/resources/LocaleData.java index 9a7a7c40119..fd9ab9e4ae5 100644 --- a/jdk/src/share/classes/sun/util/resources/LocaleData.java +++ b/jdk/src/share/classes/sun/util/resources/LocaleData.java @@ -98,6 +98,14 @@ public class LocaleData { return (TimeZoneNamesBundle) getBundle(type.getUtilResourcesPackage() + ".TimeZoneNames", locale); } + /** + * Gets a break iterator info resource bundle, using privileges + * to allow accessing a sun.* package. + */ + public ResourceBundle getBreakIteratorInfo(Locale locale) { + return getBundle(type.getTextResourcesPackage() + ".BreakIteratorInfo", locale); + } + /** * Gets a collation data resource bundle, using privileges * to allow accessing a sun.* package. diff --git a/jdk/src/share/classes/sun/util/resources/zh/CurrencyNames_zh_HK.java b/jdk/src/share/classes/sun/util/resources/zh/CurrencyNames_zh_HK.java index 950e9ba3a0b..d3fae4bb9c3 100644 --- a/jdk/src/share/classes/sun/util/resources/zh/CurrencyNames_zh_HK.java +++ b/jdk/src/share/classes/sun/util/resources/zh/CurrencyNames_zh_HK.java @@ -80,13 +80,14 @@ package sun.util.resources.zh; import java.util.Locale; import java.util.ResourceBundle; import sun.util.locale.provider.LocaleProviderAdapter; +import sun.util.locale.provider.ResourceBundleBasedAdapter; import sun.util.resources.OpenListResourceBundle; public final class CurrencyNames_zh_HK extends OpenListResourceBundle { // reparent to zh_TW for traditional Chinese names public CurrencyNames_zh_HK() { - ResourceBundle bundle = LocaleProviderAdapter.forJRE().getLocaleData().getCurrencyNames(Locale.TAIWAN); + ResourceBundle bundle = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getCurrencyNames(Locale.TAIWAN); setParent(bundle); } diff --git a/jdk/src/share/classes/sun/util/resources/zh/CurrencyNames_zh_SG.java b/jdk/src/share/classes/sun/util/resources/zh/CurrencyNames_zh_SG.java index b796db257c0..16778ffdedc 100644 --- a/jdk/src/share/classes/sun/util/resources/zh/CurrencyNames_zh_SG.java +++ b/jdk/src/share/classes/sun/util/resources/zh/CurrencyNames_zh_SG.java @@ -28,13 +28,14 @@ package sun.util.resources.zh; import java.util.Locale; import java.util.ResourceBundle; import sun.util.locale.provider.LocaleProviderAdapter; +import sun.util.locale.provider.ResourceBundleBasedAdapter; import sun.util.resources.OpenListResourceBundle; public final class CurrencyNames_zh_SG extends OpenListResourceBundle { // reparent to zh_CN for simplified Chinese names public CurrencyNames_zh_SG() { - ResourceBundle bundle = LocaleProviderAdapter.forJRE().getLocaleData().getCurrencyNames(Locale.CHINA); + ResourceBundle bundle = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getCurrencyNames(Locale.CHINA); setParent(bundle); } diff --git a/jdk/src/share/classes/sun/util/resources/zh/LocaleNames_zh_HK.java b/jdk/src/share/classes/sun/util/resources/zh/LocaleNames_zh_HK.java index fe48363c55f..6609a3fdb96 100644 --- a/jdk/src/share/classes/sun/util/resources/zh/LocaleNames_zh_HK.java +++ b/jdk/src/share/classes/sun/util/resources/zh/LocaleNames_zh_HK.java @@ -28,13 +28,14 @@ package sun.util.resources.zh; import java.util.Locale; import java.util.ResourceBundle; import sun.util.locale.provider.LocaleProviderAdapter; +import sun.util.locale.provider.ResourceBundleBasedAdapter; import sun.util.resources.OpenListResourceBundle; public final class LocaleNames_zh_HK extends OpenListResourceBundle { // reparent to zh_TW for traditional Chinese names public LocaleNames_zh_HK() { - ResourceBundle bundle = LocaleProviderAdapter.forJRE().getLocaleData().getLocaleNames(Locale.TAIWAN); + ResourceBundle bundle = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getLocaleNames(Locale.TAIWAN); setParent(bundle); } diff --git a/jdk/src/share/classes/sun/util/resources/zh/TimeZoneNames_zh_HK.java b/jdk/src/share/classes/sun/util/resources/zh/TimeZoneNames_zh_HK.java index 603be5fb272..9694c70b380 100644 --- a/jdk/src/share/classes/sun/util/resources/zh/TimeZoneNames_zh_HK.java +++ b/jdk/src/share/classes/sun/util/resources/zh/TimeZoneNames_zh_HK.java @@ -41,13 +41,14 @@ package sun.util.resources.zh; import java.util.Locale; import java.util.ResourceBundle; import sun.util.locale.provider.LocaleProviderAdapter; +import sun.util.locale.provider.ResourceBundleBasedAdapter; import sun.util.resources.TimeZoneNamesBundle; public final class TimeZoneNames_zh_HK extends TimeZoneNamesBundle { // reparent to zh_TW for traditional Chinese names public TimeZoneNames_zh_HK() { - ResourceBundle bundle = LocaleProviderAdapter.forJRE().getLocaleData().getTimeZoneNames(Locale.TAIWAN); + ResourceBundle bundle = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getTimeZoneNames(Locale.TAIWAN); setParent(bundle); } diff --git a/jdk/test/java/util/PluggableLocale/BreakIteratorProviderTest.java b/jdk/test/java/util/PluggableLocale/BreakIteratorProviderTest.java index 160f9fbcb4f..31ef767efea 100644 --- a/jdk/test/java/util/PluggableLocale/BreakIteratorProviderTest.java +++ b/jdk/test/java/util/PluggableLocale/BreakIteratorProviderTest.java @@ -67,8 +67,7 @@ public class BreakIteratorProviderTest extends ProviderTest { for (Locale target: availloc) { // pure JRE implementation - ResourceBundle rb = LocaleProviderAdapter.forJRE().getLocaleData().getBundle( - "sun.text.resources.BreakIteratorInfo", target); + ResourceBundle rb = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getBreakIteratorInfo(target); String[] classNames = rb.getStringArray("BreakIteratorClasses"); boolean jreSupportsLocale = jreimplloc.contains(target); diff --git a/jdk/test/java/util/PluggableLocale/CollatorProviderTest.java b/jdk/test/java/util/PluggableLocale/CollatorProviderTest.java index 718f1658ec3..c82f6e4c8bf 100644 --- a/jdk/test/java/util/PluggableLocale/CollatorProviderTest.java +++ b/jdk/test/java/util/PluggableLocale/CollatorProviderTest.java @@ -67,7 +67,7 @@ public class CollatorProviderTest extends ProviderTest { for (String tag : ((AvailableLanguageTags)LocaleProviderAdapter.forJRE().getCollatorProvider()).getAvailableLanguageTags()) { jreimplloc.add(Locale.forLanguageTag(tag)); } - ResourceBundle rb = LocaleProviderAdapter.forJRE().getLocaleData().getCollationData(target); + ResourceBundle rb = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getCollationData(target); boolean jreSupportsLocale = jreimplloc.contains(target); // result object diff --git a/jdk/test/java/util/PluggableLocale/CurrencyNameProviderTest.java b/jdk/test/java/util/PluggableLocale/CurrencyNameProviderTest.java index ebffc3f4f3c..b4cfaae385e 100644 --- a/jdk/test/java/util/PluggableLocale/CurrencyNameProviderTest.java +++ b/jdk/test/java/util/PluggableLocale/CurrencyNameProviderTest.java @@ -58,7 +58,7 @@ public class CurrencyNameProviderTest extends ProviderTest { for (Locale target: availloc) { // pure JRE implementation - OpenListResourceBundle rb = (OpenListResourceBundle)LocaleProviderAdapter.forJRE().getLocaleData().getCurrencyNames(target); + OpenListResourceBundle rb = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getCurrencyNames(target); boolean jreSupportsTarget = jreimplloc.contains(target); for (Locale test: testloc) { diff --git a/jdk/test/java/util/PluggableLocale/DateFormatProviderTest.java b/jdk/test/java/util/PluggableLocale/DateFormatProviderTest.java index d49b25b6acb..31ced5884ce 100644 --- a/jdk/test/java/util/PluggableLocale/DateFormatProviderTest.java +++ b/jdk/test/java/util/PluggableLocale/DateFormatProviderTest.java @@ -84,7 +84,7 @@ public class DateFormatProviderTest extends ProviderTest { break; } // pure JRE implementation - ResourceBundle rb = LocaleProviderAdapter.forJRE().getLocaleData().getDateFormatData(target); + ResourceBundle rb = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getDateFormatData(target); boolean jreSupportsLocale = jreimplloc.contains(target); // JRE string arrays diff --git a/jdk/test/java/util/PluggableLocale/DateFormatSymbolsProviderTest.java b/jdk/test/java/util/PluggableLocale/DateFormatSymbolsProviderTest.java index ef07f7e3f75..f7a89b032bd 100644 --- a/jdk/test/java/util/PluggableLocale/DateFormatSymbolsProviderTest.java +++ b/jdk/test/java/util/PluggableLocale/DateFormatSymbolsProviderTest.java @@ -62,7 +62,7 @@ public class DateFormatSymbolsProviderTest extends ProviderTest { for (Locale target: availloc) { // pure JRE implementation - ResourceBundle rb = LocaleProviderAdapter.forJRE().getLocaleData().getDateFormatData(target); + ResourceBundle rb = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getDateFormatData(target); boolean jreSupportsLocale = jreimplloc.contains(target); // JRE string arrays diff --git a/jdk/test/java/util/PluggableLocale/DecimalFormatSymbolsProviderTest.java b/jdk/test/java/util/PluggableLocale/DecimalFormatSymbolsProviderTest.java index 7d25542d650..6753590ed6a 100644 --- a/jdk/test/java/util/PluggableLocale/DecimalFormatSymbolsProviderTest.java +++ b/jdk/test/java/util/PluggableLocale/DecimalFormatSymbolsProviderTest.java @@ -61,17 +61,15 @@ public class DecimalFormatSymbolsProviderTest extends ProviderTest { for (Locale target: availloc) { // pure JRE implementation - ResourceBundle rb = LocaleProviderAdapter.forJRE().getLocaleData().getNumberFormatData(target); + Object[] data = LocaleProviderAdapter.forJRE().getLocaleResources(target).getDecimalFormatSymbolsData(); boolean jreSupportsLocale = jreimplloc.contains(target); // JRE string arrays String[] jres = new String[2]; if (jreSupportsLocale) { - try { - String[] tmp = rb.getStringArray("NumberElements"); - jres[0] = tmp[9]; // infinity - jres[1] = tmp[10]; // NaN - } catch (MissingResourceException mre) {} + String[] tmp = (String[]) data[0]; + jres[0] = tmp[9]; // infinity + jres[1] = tmp[10]; // NaN } // result object diff --git a/jdk/test/java/util/PluggableLocale/LocaleNameProviderTest.java b/jdk/test/java/util/PluggableLocale/LocaleNameProviderTest.java index c82fab0a514..c7ec9dcd49e 100644 --- a/jdk/test/java/util/PluggableLocale/LocaleNameProviderTest.java +++ b/jdk/test/java/util/PluggableLocale/LocaleNameProviderTest.java @@ -49,7 +49,7 @@ public class LocaleNameProviderTest extends ProviderTest { for (Locale target: availloc) { // pure JRE implementation - OpenListResourceBundle rb = LocaleProviderAdapter.forJRE().getLocaleData().getLocaleNames(target); + OpenListResourceBundle rb = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getLocaleNames(target); boolean jreSupportsTarget = jreimplloc.contains(target); for (Locale test: testloc) { diff --git a/jdk/test/java/util/PluggableLocale/NumberFormatProviderTest.java b/jdk/test/java/util/PluggableLocale/NumberFormatProviderTest.java index 13f63fd70d5..ecb45ad7c29 100644 --- a/jdk/test/java/util/PluggableLocale/NumberFormatProviderTest.java +++ b/jdk/test/java/util/PluggableLocale/NumberFormatProviderTest.java @@ -63,16 +63,12 @@ public class NumberFormatProviderTest extends ProviderTest { void objectValidityTest() { for (Locale target: availloc) { - // pure JRE implementation - ResourceBundle rb = LocaleProviderAdapter.forJRE().getLocaleData().getNumberFormatData(target); boolean jreSupportsLocale = jreimplloc.contains(target); // JRE string arrays String[] jreNumberPatterns = null; if (jreSupportsLocale) { - try { - jreNumberPatterns = rb.getStringArray("NumberPatterns"); - } catch (MissingResourceException mre) {} + jreNumberPatterns = LocaleProviderAdapter.forJRE().getLocaleResources(target).getNumberPatterns(); } // result object diff --git a/jdk/test/java/util/PluggableLocale/TimeZoneNameProviderTest.java b/jdk/test/java/util/PluggableLocale/TimeZoneNameProviderTest.java index 7ed2fc11429..29be59a645a 100644 --- a/jdk/test/java/util/PluggableLocale/TimeZoneNameProviderTest.java +++ b/jdk/test/java/util/PluggableLocale/TimeZoneNameProviderTest.java @@ -52,7 +52,7 @@ public class TimeZoneNameProviderTest extends ProviderTest { for (Locale target: available) { // pure JRE implementation - OpenListResourceBundle rb = LocaleProviderAdapter.forJRE().getLocaleData().getTimeZoneNames(target); + OpenListResourceBundle rb = ((ResourceBundleBasedAdapter)LocaleProviderAdapter.forJRE()).getLocaleData().getTimeZoneNames(target); boolean jreSupportsTarget = jreimplloc.contains(target); for (String id: ids) { From 33b7cd7cae2d7a22bf107e1e746d3be133bedcdb Mon Sep 17 00:00:00 2001 From: Bengt Rutisson Date: Mon, 14 Jan 2013 21:30:45 +0100 Subject: [PATCH 034/138] 8005972: ParNew should not update the tenuring threshold when promotion failed has occurred Reviewed-by: ysr, johnc, jwilhelm --- .../vm/gc_implementation/parNew/parNewGeneration.cpp | 9 ++------- .../vm/gc_implementation/parNew/parNewGeneration.hpp | 4 ---- hotspot/src/share/vm/memory/defNewGeneration.cpp | 9 ++++++--- hotspot/src/share/vm/memory/defNewGeneration.hpp | 4 +++- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp b/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp index 273322436ba..e868d870990 100644 --- a/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp +++ b/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp @@ -878,12 +878,6 @@ void EvacuateFollowersClosureGeneral::do_void() { bool ParNewGeneration::_avoid_promotion_undo = false; -void ParNewGeneration::adjust_desired_tenuring_threshold() { - // Set the desired survivor size to half the real survivor space - _tenuring_threshold = - age_table()->compute_tenuring_threshold(to()->capacity()/HeapWordSize); -} - // A Generation that does parallel young-gen collection. void ParNewGeneration::collect(bool full, @@ -1013,6 +1007,8 @@ void ParNewGeneration::collect(bool full, size_policy->reset_gc_overhead_limit_count(); assert(to()->is_empty(), "to space should be empty now"); + + adjust_desired_tenuring_threshold(); } else { assert(_promo_failure_scan_stack.is_empty(), "post condition"); _promo_failure_scan_stack.clear(true); // Clear cached segments. @@ -1035,7 +1031,6 @@ void ParNewGeneration::collect(bool full, from()->set_concurrent_iteration_safe_limit(from()->top()); to()->set_concurrent_iteration_safe_limit(to()->top()); - adjust_desired_tenuring_threshold(); if (ResizePLAB) { plab_stats()->adjust_desired_plab_sz(n_workers); } diff --git a/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.hpp b/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.hpp index bbb6176fd08..487552bfba9 100644 --- a/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.hpp +++ b/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.hpp @@ -347,10 +347,6 @@ class ParNewGeneration: public DefNewGeneration { bool survivor_overflow() { return _survivor_overflow; } void set_survivor_overflow(bool v) { _survivor_overflow = v; } - // Adjust the tenuring threshold. See the implementation for - // the details of the policy. - virtual void adjust_desired_tenuring_threshold(); - public: ParNewGeneration(ReservedSpace rs, size_t initial_byte_size, int level); diff --git a/hotspot/src/share/vm/memory/defNewGeneration.cpp b/hotspot/src/share/vm/memory/defNewGeneration.cpp index cca7cd0e704..689ce7b8bbf 100644 --- a/hotspot/src/share/vm/memory/defNewGeneration.cpp +++ b/hotspot/src/share/vm/memory/defNewGeneration.cpp @@ -550,6 +550,11 @@ HeapWord* DefNewGeneration::expand_and_allocate(size_t size, return allocate(size, is_tlab); } +void DefNewGeneration::adjust_desired_tenuring_threshold() { + // Set the desired survivor size to half the real survivor space + _tenuring_threshold = + age_table()->compute_tenuring_threshold(to()->capacity()/HeapWordSize); +} void DefNewGeneration::collect(bool full, bool clear_all_soft_refs, @@ -649,9 +654,7 @@ void DefNewGeneration::collect(bool full, assert(to()->is_empty(), "to space should be empty now"); - // Set the desired survivor size to half the real survivor space - _tenuring_threshold = - age_table()->compute_tenuring_threshold(to()->capacity()/HeapWordSize); + adjust_desired_tenuring_threshold(); // A successful scavenge should restart the GC time limit count which is // for full GC's. diff --git a/hotspot/src/share/vm/memory/defNewGeneration.hpp b/hotspot/src/share/vm/memory/defNewGeneration.hpp index b7c794d86c2..38ea742b38a 100644 --- a/hotspot/src/share/vm/memory/defNewGeneration.hpp +++ b/hotspot/src/share/vm/memory/defNewGeneration.hpp @@ -124,7 +124,9 @@ protected: _should_allocate_from_space = true; } - protected: + // Tenuring + void adjust_desired_tenuring_threshold(); + // Spaces EdenSpace* _eden_space; ContiguousSpace* _from_space; From a788c5b6b883532aa320fa2f190d297dc6090995 Mon Sep 17 00:00:00 2001 From: Kumar Srinivasan Date: Mon, 14 Jan 2013 15:46:54 -0800 Subject: [PATCH 035/138] 8005252: pack200 should support MethodParameters Reviewed-by: jrose --- .../com/sun/java/util/jar/pack/Attribute.java | 3 +- .../sun/java/util/jar/pack/BandStructure.java | 13 +++++- .../com/sun/java/util/jar/pack/Constants.java | 6 ++- .../sun/java/util/jar/pack/PackageReader.java | 10 +++- .../sun/java/util/jar/pack/PackageWriter.java | 10 ++-- .../com/sun/java/util/jar/pack/bands.cpp | 5 +- .../native/com/sun/java/util/jar/pack/bands.h | 8 +++- .../com/sun/java/util/jar/pack/constants.h | 7 ++- .../com/sun/java/util/jar/pack/main.cpp | 4 +- .../com/sun/java/util/jar/pack/unpack.cpp | 30 +++++++++--- jdk/test/ProblemList.txt | 3 -- jdk/test/tools/pack200/AttributeTests.java | 46 +++++++++++++++++-- .../src/xmlkit/ClassReader.java | 16 ++++++- 13 files changed, 134 insertions(+), 27 deletions(-) diff --git a/jdk/src/share/classes/com/sun/java/util/jar/pack/Attribute.java b/jdk/src/share/classes/com/sun/java/util/jar/pack/Attribute.java index fd644bf759e..9e891fc7c87 100644 --- a/jdk/src/share/classes/com/sun/java/util/jar/pack/Attribute.java +++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/Attribute.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -177,6 +177,7 @@ class Attribute implements Comparable { define(sd, ATTR_CONTEXT_METHOD, "Synthetic", ""); define(sd, ATTR_CONTEXT_METHOD, "Deprecated", ""); define(sd, ATTR_CONTEXT_METHOD, "Exceptions", "NH[RCH]"); + define(sd, ATTR_CONTEXT_METHOD, "MethodParameters", "NB[RUNHI]"); //define(sd, ATTR_CONTEXT_METHOD, "Code", "HHNI[B]NH[PHPOHPOHRCNH]NH[RUHNI[B]]"); define(sd, ATTR_CONTEXT_CODE, "StackMapTable", diff --git a/jdk/src/share/classes/com/sun/java/util/jar/pack/BandStructure.java b/jdk/src/share/classes/com/sun/java/util/jar/pack/BandStructure.java index f3d5cec8e48..fdfb87dc136 100644 --- a/jdk/src/share/classes/com/sun/java/util/jar/pack/BandStructure.java +++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/BandStructure.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1502,6 +1502,10 @@ class BandStructure { CPRefBand method_Exceptions_RC = method_attr_bands.newCPRefBand("method_Exceptions_RC", CONSTANT_Class); CPRefBand method_Signature_RS = method_attr_bands.newCPRefBand("method_Signature_RS", CONSTANT_Signature); MultiBand method_metadata_bands = method_attr_bands.newMultiBand("(method_metadata_bands)", UNSIGNED5); + // band for predefine method parameters + IntBand method_MethodParameters_NB = method_attr_bands.newIntBand("method_MethodParameters_NB", BYTE1); + CPRefBand method_MethodParameters_name_RUN = method_attr_bands.newCPRefBand("method_MethodParameters_name_RUN", UNSIGNED5, CONSTANT_Utf8, NULL_IS_OK); + IntBand method_MethodParameters_flag_I = method_attr_bands.newIntBand("method_MethodParameters_flag_I"); MultiBand class_attr_bands = class_bands.newMultiBand("(class_attr_bands)", UNSIGNED5); IntBand class_flags_hi = class_attr_bands.newIntBand("class_flags_hi"); @@ -1768,6 +1772,13 @@ class BandStructure { method_Exceptions_RC }, "Exceptions", "NH[RCH]"); + predefineAttribute(METHOD_ATTR_MethodParameters, ATTR_CONTEXT_METHOD, + new Band[]{ + method_MethodParameters_NB, + method_MethodParameters_name_RUN, + method_MethodParameters_flag_I + }, + "MethodParameters", "NB[RUNHI]"); assert(attrCodeEmpty == Package.attrCodeEmpty); predefineAttribute(X_ATTR_Signature, ATTR_CONTEXT_METHOD, new Band[] { method_Signature_RS }, diff --git a/jdk/src/share/classes/com/sun/java/util/jar/pack/Constants.java b/jdk/src/share/classes/com/sun/java/util/jar/pack/Constants.java index b5c1d124ef6..15882624381 100644 --- a/jdk/src/share/classes/com/sun/java/util/jar/pack/Constants.java +++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/Constants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -73,6 +73,9 @@ class Constants { public final static Package.Version JAVA7_PACKAGE_VERSION = Package.Version.of(170, 1); + public final static Package.Version JAVA8_PACKAGE_VERSION = + Package.Version.of(171, 0); + // upper limit, should point to the latest class version public final static Package.Version JAVA_MAX_CLASS_VERSION = JAVA8_MAX_CLASS_VERSION; @@ -158,6 +161,7 @@ class Constants { METHOD_ATTR_RuntimeInvisibleParameterAnnotations = 24, CLASS_ATTR_ClassFile_version = 24, METHOD_ATTR_AnnotationDefault = 25, + METHOD_ATTR_MethodParameters = 26, CODE_ATTR_StackMapTable = 0, // new in Java 6 CODE_ATTR_LineNumberTable = 1, CODE_ATTR_LocalVariableTable = 2, diff --git a/jdk/src/share/classes/com/sun/java/util/jar/pack/PackageReader.java b/jdk/src/share/classes/com/sun/java/util/jar/pack/PackageReader.java index 26bade85c5d..319b8793347 100644 --- a/jdk/src/share/classes/com/sun/java/util/jar/pack/PackageReader.java +++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/PackageReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -242,6 +242,7 @@ class PackageReader extends BandStructure { void checkArchiveVersion() throws IOException { Package.Version versionFound = null; for (Package.Version v : new Package.Version[] { + JAVA8_PACKAGE_VERSION, JAVA7_PACKAGE_VERSION, JAVA6_PACKAGE_VERSION, JAVA5_PACKAGE_VERSION @@ -252,7 +253,9 @@ class PackageReader extends BandStructure { } } if (versionFound == null) { - String expVer = JAVA7_PACKAGE_VERSION.toString() + String expVer = JAVA8_PACKAGE_VERSION.toString() + + "OR" + + JAVA7_PACKAGE_VERSION.toString() + " OR " + JAVA6_PACKAGE_VERSION.toString() + " OR " @@ -1516,6 +1519,9 @@ class PackageReader extends BandStructure { // method_metadata_bands // *method_Exceptions_N :UNSIGNED5 // *method_Exceptions_RC :UNSIGNED5 (cp_Class) + // *method_MethodParameters_NB: BYTE1 + // *method_MethodParameters_RUN: UNSIGNED5 (cp_Utf8) + // *method_MethodParameters_I: UNSIGNED5 (flag) // // code_attr_bands: // *code_flags :UNSIGNED5 diff --git a/jdk/src/share/classes/com/sun/java/util/jar/pack/PackageWriter.java b/jdk/src/share/classes/com/sun/java/util/jar/pack/PackageWriter.java index 78754b5c72b..e6990451135 100644 --- a/jdk/src/share/classes/com/sun/java/util/jar/pack/PackageWriter.java +++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/PackageWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -142,12 +142,14 @@ class PackageWriter extends BandStructure { } else if (highV.equals(JAVA6_MAX_CLASS_VERSION) || (highV.equals(JAVA7_MAX_CLASS_VERSION) && !pkg.cp.haveExtraTags())) { // force down the package version if we have jdk7 classes without - // any Indy references, this is because jdk7 class file (52.0) without - // Indy is identical to jdk6 class file (51.0). + // any Indy references, this is because jdk7 class file (51.0) without + // Indy is identical to jdk6 class file (50.0). packageVersion = JAVA6_PACKAGE_VERSION; + } else if (highV.equals(JAVA7_MAX_CLASS_VERSION)) { + packageVersion = JAVA7_PACKAGE_VERSION; } else { // Normal case. Use the newest archive format, when available - packageVersion = JAVA7_PACKAGE_VERSION; + packageVersion = JAVA8_PACKAGE_VERSION; } if (verbose > 0) { diff --git a/jdk/src/share/native/com/sun/java/util/jar/pack/bands.cpp b/jdk/src/share/native/com/sun/java/util/jar/pack/bands.cpp index 08cdc8d3e76..8ad5e556c41 100644 --- a/jdk/src/share/native/com/sun/java/util/jar/pack/bands.cpp +++ b/jdk/src/share/native/com/sun/java/util/jar/pack/bands.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -352,6 +352,9 @@ const band_init all_band_inits[] = { BAND_INIT(method_Exceptions_RC, UNSIGNED5_spec, INDEX(CONSTANT_Class)), BAND_INIT(method_Signature_RS, UNSIGNED5_spec, INDEX(CONSTANT_Signature)), BAND_INIT(method_metadata_bands, -1, -1), + BAND_INIT(method_MethodParameters_NB, BYTE1_spec, 0), + BAND_INIT(method_MethodParameters_name_RUN, UNSIGNED5_spec, NULL_OR_INDEX(CONSTANT_Utf8)), + BAND_INIT(method_MethodParameters_flag_I, UNSIGNED5_spec, 0), BAND_INIT(method_attr_bands, -1, -1), BAND_INIT(class_flags_hi, UNSIGNED5_spec, 0), BAND_INIT(class_flags_lo, UNSIGNED5_spec, 0), diff --git a/jdk/src/share/native/com/sun/java/util/jar/pack/bands.h b/jdk/src/share/native/com/sun/java/util/jar/pack/bands.h index b8e322aa1db..c555936d02e 100644 --- a/jdk/src/share/native/com/sun/java/util/jar/pack/bands.h +++ b/jdk/src/share/native/com/sun/java/util/jar/pack/bands.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -212,6 +212,9 @@ enum band_number { e_method_Exceptions_RC, e_method_Signature_RS, e_method_metadata_bands, + e_method_MethodParameters_NB, + e_method_MethodParameters_name_RUN, + e_method_MethodParameters_flag_I, e_method_attr_bands, e_class_flags_hi, @@ -388,6 +391,9 @@ enum band_number { #define method_Exceptions_N all_bands[e_method_Exceptions_N] #define method_Exceptions_RC all_bands[e_method_Exceptions_RC] #define method_Signature_RS all_bands[e_method_Signature_RS] +#define method_MethodParameters_NB all_bands[e_method_MethodParameters_NB] +#define method_MethodParameters_name_RUN all_bands[e_method_MethodParameters_name_RUN] +#define method_MethodParameters_flag_I all_bands[e_method_MethodParameters_flag_I] #define method_attr_bands all_bands[e_method_attr_bands] #define class_flags_hi all_bands[e_class_flags_hi] #define class_flags_lo all_bands[e_class_flags_lo] diff --git a/jdk/src/share/native/com/sun/java/util/jar/pack/constants.h b/jdk/src/share/native/com/sun/java/util/jar/pack/constants.h index dde13b8625b..040d8edd55b 100644 --- a/jdk/src/share/native/com/sun/java/util/jar/pack/constants.h +++ b/jdk/src/share/native/com/sun/java/util/jar/pack/constants.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -59,6 +59,9 @@ #define JAVA7_PACKAGE_MAJOR_VERSION 170 #define JAVA7_PACKAGE_MINOR_VERSION 1 +#define JAVA8_PACKAGE_MAJOR_VERSION 171 +#define JAVA8_PACKAGE_MINOR_VERSION 0 + // magic number for gzip streams (for processing pack200-gzip data) #define GZIP_MAGIC 0x1F8B0800 #define GZIP_MAGIC_MASK 0xFFFFFF00 // last byte is variable "flg" field @@ -120,6 +123,7 @@ enum { METHOD_ATTR_RuntimeVisibleParameterAnnotations = 23, METHOD_ATTR_RuntimeInvisibleParameterAnnotations = 24, METHOD_ATTR_AnnotationDefault = 25, + METHOD_ATTR_MethodParameters = 26, CODE_ATTR_StackMapTable = 0, CODE_ATTR_LineNumberTable = 1, CODE_ATTR_LocalVariableTable = 2, @@ -160,6 +164,7 @@ enum { F(METHOD_ATTR_RuntimeVisibleParameterAnnotations,RuntimeVisibleParameterAnnotations) \ F(METHOD_ATTR_RuntimeInvisibleParameterAnnotations,RuntimeInvisibleParameterAnnotations) \ F(METHOD_ATTR_AnnotationDefault,AnnotationDefault) \ + F(METHOD_ATTR_MethodParameters,MethodParameters) \ /*(end)*/ #define CODE_ATTR_DO(F) \ F(CODE_ATTR_StackMapTable,StackMapTable) \ diff --git a/jdk/src/share/native/com/sun/java/util/jar/pack/main.cpp b/jdk/src/share/native/com/sun/java/util/jar/pack/main.cpp index 1f0d0d2f3f1..6fbc43a18ae 100644 --- a/jdk/src/share/native/com/sun/java/util/jar/pack/main.cpp +++ b/jdk/src/share/native/com/sun/java/util/jar/pack/main.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -71,7 +71,7 @@ NOT_PRODUCT(static THRTYPE uThread = -1;) unpacker* unpacker::non_mt_current = null; unpacker* unpacker::current() { - assert(uThread == THREAD_SELF); + //assert(uThread == THREAD_SELF); return non_mt_current; } static void set_current_unpacker(unpacker* u) { diff --git a/jdk/src/share/native/com/sun/java/util/jar/pack/unpack.cpp b/jdk/src/share/native/com/sun/java/util/jar/pack/unpack.cpp index d7c51978ded..bc91827f66c 100644 --- a/jdk/src/share/native/com/sun/java/util/jar/pack/unpack.cpp +++ b/jdk/src/share/native/com/sun/java/util/jar/pack/unpack.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -648,13 +648,14 @@ void unpacker::read_file_header() { majver = hdr.getInt(); hdrVals += 2; - int majmin[3][2] = { + int majmin[4][2] = { {JAVA5_PACKAGE_MAJOR_VERSION, JAVA5_PACKAGE_MINOR_VERSION}, {JAVA6_PACKAGE_MAJOR_VERSION, JAVA6_PACKAGE_MINOR_VERSION}, - {JAVA7_PACKAGE_MAJOR_VERSION, JAVA7_PACKAGE_MINOR_VERSION} + {JAVA7_PACKAGE_MAJOR_VERSION, JAVA7_PACKAGE_MINOR_VERSION}, + {JAVA8_PACKAGE_MAJOR_VERSION, JAVA8_PACKAGE_MINOR_VERSION} }; int majminfound = false; - for (int i = 0 ; i < 3 ; i++) { + for (int i = 0 ; i < 4 ; i++) { if (majver == majmin[i][0] && minver == majmin[i][1]) { majminfound = true; break; @@ -663,11 +664,12 @@ void unpacker::read_file_header() { if (majminfound == null) { char message[200]; sprintf(message, "@" ERROR_FORMAT ": magic/ver = " - "%08X/%d.%d should be %08X/%d.%d OR %08X/%d.%d OR %08X/%d.%d\n", + "%08X/%d.%d should be %08X/%d.%d OR %08X/%d.%d OR %08X/%d.%d OR %08X/%d.%d\n", magic, majver, minver, JAVA_PACKAGE_MAGIC, JAVA5_PACKAGE_MAJOR_VERSION, JAVA5_PACKAGE_MINOR_VERSION, JAVA_PACKAGE_MAGIC, JAVA6_PACKAGE_MAJOR_VERSION, JAVA6_PACKAGE_MINOR_VERSION, - JAVA_PACKAGE_MAGIC, JAVA7_PACKAGE_MAJOR_VERSION, JAVA7_PACKAGE_MINOR_VERSION); + JAVA_PACKAGE_MAGIC, JAVA7_PACKAGE_MAJOR_VERSION, JAVA7_PACKAGE_MINOR_VERSION, + JAVA_PACKAGE_MAGIC, JAVA8_PACKAGE_MAJOR_VERSION, JAVA8_PACKAGE_MINOR_VERSION); abort(message); } CHECK; @@ -2481,6 +2483,13 @@ void unpacker::read_attrs(int attrc, int obj_count) { ad.readBandData(METHOD_ATTR_RuntimeInvisibleParameterAnnotations); ad.readBandData(METHOD_ATTR_AnnotationDefault); CHECK; + + count = ad.predefCount(METHOD_ATTR_MethodParameters); + method_MethodParameters_NB.readData(count); + count = method_MethodParameters_NB.getIntTotal(); + method_MethodParameters_name_RUN.readData(count); + method_MethodParameters_flag_I.readData(count); + CHECK; break; case ATTR_CONTEXT_CODE: @@ -4417,6 +4426,15 @@ int unpacker::write_attrs(int attrc, julong indexBits) { } break; + case ADH_BYTE(ATTR_CONTEXT_METHOD, METHOD_ATTR_MethodParameters): + aname = cp.sym[cpool::s_MethodParameters]; + putu1(count = method_MethodParameters_NB.getByte()); + for (j = 0; j < count; j++) { + putref(method_MethodParameters_name_RUN.getRefN()); + putu4(method_MethodParameters_flag_I.getInt()); + } + break; + case ADH_BYTE(ATTR_CONTEXT_CODE, CODE_ATTR_StackMapTable): aname = cp.sym[cpool::s_StackMapTable]; // (keep this code aligned with its brother in unpacker::read_attrs) diff --git a/jdk/test/ProblemList.txt b/jdk/test/ProblemList.txt index 6929924565e..8191d5f0b91 100644 --- a/jdk/test/ProblemList.txt +++ b/jdk/test/ProblemList.txt @@ -321,9 +321,6 @@ tools/pack200/Pack200Test.java generic-all # 7150569 tools/launcher/UnicodeTest.java macosx-all -# 8005252 -tools/pack200/AttributeTests.java generic-all - ############################################################################ # jdk_jdi diff --git a/jdk/test/tools/pack200/AttributeTests.java b/jdk/test/tools/pack200/AttributeTests.java index 69547083025..d4a40a90cc5 100644 --- a/jdk/test/tools/pack200/AttributeTests.java +++ b/jdk/test/tools/pack200/AttributeTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,13 +21,15 @@ * questions. */ import java.io.File; -import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import static java.nio.file.StandardOpenOption.*; /* * @test - * @bug 6746111 + * @bug 6746111 8005252 * @summary tests various classfile format and attribute handling by pack200 * @compile -XDignore.symbol.file Utils.java AttributeTests.java * @run main AttributeTests @@ -37,8 +39,46 @@ public class AttributeTests { public static void main(String... args) throws Exception { test6746111(); + testMethodParameters(); } + /* + * this tests ensure that MethodParameters produces by javac is packed + * correctly. Usually this is not the case as new attributes are available + * in the sdk jars, since MethodParameters happens to be an optional + * attribute, thus this test. + */ + static void testMethodParameters() throws Exception { + List scratch = new ArrayList<>(); + final String fname = "MP"; + String javaFileName = fname + Utils.JAVA_FILE_EXT; + String javaClassName = fname + Utils.CLASS_FILE_EXT; + scratch.add("class " + fname + " {"); + scratch.add("void foo2(int j, final int k){}"); + scratch.add("}"); + File cwd = new File("."); + File javaFile = new File(cwd, javaFileName); + Files.write(javaFile.toPath(), scratch, Charset.defaultCharset(), + CREATE, TRUNCATE_EXISTING); + + Utils.compiler(javaFile.getName(), "-parameters"); + + // jar the file up + File testjarFile = new File(cwd, "test" + Utils.JAR_FILE_EXT); + Utils.jar("cvf", testjarFile.getName(), javaClassName); + + // pack using --repack + File outjarFile = new File(cwd, "out" + Utils.JAR_FILE_EXT); + scratch.clear(); + scratch.add(Utils.getPack200Cmd()); + scratch.add("--repack"); + scratch.add("--unknown-attribute=error"); + scratch.add(outjarFile.getName()); + scratch.add(testjarFile.getName()); + Utils.runExec(scratch); + + Utils.doCompareVerify(testjarFile, outjarFile); + } /* * this test checks to see if we get the expected strings for output */ diff --git a/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java b/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java index 0579b5b99cd..177849872a9 100644 --- a/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java +++ b/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -53,6 +53,7 @@ import com.sun.tools.classfile.LineNumberTable_attribute; import com.sun.tools.classfile.LocalVariableTable_attribute; import com.sun.tools.classfile.LocalVariableTypeTable_attribute; import com.sun.tools.classfile.Method; +import com.sun.tools.classfile.MethodParameters_attribute; import com.sun.tools.classfile.Opcode; import com.sun.tools.classfile.RuntimeInvisibleAnnotations_attribute; import com.sun.tools.classfile.RuntimeInvisibleParameterAnnotations_attribute; @@ -1073,6 +1074,19 @@ class AttributeVisitor implements Attribute.Visitor { return null; // already added to parent } + @Override + public Element visitMethodParameters(MethodParameters_attribute mp, Element p) { + String name = x.getCpString(mp.attribute_name_index); + for (MethodParameters_attribute.Entry e : mp.method_parameter_table) { + Element l = new Element(name); + l.setAttr("name", x.getCpString(e.name_index)); + l.setAttr("flag", "" + e.flags); + l.trimToSize(); + p.add(l); + } + return null; // already added to parent + } + private void parseAnnotations(Annotation[] ra, Element p) { for (Annotation anno : ra) { Element ea = new Element("Member"); From 40039951b823ae3a8bf6b1fceb5d81f4718806bd Mon Sep 17 00:00:00 2001 From: Xue-Lei Andrew Fan Date: Mon, 14 Jan 2013 18:31:48 -0800 Subject: [PATCH 036/138] 8006265: Add test SSLEngineDeadlock.java to ProblemList Reviewed-by: weijun --- jdk/test/ProblemList.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jdk/test/ProblemList.txt b/jdk/test/ProblemList.txt index 8191d5f0b91..6813cce7aeb 100644 --- a/jdk/test/ProblemList.txt +++ b/jdk/test/ProblemList.txt @@ -288,6 +288,9 @@ sun/security/mscapi/ShortRSAKey1024.sh windows-all # 8000897, vm crash sun/security/provider/DSA/TestAlgParameterGenerator.java generic-all +# 7144048, performance issue +sun/security/ssl/com/sun/net/ssl/internal/ssl/SSLEngineImpl/SSLEngineDeadlock.java generic-all + ############################################################################ # jdk_sound From 97013ba02828f3025f3bb019c826bad332b1ee17 Mon Sep 17 00:00:00 2001 From: Alexander Scherbatiy Date: Tue, 15 Jan 2013 12:49:03 +0400 Subject: [PATCH 037/138] 8003978: closed/javax/swing/JRootPane/bug4670486.java fails since jdk7u12b01 on macosx Reviewed-by: serb, leonidr --- .../com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java | 1 - .../sun/java/swing/plaf/motif/MotifLookAndFeel.java | 6 ++++-- .../javax/swing/plaf/basic/BasicLookAndFeel.java | 4 +++- jdk/src/share/classes/sun/swing/SwingUtilities2.java | 8 ++++++++ .../TypeAhead/SubMenuShowTest/SubMenuShowTest.java | 10 ++++++++++ 5 files changed, 25 insertions(+), 4 deletions(-) diff --git a/jdk/src/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java b/jdk/src/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java index 6fb52a4cd4a..1fe26b09f31 100644 --- a/jdk/src/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java +++ b/jdk/src/share/classes/com/sun/java/swing/plaf/gtk/GTKLookAndFeel.java @@ -787,7 +787,6 @@ public class GTKLookAndFeel extends SynthLookAndFeel { "List.font", new FontLazyValue(Region.LIST), "List.rendererUseUIBorder", Boolean.FALSE, - "Menu.shortcutKeys", new int[] {KeyEvent.ALT_MASK}, "Menu.arrowIcon", new GTKStyle.GTKLazyValue( "com.sun.java.swing.plaf.gtk.GTKIconFactory", "getMenuArrowIcon"), diff --git a/jdk/src/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java b/jdk/src/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java index 923cd9d18b9..fdc6fa6210a 100644 --- a/jdk/src/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java +++ b/jdk/src/share/classes/com/sun/java/swing/plaf/motif/MotifLookAndFeel.java @@ -630,8 +630,10 @@ public class MotifLookAndFeel extends BasicLookAndFeel "Menu.menuPopupOffsetY", new Integer(0), "Menu.submenuPopupOffsetX", new Integer(-2), "Menu.submenuPopupOffsetY", new Integer(3), - "Menu.shortcutKeys", new int[] {KeyEvent.ALT_MASK, - KeyEvent.META_MASK}, + "Menu.shortcutKeys", new int[]{ + SwingUtilities2.getSystemMnemonicKeyMask(), + KeyEvent.META_MASK + }, "Menu.cancelMode", "hideMenuTree", "MenuBar.border", menuBarBorder, diff --git a/jdk/src/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java b/jdk/src/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java index 437704ad4e7..5a9135844bc 100644 --- a/jdk/src/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java +++ b/jdk/src/share/classes/javax/swing/plaf/basic/BasicLookAndFeel.java @@ -1153,7 +1153,9 @@ public abstract class BasicLookAndFeel extends LookAndFeel implements Serializab "Menu.menuPopupOffsetY", new Integer(0), "Menu.submenuPopupOffsetX", new Integer(0), "Menu.submenuPopupOffsetY", new Integer(0), - "Menu.shortcutKeys", new int[] {KeyEvent.ALT_MASK}, + "Menu.shortcutKeys", new int[]{ + SwingUtilities2.getSystemMnemonicKeyMask() + }, "Menu.crossMenuMnemonic", Boolean.TRUE, // Menu.cancelMode affects the cancel menu action behaviour; // currently supports: diff --git a/jdk/src/share/classes/sun/swing/SwingUtilities2.java b/jdk/src/share/classes/sun/swing/SwingUtilities2.java index 2094ec0c11d..c6134885adf 100644 --- a/jdk/src/share/classes/sun/swing/SwingUtilities2.java +++ b/jdk/src/share/classes/sun/swing/SwingUtilities2.java @@ -1879,4 +1879,12 @@ public class SwingUtilities2 { } return -1; } + + public static int getSystemMnemonicKeyMask() { + Toolkit toolkit = Toolkit.getDefaultToolkit(); + if (toolkit instanceof SunToolkit) { + return ((SunToolkit) toolkit).getFocusAcceleratorKeyMask(); + } + return InputEvent.ALT_MASK; + } } diff --git a/jdk/test/java/awt/KeyboardFocusmanager/TypeAhead/SubMenuShowTest/SubMenuShowTest.java b/jdk/test/java/awt/KeyboardFocusmanager/TypeAhead/SubMenuShowTest/SubMenuShowTest.java index f01b8856f0f..d95441ba1bc 100644 --- a/jdk/test/java/awt/KeyboardFocusmanager/TypeAhead/SubMenuShowTest/SubMenuShowTest.java +++ b/jdk/test/java/awt/KeyboardFocusmanager/TypeAhead/SubMenuShowTest/SubMenuShowTest.java @@ -36,6 +36,7 @@ import java.applet.Applet; import java.util.concurrent.atomic.AtomicBoolean; import java.lang.reflect.InvocationTargetException; import test.java.awt.regtesthelpers.Util; +import sun.awt.OSInfo; public class SubMenuShowTest extends Applet { Robot robot; @@ -86,6 +87,11 @@ public class SubMenuShowTest extends Applet { frame.setVisible(true); Util.waitForIdle(robot); + boolean isMacOSX = (OSInfo.getOSType() == OSInfo.OSType.MACOSX); + if (isMacOSX) { + robot.keyPress(KeyEvent.VK_CONTROL); + robot.delay(20); + } robot.keyPress(KeyEvent.VK_ALT); robot.delay(20); robot.keyPress(KeyEvent.VK_F); @@ -93,6 +99,10 @@ public class SubMenuShowTest extends Applet { robot.keyRelease(KeyEvent.VK_F); robot.delay(20); robot.keyRelease(KeyEvent.VK_ALT); + if (isMacOSX) { + robot.keyRelease(KeyEvent.VK_CONTROL); + robot.delay(20); + } Util.waitForIdle(robot); robot.keyPress(KeyEvent.VK_M); From 67fc68ea7f815c3c6b23fc29ce3f60998e9ad0a7 Mon Sep 17 00:00:00 2001 From: Stefan Karlsson Date: Tue, 15 Jan 2013 13:32:13 +0100 Subject: [PATCH 038/138] 8005590: java_lang_Class injected field resolved_constructor appears unused Reviewed-by: coleenp, dholmes --- hotspot/src/share/vm/classfile/javaClasses.cpp | 15 --------------- hotspot/src/share/vm/classfile/javaClasses.hpp | 6 ------ hotspot/src/share/vm/classfile/vmSymbols.hpp | 1 - hotspot/src/share/vm/oops/instanceKlass.cpp | 4 ---- hotspot/src/share/vm/runtime/vmStructs.cpp | 1 - 5 files changed, 27 deletions(-) diff --git a/hotspot/src/share/vm/classfile/javaClasses.cpp b/hotspot/src/share/vm/classfile/javaClasses.cpp index cb30a83d455..1c46188b728 100644 --- a/hotspot/src/share/vm/classfile/javaClasses.cpp +++ b/hotspot/src/share/vm/classfile/javaClasses.cpp @@ -687,19 +687,6 @@ void java_lang_Class::set_array_klass(oop java_class, Klass* klass) { } -Method* java_lang_Class::resolved_constructor(oop java_class) { - Metadata* constructor = java_class->metadata_field(_resolved_constructor_offset); - assert(constructor == NULL || constructor->is_method(), "should be method"); - return ((Method*)constructor); -} - - -void java_lang_Class::set_resolved_constructor(oop java_class, Method* constructor) { - assert(constructor->is_method(), "should be method"); - java_class->metadata_field_put(_resolved_constructor_offset, constructor); -} - - bool java_lang_Class::is_primitive(oop java_class) { // should assert: //assert(java_lang_Class::is_instance(java_class), "must be a Class object"); @@ -2949,7 +2936,6 @@ int java_lang_System::err_offset_in_bytes() { int java_lang_Class::_klass_offset; int java_lang_Class::_array_klass_offset; -int java_lang_Class::_resolved_constructor_offset; int java_lang_Class::_oop_size_offset; int java_lang_Class::_static_oop_field_count_offset; GrowableArray* java_lang_Class::_fixup_mirror_list = NULL; @@ -3303,7 +3289,6 @@ void JavaClasses::check_offsets() { // Fake fields // CHECK_OFFSET("java/lang/Class", java_lang_Class, klass); // %%% this needs to be checked // CHECK_OFFSET("java/lang/Class", java_lang_Class, array_klass); // %%% this needs to be checked - // CHECK_OFFSET("java/lang/Class", java_lang_Class, resolved_constructor); // %%% this needs to be checked // java.lang.Throwable diff --git a/hotspot/src/share/vm/classfile/javaClasses.hpp b/hotspot/src/share/vm/classfile/javaClasses.hpp index 5ab16251c10..fe48e0f614d 100644 --- a/hotspot/src/share/vm/classfile/javaClasses.hpp +++ b/hotspot/src/share/vm/classfile/javaClasses.hpp @@ -206,7 +206,6 @@ class java_lang_String : AllStatic { #define CLASS_INJECTED_FIELDS(macro) \ macro(java_lang_Class, klass, intptr_signature, false) \ - macro(java_lang_Class, resolved_constructor, intptr_signature, false) \ macro(java_lang_Class, array_klass, intptr_signature, false) \ macro(java_lang_Class, oop_size, int_signature, false) \ macro(java_lang_Class, static_oop_field_count, int_signature, false) @@ -218,7 +217,6 @@ class java_lang_Class : AllStatic { // The fake offsets are added by the class loader when java.lang.Class is loaded static int _klass_offset; - static int _resolved_constructor_offset; static int _array_klass_offset; static int _oop_size_offset; @@ -254,15 +252,11 @@ class java_lang_Class : AllStatic { static bool is_primitive(oop java_class); static BasicType primitive_type(oop java_class); static oop primitive_mirror(BasicType t); - // JVM_NewInstance support - static Method* resolved_constructor(oop java_class); - static void set_resolved_constructor(oop java_class, Method* constructor); // JVM_NewArray support static Klass* array_klass(oop java_class); static void set_array_klass(oop java_class, Klass* klass); // compiler support for class operations static int klass_offset_in_bytes() { return _klass_offset; } - static int resolved_constructor_offset_in_bytes() { return _resolved_constructor_offset; } static int array_klass_offset_in_bytes() { return _array_klass_offset; } // Support for classRedefinedCount field static int classRedefinedCount(oop the_class_mirror); diff --git a/hotspot/src/share/vm/classfile/vmSymbols.hpp b/hotspot/src/share/vm/classfile/vmSymbols.hpp index e6168ceba2c..9829e834875 100644 --- a/hotspot/src/share/vm/classfile/vmSymbols.hpp +++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp @@ -383,7 +383,6 @@ template(basicType_name, "basicType") \ template(append_name, "append") \ template(klass_name, "klass") \ - template(resolved_constructor_name, "resolved_constructor") \ template(array_klass_name, "array_klass") \ template(oop_size_name, "oop_size") \ template(static_oop_field_count_name, "static_oop_field_count") \ diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp index d123641a0c7..b0b7038e35b 100644 --- a/hotspot/src/share/vm/oops/instanceKlass.cpp +++ b/hotspot/src/share/vm/oops/instanceKlass.cpp @@ -2890,11 +2890,7 @@ void InstanceKlass::oop_print_on(oop obj, outputStream* st) { st->print(BULLET"fake entry for mirror: "); mirrored_klass->print_value_on_maybe_null(st); st->cr(); - st->print(BULLET"fake entry resolved_constructor: "); - Method* ctor = java_lang_Class::resolved_constructor(obj); - ctor->print_value_on_maybe_null(st); Klass* array_klass = java_lang_Class::array_klass(obj); - st->cr(); st->print(BULLET"fake entry for array: "); array_klass->print_value_on_maybe_null(st); st->cr(); diff --git a/hotspot/src/share/vm/runtime/vmStructs.cpp b/hotspot/src/share/vm/runtime/vmStructs.cpp index 9ac10e26d4c..c4172c18212 100644 --- a/hotspot/src/share/vm/runtime/vmStructs.cpp +++ b/hotspot/src/share/vm/runtime/vmStructs.cpp @@ -1200,7 +1200,6 @@ typedef BinaryTreeDictionary MetablockTreeDictionary; /*********************************/ \ \ static_field(java_lang_Class, _klass_offset, int) \ - static_field(java_lang_Class, _resolved_constructor_offset, int) \ static_field(java_lang_Class, _array_klass_offset, int) \ static_field(java_lang_Class, _oop_size_offset, int) \ static_field(java_lang_Class, _static_oop_field_count_offset, int) \ From 9d65c6d24f2326b52541e9ae6820d8a6ad2e8448 Mon Sep 17 00:00:00 2001 From: Stefan Karlsson Date: Tue, 15 Jan 2013 10:09:45 +0100 Subject: [PATCH 039/138] 8005994: Method annotations are allocated unnecessarily during class file parsing Also reviewed by: vitalyd@gmail.com Reviewed-by: coleenp, acorn --- .../share/vm/classfile/classFileParser.cpp | 47 ++++++++++++------- hotspot/src/share/vm/prims/jvm.cpp | 7 ++- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/hotspot/src/share/vm/classfile/classFileParser.cpp b/hotspot/src/share/vm/classfile/classFileParser.cpp index 9dd0640de06..102d551f00b 100644 --- a/hotspot/src/share/vm/classfile/classFileParser.cpp +++ b/hotspot/src/share/vm/classfile/classFileParser.cpp @@ -2475,26 +2475,38 @@ Array* ClassFileParser::parse_methods(ClassLoaderData* loader_data, *has_default_methods = true; } methods->at_put(index, method()); - if (*methods_annotations == NULL) { - *methods_annotations = - MetadataFactory::new_array(loader_data, length, NULL, CHECK_NULL); + + if (method_annotations != NULL) { + if (*methods_annotations == NULL) { + *methods_annotations = + MetadataFactory::new_array(loader_data, length, NULL, CHECK_NULL); + } + (*methods_annotations)->at_put(index, method_annotations); } - (*methods_annotations)->at_put(index, method_annotations); - if (*methods_parameter_annotations == NULL) { - *methods_parameter_annotations = - MetadataFactory::new_array(loader_data, length, NULL, CHECK_NULL); + + if (method_parameter_annotations != NULL) { + if (*methods_parameter_annotations == NULL) { + *methods_parameter_annotations = + MetadataFactory::new_array(loader_data, length, NULL, CHECK_NULL); + } + (*methods_parameter_annotations)->at_put(index, method_parameter_annotations); } - (*methods_parameter_annotations)->at_put(index, method_parameter_annotations); - if (*methods_default_annotations == NULL) { - *methods_default_annotations = - MetadataFactory::new_array(loader_data, length, NULL, CHECK_NULL); + + if (method_default_annotations != NULL) { + if (*methods_default_annotations == NULL) { + *methods_default_annotations = + MetadataFactory::new_array(loader_data, length, NULL, CHECK_NULL); + } + (*methods_default_annotations)->at_put(index, method_default_annotations); } - (*methods_default_annotations)->at_put(index, method_default_annotations); - if (*methods_type_annotations == NULL) { - *methods_type_annotations = - MetadataFactory::new_array(loader_data, length, NULL, CHECK_NULL); + + if (method_type_annotations != NULL) { + if (*methods_type_annotations == NULL) { + *methods_type_annotations = + MetadataFactory::new_array(loader_data, length, NULL, CHECK_NULL); + } + (*methods_type_annotations)->at_put(index, method_type_annotations); } - (*methods_type_annotations)->at_put(index, method_type_annotations); } if (_need_verify && length > 1) { @@ -3309,8 +3321,7 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, bool has_final_method = false; AccessFlags promoted_flags; promoted_flags.set_flags(0); - // These need to be oop pointers because they are allocated lazily - // inside parse_methods inside a nested HandleMark + Array* methods_annotations = NULL; Array* methods_parameter_annotations = NULL; Array* methods_default_annotations = NULL; diff --git a/hotspot/src/share/vm/prims/jvm.cpp b/hotspot/src/share/vm/prims/jvm.cpp index f899aac02e7..0bf74c44bb7 100644 --- a/hotspot/src/share/vm/prims/jvm.cpp +++ b/hotspot/src/share/vm/prims/jvm.cpp @@ -1582,8 +1582,11 @@ JVM_ENTRY(jbyteArray, JVM_GetClassTypeAnnotations(JNIEnv *env, jclass cls)) if (!java_lang_Class::is_primitive(JNIHandles::resolve(cls))) { Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve(cls)); if (k->oop_is_instance()) { - typeArrayOop a = Annotations::make_java_array(InstanceKlass::cast(k)->type_annotations()->class_annotations(), CHECK_NULL); - return (jbyteArray) JNIHandles::make_local(env, a); + Annotations* type_annotations = InstanceKlass::cast(k)->type_annotations(); + if (type_annotations != NULL) { + typeArrayOop a = Annotations::make_java_array(type_annotations->class_annotations(), CHECK_NULL); + return (jbyteArray) JNIHandles::make_local(env, a); + } } } return NULL; From 5bd55334906d2f425a07f265dbe511d0324413b8 Mon Sep 17 00:00:00 2001 From: Alexey Utkin Date: Tue, 15 Jan 2013 14:26:59 +0400 Subject: [PATCH 040/138] 8005250: Downgrade normative references to ${java.home}/lib folder from Java client code Javadoc was changed in accordance with CCC-8005250 request. Reviewed-by: alanb, amenkov --- .../awt/datatransfer/SystemFlavorMap.java | 11 +++------ .../javax/imageio/spi/IIORegistry.java | 14 ++++++----- .../classes/javax/sound/midi/MidiSystem.java | 8 ++++--- .../javax/sound/sampled/AudioSystem.java | 9 +++---- .../share/classes/javax/swing/UIManager.java | 24 +++++++++++-------- 5 files changed, 35 insertions(+), 31 deletions(-) diff --git a/jdk/src/share/classes/java/awt/datatransfer/SystemFlavorMap.java b/jdk/src/share/classes/java/awt/datatransfer/SystemFlavorMap.java index 81a43411563..eee5c0917d1 100644 --- a/jdk/src/share/classes/java/awt/datatransfer/SystemFlavorMap.java +++ b/jdk/src/share/classes/java/awt/datatransfer/SystemFlavorMap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -56,11 +56,6 @@ import sun.awt.datatransfer.DataTransferer; * by the data transfer subsystem to transfer data between Java and native * applications, and between Java applications in separate VMs. *

- * In the Sun reference implementation, the default SystemFlavorMap is - * initialized by the file jre/lib/flavormap.properties and the - * contents of the URL referenced by the AWT property - * AWT.DnD.flavorMapFileURL. See flavormap.properties - * for details. * * @since 1.2 */ @@ -1213,7 +1208,7 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { * are equal according to String.equals(Object). * *

- * Sun's reference implementation of this method returns the specified MIME + * The reference implementation of this method returns the specified MIME * type String prefixed with JAVA_DATAFLAVOR:. * * @param mimeType the MIME type to encode @@ -1241,7 +1236,7 @@ public final class SystemFlavorMap implements FlavorMap, FlavorTable { * according to String.equals(Object). * *

- * Sun's reference implementation of this method returns the MIME type + * The reference implementation of this method returns the MIME type * String of the specified DataFlavor prefixed * with JAVA_DATAFLAVOR:. * diff --git a/jdk/src/share/classes/javax/imageio/spi/IIORegistry.java b/jdk/src/share/classes/javax/imageio/spi/IIORegistry.java index 1baf476ca32..36deb519909 100644 --- a/jdk/src/share/classes/javax/imageio/spi/IIORegistry.java +++ b/jdk/src/share/classes/javax/imageio/spi/IIORegistry.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -64,9 +64,9 @@ import java.util.ServiceConfigurationError; * ImageWriter, ImageTranscoder, * ImageInputStream, and ImageOutputStream. * - *

Service providers found on the system classpath (e.g., - * the jre/lib/ext directory in Sun's implementation of - * JDK) are automatically loaded as soon as this class is + *

Service providers found on the system classpath (typically + * the lib/ext directory in the Java + * installation directory) are automatically loaded as soon as this class is * instantiated. * *

When the registerApplicationClasspathSpis method @@ -226,8 +226,10 @@ public final class IIORegistry extends ServiceRegistry { private void registerInstalledProviders() { /* - We need load installed providers from lib/ext - directory in the privileged mode in order to + We need to load installed providers from the + system classpath (typically the lib/ext + directory in in the Java installation directory) + in the privileged mode in order to be able read corresponding jar files even if file read capability is restricted (like the applet context case). diff --git a/jdk/src/share/classes/javax/sound/midi/MidiSystem.java b/jdk/src/share/classes/javax/sound/midi/MidiSystem.java index 7a3b186d92e..9d6763a6406 100644 --- a/jdk/src/share/classes/javax/sound/midi/MidiSystem.java +++ b/jdk/src/share/classes/javax/sound/midi/MidiSystem.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -68,8 +68,10 @@ import com.sun.media.sound.MidiDeviceTransmitterEnvelope; * *

Properties can be used to specify default MIDI devices. * Both system properties and a properties file are considered. - * The properties file is "lib/sound.properties" in the JRE - * directory. If a property exists both as a system property and in the + * The sound.properties properties file is read from + * an implementation-specific location (typically it is the lib + * directory in the Java installation directory). + * If a property exists both as a system property and in the * properties file, the system property takes precedence. If none is * specified, a suitable default is chosen among the available devices. * The syntax of the properties file is specified in diff --git a/jdk/src/share/classes/javax/sound/sampled/AudioSystem.java b/jdk/src/share/classes/javax/sound/sampled/AudioSystem.java index 9a47d9f4632..cf06ca25741 100644 --- a/jdk/src/share/classes/javax/sound/sampled/AudioSystem.java +++ b/jdk/src/share/classes/javax/sound/sampled/AudioSystem.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -63,9 +63,10 @@ import com.sun.media.sound.JDK13Services; *

Properties can be used to specify the default mixer * for specific line types. * Both system properties and a properties file are considered. - * In the Oracle reference implementation, the properties file is - * "lib/sound.properties" in the JRE - * directory. If a property exists both as a system property and in the + * The sound.properties properties file is read from + * an implementation-specific location (typically it is the lib + * directory in the Java installation directory). + * If a property exists both as a system property and in the * properties file, the system property takes precedence. If none is * specified, a suitable default is chosen among the available devices. * The syntax of the properties file is specified in diff --git a/jdk/src/share/classes/javax/swing/UIManager.java b/jdk/src/share/classes/javax/swing/UIManager.java index d135e34b5c9..17a7b9e81dc 100644 --- a/jdk/src/share/classes/javax/swing/UIManager.java +++ b/jdk/src/share/classes/javax/swing/UIManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -105,8 +105,9 @@ import sun.awt.AWTAccessor; * exists and contains the key swing.defaultlaf, * use its value as the default look and feel class name. The location * that is checked for swing.properties may vary depending - * upon the implementation of the Java platform. In Sun's implementation - * the location is ${java.home}/lib/swing.properties. + * upon the implementation of the Java platform. Typically the + * swing.properties file is located in the lib + * subdirectory of the Java installation directory. * Refer to the release notes of the implementation being used for * further details. *

  • Otherwise use the cross platform look and feel. @@ -256,7 +257,7 @@ public class UIManager implements Serializable } - /* Keys used for the properties file in /lib/swing.properties. + /* Keys used in the swing.properties properties file. * See loadUserProperties(), initialize(). */ @@ -267,7 +268,7 @@ public class UIManager implements Serializable private static final String disableMnemonicKey = "swing.disablenavaids"; /** - * Return a swing.properties file key for the attribute of specified + * Return a swing.properties file key for the attribute of specified * look and feel. The attr is either "name" or "class", a typical * key would be: "swing.installedlaf.windows.name" */ @@ -276,9 +277,11 @@ public class UIManager implements Serializable } /** - * The filename for swing.properties is a path like this (Unix version): - * /lib/swing.properties. This method returns a bogus - * filename if java.home isn't defined. + * The location of the swing.properties property file is + * implementation-specific. + * It is typically located in the lib subdirectory of the Java + * installation directory. This method returns a bogus filename + * if java.home isn't defined. */ private static String makeSwingPropertiesFilename() { String sep = File.separator; @@ -352,7 +355,7 @@ public class UIManager implements Serializable /** * The default value of installedLAFS is used when no - * swing.properties + * swing.properties * file is available or if the file doesn't contain a "swing.installedlafs" * property. * @@ -1271,7 +1274,8 @@ public class UIManager implements Serializable /** - * If a swing.properties file exist and it has a swing.installedlafs property + * If a swing.properties file exist and it has a + * swing.installedlafs property * then initialize the installedLAFs field. * * @see #getInstalledLookAndFeels From cb7b639e6a609d483ef734778a6f552caf97b990 Mon Sep 17 00:00:00 2001 From: Mark Sheppard Date: Tue, 15 Jan 2013 11:44:20 +0000 Subject: [PATCH 041/138] 8005406: HTTP server implementation should use Base64 API Reviewed-by: khazra, alanb, chegar --- .../net/httpserver/BasicAuthenticator.java | 209 +----------------- .../protocol/http/BasicAuthentication.java | 1 - 2 files changed, 3 insertions(+), 207 deletions(-) diff --git a/jdk/src/share/classes/com/sun/net/httpserver/BasicAuthenticator.java b/jdk/src/share/classes/com/sun/net/httpserver/BasicAuthenticator.java index 781510a2cca..60ccd3ab73d 100644 --- a/jdk/src/share/classes/com/sun/net/httpserver/BasicAuthenticator.java +++ b/jdk/src/share/classes/com/sun/net/httpserver/BasicAuthenticator.java @@ -25,6 +25,8 @@ package com.sun.net.httpserver; +import java.util.Base64; + /** * BasicAuthenticator provides an implementation of HTTP Basic * authentication. It is an abstract class and must be extended @@ -68,7 +70,7 @@ public abstract class BasicAuthenticator extends Authenticator { if (sp == -1 || !auth.substring(0, sp).equals ("Basic")) { return new Authenticator.Failure (401); } - byte[] b = Base64.base64ToByteArray (auth.substring(sp+1)); + byte[] b = Base64.getDecoder().decode(auth.substring(sp+1)); String userpass = new String (b); int colon = userpass.indexOf (':'); String uname = userpass.substring (0, colon); @@ -102,208 +104,3 @@ public abstract class BasicAuthenticator extends Authenticator { public abstract boolean checkCredentials (String username, String password); } -class Base64 { - - /** - * Translates the specified byte array into a Base64 string as per - * Preferences.put(byte[]). - */ - static String byteArrayToBase64(byte[] a) { - return byteArrayToBase64(a, false); - } - - /** - * Translates the specified byte array into an "aternate representation" - * Base64 string. This non-standard variant uses an alphabet that does - * not contain the uppercase alphabetic characters, which makes it - * suitable for use in situations where case-folding occurs. - */ - static String byteArrayToAltBase64(byte[] a) { - return byteArrayToBase64(a, true); - } - - private static String byteArrayToBase64(byte[] a, boolean alternate) { - int aLen = a.length; - int numFullGroups = aLen/3; - int numBytesInPartialGroup = aLen - 3*numFullGroups; - int resultLen = 4*((aLen + 2)/3); - StringBuffer result = new StringBuffer(resultLen); - char[] intToAlpha = (alternate ? intToAltBase64 : intToBase64); - - // Translate all full groups from byte array elements to Base64 - int inCursor = 0; - for (int i=0; i> 2]); - result.append(intToAlpha[(byte0 << 4)&0x3f | (byte1 >> 4)]); - result.append(intToAlpha[(byte1 << 2)&0x3f | (byte2 >> 6)]); - result.append(intToAlpha[byte2 & 0x3f]); - } - - // Translate partial group if present - if (numBytesInPartialGroup != 0) { - int byte0 = a[inCursor++] & 0xff; - result.append(intToAlpha[byte0 >> 2]); - if (numBytesInPartialGroup == 1) { - result.append(intToAlpha[(byte0 << 4) & 0x3f]); - result.append("=="); - } else { - // assert numBytesInPartialGroup == 2; - int byte1 = a[inCursor++] & 0xff; - result.append(intToAlpha[(byte0 << 4)&0x3f | (byte1 >> 4)]); - result.append(intToAlpha[(byte1 << 2)&0x3f]); - result.append('='); - } - } - // assert inCursor == a.length; - // assert result.length() == resultLen; - return result.toString(); - } - - /** - * This array is a lookup table that translates 6-bit positive integer - * index values into their "Base64 Alphabet" equivalents as specified - * in Table 1 of RFC 2045. - */ - private static final char intToBase64[] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' - }; - - /** - * This array is a lookup table that translates 6-bit positive integer - * index values into their "Alternate Base64 Alphabet" equivalents. - * This is NOT the real Base64 Alphabet as per in Table 1 of RFC 2045. - * This alternate alphabet does not use the capital letters. It is - * designed for use in environments where "case folding" occurs. - */ - private static final char intToAltBase64[] = { - '!', '"', '#', '$', '%', '&', '\'', '(', ')', ',', '-', '.', ':', - ';', '<', '>', '@', '[', ']', '^', '`', '_', '{', '|', '}', '~', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '?' - }; - - /** - * Translates the specified Base64 string (as per Preferences.get(byte[])) - * into a byte array. - * - * @throw IllegalArgumentException if s is not a valid Base64 - * string. - */ - static byte[] base64ToByteArray(String s) { - return base64ToByteArray(s, false); - } - - /** - * Translates the specified "aternate representation" Base64 string - * into a byte array. - * - * @throw IllegalArgumentException or ArrayOutOfBoundsException - * if s is not a valid alternate representation - * Base64 string. - */ - static byte[] altBase64ToByteArray(String s) { - return base64ToByteArray(s, true); - } - - private static byte[] base64ToByteArray(String s, boolean alternate) { - byte[] alphaToInt = (alternate ? altBase64ToInt : base64ToInt); - int sLen = s.length(); - int numGroups = sLen/4; - if (4*numGroups != sLen) - throw new IllegalArgumentException( - "String length must be a multiple of four."); - int missingBytesInLastGroup = 0; - int numFullGroups = numGroups; - if (sLen != 0) { - if (s.charAt(sLen-1) == '=') { - missingBytesInLastGroup++; - numFullGroups--; - } - if (s.charAt(sLen-2) == '=') - missingBytesInLastGroup++; - } - byte[] result = new byte[3*numGroups - missingBytesInLastGroup]; - - // Translate all full groups from base64 to byte array elements - int inCursor = 0, outCursor = 0; - for (int i=0; i> 4)); - result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2)); - result[outCursor++] = (byte) ((ch2 << 6) | ch3); - } - - // Translate partial group, if present - if (missingBytesInLastGroup != 0) { - int ch0 = base64toInt(s.charAt(inCursor++), alphaToInt); - int ch1 = base64toInt(s.charAt(inCursor++), alphaToInt); - result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4)); - - if (missingBytesInLastGroup == 1) { - int ch2 = base64toInt(s.charAt(inCursor++), alphaToInt); - result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2)); - } - } - // assert inCursor == s.length()-missingBytesInLastGroup; - // assert outCursor == result.length; - return result; - } - - /** - * Translates the specified character, which is assumed to be in the - * "Base 64 Alphabet" into its equivalent 6-bit positive integer. - * - * @throw IllegalArgumentException or ArrayOutOfBoundsException if - * c is not in the Base64 Alphabet. - */ - private static int base64toInt(char c, byte[] alphaToInt) { - int result = alphaToInt[c]; - if (result < 0) - throw new IllegalArgumentException("Illegal character " + c); - return result; - } - - /** - * This array is a lookup table that translates unicode characters - * drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045) - * into their 6-bit positive integer equivalents. Characters that - * are not in the Base64 alphabet but fall within the bounds of the - * array are translated to -1. - */ - private static final byte base64ToInt[] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, - 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, - 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 - }; - - /** - * This array is the analogue of base64ToInt, but for the nonstandard - * variant that avoids the use of uppercase alphabetic characters. - */ - private static final byte altBase64ToInt[] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, - 2, 3, 4, 5, 6, 7, 8, -1, 62, 9, 10, 11, -1 , 52, 53, 54, 55, 56, 57, - 58, 59, 60, 61, 12, 13, 14, -1, 15, 63, 16, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 17, -1, 18, 19, 21, 20, 26, 27, 28, 29, 30, 31, 32, 33, - 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, - 51, 22, 23, 24, 25 - }; - -} diff --git a/jdk/src/share/classes/sun/net/www/protocol/http/BasicAuthentication.java b/jdk/src/share/classes/sun/net/www/protocol/http/BasicAuthentication.java index 50de0383952..2cc087f8233 100644 --- a/jdk/src/share/classes/sun/net/www/protocol/http/BasicAuthentication.java +++ b/jdk/src/share/classes/sun/net/www/protocol/http/BasicAuthentication.java @@ -32,7 +32,6 @@ import java.net.PasswordAuthentication; import java.io.IOException; import java.io.OutputStream; import java.util.Base64; -import java.util.Base64.Encoder; import sun.net.www.HeaderParser; /** From 08f29b04df596c4c082b01ad96bec5a943b8cc1a Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Tue, 15 Jan 2013 21:57:47 +0400 Subject: [PATCH 042/138] 7124525: [macosx] No animation on certain Swing components in Aqua LaF Reviewed-by: alexsch, swingler --- .../classes/com/apple/laf/AquaPainter.java | 44 ++++++++----------- .../classes/com/apple/laf/ImageCache.java | 22 ++++++---- 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/jdk/src/macosx/classes/com/apple/laf/AquaPainter.java b/jdk/src/macosx/classes/com/apple/laf/AquaPainter.java index cfafa43d9de..303938d5988 100644 --- a/jdk/src/macosx/classes/com/apple/laf/AquaPainter.java +++ b/jdk/src/macosx/classes/com/apple/laf/AquaPainter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,7 +37,6 @@ import sun.awt.image.*; import sun.java2d.*; import sun.print.*; import apple.laf.*; -import apple.laf.JRSUIConstants.Widget; import apple.laf.JRSUIUtils.NineSliceMetricsProvider; abstract class AquaPainter { @@ -63,7 +62,7 @@ abstract class AquaPainter { } static AquaPainter create(final T state, final NineSliceMetricsProvider metricsProvider) { - return new AquaNineSlicingImagePainter(state, metricsProvider); + return new AquaNineSlicingImagePainter<>(state, metricsProvider); } abstract void paint(final Graphics2D g, final T stateToPaint, final Component c); @@ -71,7 +70,7 @@ abstract class AquaPainter { final Rectangle boundsRect = new Rectangle(); final JRSUIControl control; T state; - public AquaPainter(final JRSUIControl control, final T state) { + AquaPainter(final JRSUIControl control, final T state) { this.control = control; this.state = state; } @@ -94,14 +93,14 @@ abstract class AquaPainter { protected final HashMap slicedControlImages; protected final NineSliceMetricsProvider metricsProvider; - public AquaNineSlicingImagePainter(final T state) { + AquaNineSlicingImagePainter(final T state) { this(state, null); } - public AquaNineSlicingImagePainter(final T state, final NineSliceMetricsProvider metricsProvider) { + AquaNineSlicingImagePainter(final T state, final NineSliceMetricsProvider metricsProvider) { super(new JRSUIControl(false), state); this.metricsProvider = metricsProvider; - slicedControlImages = new HashMap(); + slicedControlImages = new HashMap<>(); } @Override @@ -127,7 +126,7 @@ abstract class AquaPainter { } static class AquaSingleImagePainter extends AquaPainter { - public AquaSingleImagePainter(final T state) { + AquaSingleImagePainter(final T state) { super(new JRSUIControl(false), state); } @@ -137,12 +136,12 @@ abstract class AquaPainter { } static void paintFromSingleCachedImage(final Graphics2D g, final JRSUIControl control, final JRSUIState controlState, final Component c, final Rectangle boundsRect) { - Rectangle clipRect = g.getClipBounds(); - Rectangle intersection = boundsRect.intersection(clipRect); + final Rectangle clipRect = g.getClipBounds(); + final Rectangle intersection = boundsRect.intersection(clipRect); if (intersection.width <= 0 || intersection.height <= 0) return; - int imgX1 = intersection.x - boundsRect.x; - int imgY1 = intersection.y - boundsRect.y; + final int imgX1 = intersection.x - boundsRect.x; + final int imgY1 = intersection.y - boundsRect.y; final GraphicsConfiguration config = g.getDeviceConfiguration(); final ImageCache cache = ImageCache.getInstance(); @@ -150,20 +149,15 @@ abstract class AquaPainter { if (image == null) { image = new BufferedImage(boundsRect.width, boundsRect.height, BufferedImage.TYPE_INT_ARGB_PRE); cache.setImage(image, config, boundsRect.width, boundsRect.height, controlState); - } else { - g.drawImage(image, intersection.x, intersection.y, intersection.x + intersection.width, intersection.y + intersection.height, - imgX1, imgY1, imgX1 + intersection.width, imgY1 + intersection.height, null); - return; + final WritableRaster raster = image.getRaster(); + final DataBufferInt buffer = (DataBufferInt)raster.getDataBuffer(); + + control.set(controlState); + control.paint(SunWritableRaster.stealData(buffer, 0), + image.getWidth(), image.getHeight(), 0, 0, boundsRect.width, boundsRect.height); + SunWritableRaster.markDirty(buffer); } - final WritableRaster raster = image.getRaster(); - final DataBufferInt buffer = (DataBufferInt)raster.getDataBuffer(); - - control.set(controlState); - control.paint(SunWritableRaster.stealData(buffer, 0), - image.getWidth(), image.getHeight(), 0, 0, boundsRect.width, boundsRect.height); - SunWritableRaster.markDirty(buffer); - g.drawImage(image, intersection.x, intersection.y, intersection.x + intersection.width, intersection.y + intersection.height, imgX1, imgY1, imgX1 + intersection.width, imgY1 + intersection.height, null); } @@ -173,7 +167,7 @@ abstract class AquaPainter { final JRSUIControl control; final JRSUIState state; - public RecyclableJRSUISlicedImageControl(final JRSUIControl control, final JRSUIState state, final NineSliceMetrics metrics) { + RecyclableJRSUISlicedImageControl(final JRSUIControl control, final JRSUIState state, final NineSliceMetrics metrics) { super(metrics); this.control = control; this.state = state; diff --git a/jdk/src/macosx/classes/com/apple/laf/ImageCache.java b/jdk/src/macosx/classes/com/apple/laf/ImageCache.java index 7ed83ae961c..f9af5c06161 100644 --- a/jdk/src/macosx/classes/com/apple/laf/ImageCache.java +++ b/jdk/src/macosx/classes/com/apple/laf/ImageCache.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ import java.lang.ref.*; import java.util.*; import java.util.concurrent.locks.*; +import apple.laf.JRSUIConstants; import apple.laf.JRSUIState; import com.apple.laf.AquaUtils.RecyclableSingleton; @@ -38,9 +39,9 @@ import com.apple.laf.AquaUtils.RecyclableSingleton; * SoftReferences so they will be dropped by the GC if heap memory gets tight. When our size hits max pixel count least * recently requested images are removed first. */ -class ImageCache { +final class ImageCache { // Ordered Map keyed by args hash, ordered by most recent accessed entry. - private final LinkedHashMap map = new LinkedHashMap(16, 0.75f, true); + private final LinkedHashMap map = new LinkedHashMap<>(16, 0.75f, true); // Maximum number of pixels to cache, this is used if maxCount private final int maxPixelCount; @@ -50,7 +51,7 @@ class ImageCache { // Lock for concurrent access to map private final ReadWriteLock lock = new ReentrantReadWriteLock(); // Reference queue for tracking lost softreferences to images in the cache - private final ReferenceQueue referenceQueue = new ReferenceQueue(); + private final ReferenceQueue referenceQueue = new ReferenceQueue<>(); // Singleton Instance private static final RecyclableSingleton instance = new RecyclableSingleton() { @@ -63,11 +64,11 @@ class ImageCache { return instance.get(); } - public ImageCache(final int maxPixelCount) { + ImageCache(final int maxPixelCount) { this.maxPixelCount = maxPixelCount; } - public ImageCache() { + ImageCache() { this((8 * 1024 * 1024) / 4); // 8Mb of pixels } @@ -99,10 +100,13 @@ class ImageCache { * @param config The graphics configuration, needed if cached image is a Volatile Image. Used as part of cache key * @param w The image width, used as part of cache key * @param h The image height, used as part of cache key - * @param args Other arguments to use as part of the cache key - * @return true if the image could be cached or false if the image is too big + * @return true if the image could be cached, false otherwise. */ public boolean setImage(final Image image, final GraphicsConfiguration config, final int w, final int h, final JRSUIState state) { + if (state.is(JRSUIConstants.Animating.YES)) { + return false; + } + final int hash = hash(config, w, h, state); lock.writeLock().lock(); @@ -167,7 +171,7 @@ class ImageCache { private final int h; private final JRSUIState state; - public PixelCountSoftReference(final Image referent, final ReferenceQueue q, final int pixelCount, final int hash, final GraphicsConfiguration config, final int w, final int h, final JRSUIState state) { + PixelCountSoftReference(final Image referent, final ReferenceQueue q, final int pixelCount, final int hash, final GraphicsConfiguration config, final int w, final int h, final JRSUIState state) { super(referent, q); this.pixelCount = pixelCount; this.hash = hash; From bdca3e8f8c9502118e79aa899b63a66f946bbaa4 Mon Sep 17 00:00:00 2001 From: Rob McKenna Date: Tue, 15 Jan 2013 19:58:22 +0000 Subject: [PATCH 043/138] 8005618: TEST_BUG: java/lang/ProcessBuilder/Basic.java failing intermittently Reviewed-by: alanb, martin, dholmes --- jdk/test/java/lang/ProcessBuilder/Basic.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/jdk/test/java/lang/ProcessBuilder/Basic.java b/jdk/test/java/lang/ProcessBuilder/Basic.java index 8ad7bd93709..a99e6919371 100644 --- a/jdk/test/java/lang/ProcessBuilder/Basic.java +++ b/jdk/test/java/lang/ProcessBuilder/Basic.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2218,7 +2218,7 @@ public class Basic { start = System.nanoTime(); p.waitFor(1000, TimeUnit.MILLISECONDS); end = System.nanoTime(); - if ((end - start) > 100000000) + if ((end - start) > 900000000) fail("Test failed: waitFor took too long on a dead process."); } catch (Throwable t) { unexpected(t); } @@ -2231,11 +2231,13 @@ public class Basic { childArgs.add("sleep"); final Process p = new ProcessBuilder(childArgs).start(); final long start = System.nanoTime(); + final CountDownLatch latch = new CountDownLatch(1); final Thread thread = new Thread() { public void run() { try { try { + latch.countDown(); p.waitFor(10000, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { return; @@ -2244,6 +2246,7 @@ public class Basic { } catch (Throwable t) { unexpected(t); }}}; thread.start(); + latch.await(); Thread.sleep(1000); thread.interrupt(); p.destroy(); From 5dda34f79842cfe079f94fcd169517a71621d87b Mon Sep 17 00:00:00 2001 From: Christian Thalinger Date: Tue, 15 Jan 2013 12:06:18 -0800 Subject: [PATCH 044/138] 8006109: test/java/util/AbstractSequentialList/AddAll.java fails: assert(rtype == ctype) failed: mismatched return types Reviewed-by: kvn --- hotspot/src/share/vm/ci/ciType.cpp | 16 +++++++++++++++- hotspot/src/share/vm/ci/ciType.hpp | 1 + hotspot/src/share/vm/opto/doCall.cpp | 8 +++++++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/hotspot/src/share/vm/ci/ciType.cpp b/hotspot/src/share/vm/ci/ciType.cpp index 47412130f04..e74dd921804 100644 --- a/hotspot/src/share/vm/ci/ciType.cpp +++ b/hotspot/src/share/vm/ci/ciType.cpp @@ -59,6 +59,19 @@ bool ciType::is_subtype_of(ciType* type) { return false; } +// ------------------------------------------------------------------ +// ciType::name +// +// Return the name of this type +const char* ciType::name() { + if (is_primitive_type()) { + return type2name(basic_type()); + } else { + assert(is_klass(), "must be"); + return as_klass()->name()->as_utf8(); + } +} + // ------------------------------------------------------------------ // ciType::print_impl // @@ -73,7 +86,8 @@ void ciType::print_impl(outputStream* st) { // // Print the name of this type void ciType::print_name_on(outputStream* st) { - st->print(type2name(basic_type())); + ResourceMark rm; + st->print(name()); } diff --git a/hotspot/src/share/vm/ci/ciType.hpp b/hotspot/src/share/vm/ci/ciType.hpp index 807ae1bec92..25f79e01263 100644 --- a/hotspot/src/share/vm/ci/ciType.hpp +++ b/hotspot/src/share/vm/ci/ciType.hpp @@ -77,6 +77,7 @@ public: bool is_type() const { return true; } bool is_classless() const { return is_primitive_type(); } + const char* name(); virtual void print_name_on(outputStream* st); void print_name() { print_name_on(tty); diff --git a/hotspot/src/share/vm/opto/doCall.cpp b/hotspot/src/share/vm/opto/doCall.cpp index 5d094c99afc..9a7562d01fb 100644 --- a/hotspot/src/share/vm/opto/doCall.cpp +++ b/hotspot/src/share/vm/opto/doCall.cpp @@ -553,7 +553,13 @@ void Parse::do_call() { rtype = ctype; } } else { - assert(rtype == ctype, "mismatched return types"); // symbolic resolution enforces this + // Symbolic resolution enforces the types to be the same. + // NOTE: We must relax the assert for unloaded types because two + // different ciType instances of the same unloaded class type + // can appear to be "loaded" by different loaders (depending on + // the accessing class). + assert(!rtype->is_loaded() || !ctype->is_loaded() || rtype == ctype, + err_msg_res("mismatched return types: rtype=%s, ctype=%s", rtype->name(), ctype->name())); } // If the return type of the method is not loaded, assert that the From b3b1b412b1e94f437b9b5bcda4d5cbb2713470f4 Mon Sep 17 00:00:00 2001 From: John Cuthbertson Date: Tue, 15 Jan 2013 12:32:26 -0800 Subject: [PATCH 045/138] 8001425: G1: Change the default values for certain G1 specific flags Changes to default and ergonomic flag values recommended by performance team. Changes were also reviewed by Monica Beckwith . Reviewed-by: brutisso, huntch --- .../src/share/vm/gc_implementation/g1/g1_globals.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp index 92b1cb3fc49..d362956ea80 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -287,24 +287,24 @@ "The number of times we'll force an overflow during " \ "concurrent marking") \ \ - experimental(uintx, G1NewSizePercent, 20, \ + experimental(uintx, G1NewSizePercent, 5, \ "Percentage (0-100) of the heap size to use as default " \ "minimum young gen size.") \ \ - experimental(uintx, G1MaxNewSizePercent, 80, \ + experimental(uintx, G1MaxNewSizePercent, 60, \ "Percentage (0-100) of the heap size to use as default " \ " maximum young gen size.") \ \ - experimental(uintx, G1MixedGCLiveThresholdPercent, 90, \ + experimental(uintx, G1MixedGCLiveThresholdPercent, 65, \ "Threshold for regions to be considered for inclusion in the " \ "collection set of mixed GCs. " \ "Regions with live bytes exceeding this will not be collected.") \ \ - product(uintx, G1HeapWastePercent, 5, \ + product(uintx, G1HeapWastePercent, 10, \ "Amount of space, expressed as a percentage of the heap size, " \ "that G1 is willing not to collect to avoid expensive GCs.") \ \ - product(uintx, G1MixedGCCountTarget, 4, \ + product(uintx, G1MixedGCCountTarget, 8, \ "The target number of mixed GCs after a marking cycle.") \ \ experimental(uintx, G1OldCSetRegionThresholdPercent, 10, \ From 84fce989ddf522c4b558064b9e6afa9aad404951 Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Tue, 15 Jan 2013 17:05:53 -0500 Subject: [PATCH 046/138] 8005467: CDS size information is incorrect and unfriendly Changed words to bytes, and added usage percentage information Reviewed-by: coleenp, twisti --- .../src/share/vm/memory/metaspaceShared.cpp | 47 +++++++++++++++---- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/hotspot/src/share/vm/memory/metaspaceShared.cpp b/hotspot/src/share/vm/memory/metaspaceShared.cpp index 5954e43d05a..4f53114c6cd 100644 --- a/hotspot/src/share/vm/memory/metaspaceShared.cpp +++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -373,17 +373,44 @@ void VM_PopulateDumpSharedSpace::doit() { md_top = wc.get_top(); // Print shared spaces all the time - const char* fmt = "%s space: " PTR_FORMAT " out of " PTR_FORMAT " words allocated at " PTR_FORMAT "."; + const char* fmt = "%s space: %9d [ %4.1f%% of total] out of %9d bytes [%4.1f%% used] at " PTR_FORMAT; Metaspace* ro_space = _loader_data->ro_metaspace(); Metaspace* rw_space = _loader_data->rw_metaspace(); - tty->print_cr(fmt, "ro", ro_space->used_words(Metaspace::NonClassType), - ro_space->capacity_words(Metaspace::NonClassType), - ro_space->bottom()); - tty->print_cr(fmt, "rw", rw_space->used_words(Metaspace::NonClassType), - rw_space->capacity_words(Metaspace::NonClassType), - rw_space->bottom()); - tty->print_cr(fmt, "md", md_top - md_low, md_end-md_low, md_low); - tty->print_cr(fmt, "mc", mc_top - mc_low, mc_end-mc_low, mc_low); + const size_t BPW = BytesPerWord; + + // Allocated size of each space (may not be all occupied) + const size_t ro_alloced = ro_space->capacity_words(Metaspace::NonClassType) * BPW; + const size_t rw_alloced = rw_space->capacity_words(Metaspace::NonClassType) * BPW; + const size_t md_alloced = md_end-md_low; + const size_t mc_alloced = mc_end-mc_low; + const size_t total_alloced = ro_alloced + rw_alloced + md_alloced + mc_alloced; + + // Occupied size of each space. + const size_t ro_bytes = ro_space->used_words(Metaspace::NonClassType) * BPW; + const size_t rw_bytes = rw_space->used_words(Metaspace::NonClassType) * BPW; + const size_t md_bytes = size_t(md_top - md_low); + const size_t mc_bytes = size_t(mc_top - mc_low); + + // Percent of total size + const size_t total_bytes = ro_bytes + rw_bytes + md_bytes + mc_bytes; + const double ro_t_perc = ro_bytes / double(total_bytes) * 100.0; + const double rw_t_perc = rw_bytes / double(total_bytes) * 100.0; + const double md_t_perc = md_bytes / double(total_bytes) * 100.0; + const double mc_t_perc = mc_bytes / double(total_bytes) * 100.0; + + // Percent of fullness of each space + const double ro_u_perc = ro_bytes / double(ro_alloced) * 100.0; + const double rw_u_perc = rw_bytes / double(rw_alloced) * 100.0; + const double md_u_perc = md_bytes / double(md_alloced) * 100.0; + const double mc_u_perc = mc_bytes / double(mc_alloced) * 100.0; + const double total_u_perc = total_bytes / double(total_alloced) * 100.0; + + tty->print_cr(fmt, "ro", ro_bytes, ro_t_perc, ro_alloced, ro_u_perc, ro_space->bottom()); + tty->print_cr(fmt, "rw", rw_bytes, rw_t_perc, rw_alloced, rw_u_perc, rw_space->bottom()); + tty->print_cr(fmt, "md", md_bytes, md_t_perc, md_alloced, md_u_perc, md_low); + tty->print_cr(fmt, "mc", mc_bytes, mc_t_perc, mc_alloced, mc_u_perc, mc_low); + tty->print_cr("total : %9d [100.0%% of total] out of %9d bytes [%4.1f%% used]", + total_bytes, total_alloced, total_u_perc); // Update the vtable pointers in all of the Klass objects in the // heap. They should point to newly generated vtable. From cc15237ca514c501a2e9ffff0386d4d890634d83 Mon Sep 17 00:00:00 2001 From: David Chase Date: Tue, 15 Jan 2013 14:45:12 -0800 Subject: [PATCH 047/138] 8005821: C2: -XX:+PrintIntrinsics is broken Check all print inlining flags when processing inlining list. Reviewed-by: kvn, twisti --- hotspot/src/share/vm/opto/compile.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hotspot/src/share/vm/opto/compile.cpp b/hotspot/src/share/vm/opto/compile.cpp index f090939186d..ae9d6996a9c 100644 --- a/hotspot/src/share/vm/opto/compile.cpp +++ b/hotspot/src/share/vm/opto/compile.cpp @@ -692,7 +692,7 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr PhaseGVN gvn(node_arena(), estimated_size); set_initial_gvn(&gvn); - if (PrintInlining) { + if (PrintInlining || PrintIntrinsics NOT_PRODUCT( || PrintOptoInlining)) { _print_inlining_list = new (comp_arena())GrowableArray(comp_arena(), 1, 1, PrintInliningBuffer()); } { // Scope for timing the parser @@ -2049,7 +2049,7 @@ void Compile::Optimize() { } // (End scope of igvn; run destructor if necessary for asserts.) - dump_inlining(); + dump_inlining(); // A method with only infinite loops has no edges entering loops from root { NOT_PRODUCT( TracePhase t2("graphReshape", &_t_graphReshaping, TimeCompiler); ) @@ -3497,7 +3497,7 @@ void Compile::ConstantTable::fill_jump_table(CodeBuffer& cb, MachConstantNode* n } void Compile::dump_inlining() { - if (PrintInlining) { + if (PrintInlining || PrintIntrinsics NOT_PRODUCT( || PrintOptoInlining)) { // Print inlining message for candidates that we couldn't inline // for lack of space or non constant receiver for (int i = 0; i < _late_inlines.length(); i++) { From 7062898817ceb2d8519173f18a4f16b46bcd0efd Mon Sep 17 00:00:00 2001 From: Doug Lea Date: Wed, 16 Jan 2013 10:14:09 +0000 Subject: [PATCH 048/138] 8005926: Merge ThreadLocalRandom state into java.lang.Thread Reviewed-by: shade, chegar --- jdk/src/share/classes/java/lang/Thread.java | 10 + .../util/concurrent/ThreadLocalRandom.java | 290 +++++++++++++++--- 2 files changed, 262 insertions(+), 38 deletions(-) diff --git a/jdk/src/share/classes/java/lang/Thread.java b/jdk/src/share/classes/java/lang/Thread.java index 15588ff0f9d..64987bdc2fa 100644 --- a/jdk/src/share/classes/java/lang/Thread.java +++ b/jdk/src/share/classes/java/lang/Thread.java @@ -2028,6 +2028,16 @@ class Thread implements Runnable { } } + + // The following three initially uninitialized fields are exclusively + // managed by class java.util.concurrent.ThreadLocalRandom. + /** The current seed for a ThreadLocalRandom */ + long threadLocalRandomSeed; + /** Probe hash value; nonzero if threadLocalRandomSeed initialized */ + int threadLocalRandomProbe; + /** Secondary seed isolated from public ThreadLocalRandom sequence */ + int threadLocalRandomSecondarySeed; + /* Some private helper methods */ private native void setPriority0(int newPriority); private native void stop0(Object o); diff --git a/jdk/src/share/classes/java/util/concurrent/ThreadLocalRandom.java b/jdk/src/share/classes/java/util/concurrent/ThreadLocalRandom.java index aa8210bce4a..08c870f0c22 100644 --- a/jdk/src/share/classes/java/util/concurrent/ThreadLocalRandom.java +++ b/jdk/src/share/classes/java/util/concurrent/ThreadLocalRandom.java @@ -35,7 +35,10 @@ package java.util.concurrent; +import java.io.ObjectStreamField; import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; /** * A random number generator isolated to the current thread. Like the @@ -62,46 +65,105 @@ import java.util.Random; * @author Doug Lea */ public class ThreadLocalRandom extends Random { + /* + * This class implements the java.util.Random API (and subclasses + * Random) using a single static instance that accesses random + * number state held in class Thread (primarily, field + * threadLocalRandomSeed). In doing so, it also provides a home + * for managing package-private utilities that rely on exactly the + * same state as needed to maintain the ThreadLocalRandom + * instances. We leverage the need for an initialization flag + * field to also use it as a "probe" -- a self-adjusting thread + * hash used for contention avoidance, as well as a secondary + * simpler (xorShift) random seed that is conservatively used to + * avoid otherwise surprising users by hijacking the + * ThreadLocalRandom sequence. The dual use is a marriage of + * convenience, but is a simple and efficient way of reducing + * application-level overhead and footprint of most concurrent + * programs. + * + * Because this class is in a different package than class Thread, + * field access methods must use Unsafe to bypass access control + * rules. The base functionality of Random methods is + * conveniently isolated in method next(bits), that just reads and + * writes the Thread field rather than its own field. However, to + * conform to the requirements of the Random constructor, during + * construction, the common static ThreadLocalRandom must maintain + * initialization and value fields, mainly for the sake of + * disabling user calls to setSeed while still allowing a call + * from constructor. For serialization compatibility, these + * fields are left with the same declarations as used in the + * previous ThreadLocal-based version of this class, that used + * them differently. Note that serialization is completely + * unnecessary because there is only a static singleton. But these + * mechanics still ensure compatibility across versions. + * + * Per-instance initialization is similar to that in the no-arg + * Random constructor, but we avoid correlation among not only + * initial seeds of those created in different threads, but also + * those created using class Random itself; while at the same time + * not changing any statistical properties. So we use the same + * underlying multiplicative sequence, but start the sequence far + * away from the base version, and then merge (xor) current time + * and per-thread probe bits to generate initial values. + * + * The nextLocalGaussian ThreadLocal supports the very rarely used + * nextGaussian method by providing a holder for the second of a + * pair of them. As is true for the base class version of this + * method, this time/space tradeoff is probably never worthwhile, + * but we provide identical statistical properties. + */ + // same constants as Random, but must be redeclared because private private static final long multiplier = 0x5DEECE66DL; private static final long addend = 0xBL; private static final long mask = (1L << 48) - 1; + private static final int PROBE_INCREMENT = 0x61c88647; + + /** Generates the basis for per-thread initial seed values */ + private static final AtomicLong seedGenerator = + new AtomicLong(1269533684904616924L); + + /** Generates per-thread initialization/probe field */ + private static final AtomicInteger probeGenerator = + new AtomicInteger(0xe80f8647); + + /** Rarely-used holder for the second of a pair of Gaussians */ + private static final ThreadLocal nextLocalGaussian = + new ThreadLocal(); + + /* + * Field used only during singleton initialization + */ + boolean initialized; // true when constructor completes + + /** Constructor used only for static singleton */ + private ThreadLocalRandom() { + initialized = true; // false during super() call + } + + /** The common ThreadLocalRandom */ + static final ThreadLocalRandom instance = new ThreadLocalRandom(); /** - * The random seed. We can't use super.seed. + * Initialize Thread fields for the current thread. Called only + * when Thread.threadLocalRandomProbe is zero, indicating that a + * thread local seed value needs to be generated. Note that even + * though the initialization is purely thread-local, we need to + * rely on (static) atomic generators to initialize the values. */ - private long rnd; - - /** - * Initialization flag to permit calls to setSeed to succeed only - * while executing the Random constructor. We can't allow others - * since it would cause setting seed in one part of a program to - * unintentionally impact other usages by the thread. - */ - boolean initialized; - - // Padding to help avoid memory contention among seed updates in - // different TLRs in the common case that they are located near - // each other. - private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7; - - /** - * The actual ThreadLocal - */ - private static final ThreadLocal localRandom = - new ThreadLocal() { - protected ThreadLocalRandom initialValue() { - return new ThreadLocalRandom(); - } - }; - - - /** - * Constructor called only by localRandom.initialValue. - */ - ThreadLocalRandom() { - super(); - initialized = true; + static final void localInit() { + int p = probeGenerator.getAndAdd(PROBE_INCREMENT); + int probe = (p == 0) ? 1 : p; // skip 0 + long current, next; + do { // same sequence as j.u.Random but different initial value + current = seedGenerator.get(); + next = current * 181783497276652981L; + } while (!seedGenerator.compareAndSet(current, next)); + long r = next ^ ((long)probe << 32) ^ System.nanoTime(); + Thread t = Thread.currentThread(); + UNSAFE.putLong(t, SEED, r); + UNSAFE.putInt(t, PROBE, probe); } /** @@ -110,7 +172,9 @@ public class ThreadLocalRandom extends Random { * @return the current thread's {@code ThreadLocalRandom} */ public static ThreadLocalRandom current() { - return localRandom.get(); + if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0) + localInit(); + return instance; } /** @@ -120,14 +184,16 @@ public class ThreadLocalRandom extends Random { * @throws UnsupportedOperationException always */ public void setSeed(long seed) { - if (initialized) + if (initialized) // allow call from super() constructor throw new UnsupportedOperationException(); - rnd = (seed ^ multiplier) & mask; } protected int next(int bits) { - rnd = (rnd * multiplier + addend) & mask; - return (int) (rnd >>> (48-bits)); + Thread t; long r; // read and update per-thread seed + UNSAFE.putLong + (t = Thread.currentThread(), SEED, + r = (UNSAFE.getLong(t, SEED) * multiplier + addend) & mask); + return (int) (r >>> (48-bits)); } /** @@ -222,5 +288,153 @@ public class ThreadLocalRandom extends Random { return nextDouble() * (bound - least) + least; } + public double nextGaussian() { + // Use nextLocalGaussian instead of nextGaussian field + Double d = nextLocalGaussian.get(); + if (d != null) { + nextLocalGaussian.set(null); + return d.doubleValue(); + } + double v1, v2, s; + do { + v1 = 2 * nextDouble() - 1; // between -1 and 1 + v2 = 2 * nextDouble() - 1; // between -1 and 1 + s = v1 * v1 + v2 * v2; + } while (s >= 1 || s == 0); + double multiplier = StrictMath.sqrt(-2 * StrictMath.log(s)/s); + nextLocalGaussian.set(new Double(v2 * multiplier)); + return v1 * multiplier; + } + + // Within-package utilities + + /* + * Descriptions of the usages of the methods below can be found in + * the classes that use them. Briefly, a thread's "probe" value is + * a non-zero hash code that (probably) does not collide with + * other existing threads with respect to any power of two + * collision space. When it does collide, it is pseudo-randomly + * adjusted (using a Marsaglia XorShift). The nextSecondarySeed + * method is used in the same contexts as ThreadLocalRandom, but + * only for transient usages such as random adaptive spin/block + * sequences for which a cheap RNG suffices and for which it could + * in principle disrupt user-visible statistical properties of the + * main ThreadLocalRandom if we were to use it. + * + * Note: Because of package-protection issues, versions of some + * these methods also appear in some subpackage classes. + */ + + /** + * Returns the probe value for the current thread without forcing + * initialization. Note that invoking ThreadLocalRandom.current() + * can be used to force initialization on zero return. + */ + static final int getProbe() { + return UNSAFE.getInt(Thread.currentThread(), PROBE); + } + + /** + * Pseudo-randomly advances and records the given probe value for the + * given thread. + */ + static final int advanceProbe(int probe) { + probe ^= probe << 13; // xorshift + probe ^= probe >>> 17; + probe ^= probe << 5; + UNSAFE.putInt(Thread.currentThread(), PROBE, probe); + return probe; + } + + /** + * Returns the pseudo-randomly initialized or updated secondary seed. + */ + static final int nextSecondarySeed() { + int r; + Thread t = Thread.currentThread(); + if ((r = UNSAFE.getInt(t, SECONDARY)) != 0) { + r ^= r << 13; // xorshift + r ^= r >>> 17; + r ^= r << 5; + } + else if ((r = (int)UNSAFE.getLong(t, SEED)) == 0) + r = 1; // avoid zero + UNSAFE.putInt(t, SECONDARY, r); + return r; + } + + // Serialization support, maintains original persistent form. + private static final long serialVersionUID = -5851777807851030925L; + + /** + * @serialField rnd long + * @serialField initialized boolean + * @serialField pad0 long + * @serialField pad1 long + * @serialField pad2 long + * @serialField pad3 long + * @serialField pad4 long + * @serialField pad5 long + * @serialField pad6 long + * @serialField pad7 long + */ + private static final ObjectStreamField[] serialPersistentFields = { + new ObjectStreamField("rnd", long.class), + new ObjectStreamField("initialized", boolean.class), + new ObjectStreamField("pad0", long.class), + new ObjectStreamField("pad1", long.class), + new ObjectStreamField("pad2", long.class), + new ObjectStreamField("pad3", long.class), + new ObjectStreamField("pad4", long.class), + new ObjectStreamField("pad5", long.class), + new ObjectStreamField("pad6", long.class), + new ObjectStreamField("pad7", long.class) }; + + /** + * Saves the {@code ThreadLocalRandom} to a stream (that is, serializes it). + */ + private void writeObject(java.io.ObjectOutputStream out) + throws java.io.IOException { + + java.io.ObjectOutputStream.PutField fields = out.putFields(); + fields.put("rnd", 0L); + fields.put("initialized", true); + fields.put("pad0", 0L); + fields.put("pad1", 0L); + fields.put("pad2", 0L); + fields.put("pad3", 0L); + fields.put("pad4", 0L); + fields.put("pad5", 0L); + fields.put("pad6", 0L); + fields.put("pad7", 0L); + out.writeFields(); + } + + /** + * Returns the {@link #current() current} thread's {@code ThreadLocalRandom}. + */ + private Object readResolve() { + return current(); + } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long SEED; + private static final long PROBE; + private static final long SECONDARY; + static { + try { + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class tk = Thread.class; + SEED = UNSAFE.objectFieldOffset + (tk.getDeclaredField("threadLocalRandomSeed")); + PROBE = UNSAFE.objectFieldOffset + (tk.getDeclaredField("threadLocalRandomProbe")); + SECONDARY = UNSAFE.objectFieldOffset + (tk.getDeclaredField("threadLocalRandomSecondarySeed")); + } catch (Exception e) { + throw new Error(e); + } + } } From 38f6de7a0c6a19c03261545c3bdd8875632310de Mon Sep 17 00:00:00 2001 From: Bengt Rutisson Date: Wed, 16 Jan 2013 12:46:27 +0100 Subject: [PATCH 049/138] 8006242: G1: WorkerDataArray::verify() too strict for double calculations Also reviewed by vitalyd@gmail.com. Reviewed-by: johnc, mgerdin --- .../gc_implementation/g1/g1GCPhaseTimes.cpp | 34 +++++++++++-------- .../gc_implementation/g1/g1GCPhaseTimes.hpp | 2 ++ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp index 7a102cf43d7..9782b67c007 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.cpp @@ -131,17 +131,23 @@ void WorkerDataArray::print(int level, const char* title) { #ifndef PRODUCT +template <> const int WorkerDataArray::_uninitialized = -1; +template <> const double WorkerDataArray::_uninitialized = -1.0; +template <> const size_t WorkerDataArray::_uninitialized = (size_t)-1; + template void WorkerDataArray::reset() { for (uint i = 0; i < _length; i++) { - _data[i] = (T)-1; + _data[i] = (T)_uninitialized; } } template void WorkerDataArray::verify() { for (uint i = 0; i < _length; i++) { - assert(_data[i] >= (T)0, err_msg("Invalid data for worker %d", i)); + assert(_data[i] != _uninitialized, + err_msg("Invalid data for worker " UINT32_FORMAT ", data: %lf, uninitialized: %lf", + i, (double)_data[i], (double)_uninitialized)); } } @@ -201,20 +207,20 @@ void G1GCPhaseTimes::note_gc_end() { _last_termination_attempts.verify(); _last_gc_worker_end_times_ms.verify(); - for (uint i = 0; i < _active_gc_threads; i++) { - double worker_time = _last_gc_worker_end_times_ms.get(i) - _last_gc_worker_start_times_ms.get(i); - _last_gc_worker_times_ms.set(i, worker_time); + for (uint i = 0; i < _active_gc_threads; i++) { + double worker_time = _last_gc_worker_end_times_ms.get(i) - _last_gc_worker_start_times_ms.get(i); + _last_gc_worker_times_ms.set(i, worker_time); - double worker_known_time = _last_ext_root_scan_times_ms.get(i) + - _last_satb_filtering_times_ms.get(i) + - _last_update_rs_times_ms.get(i) + - _last_scan_rs_times_ms.get(i) + - _last_obj_copy_times_ms.get(i) + - _last_termination_times_ms.get(i); + double worker_known_time = _last_ext_root_scan_times_ms.get(i) + + _last_satb_filtering_times_ms.get(i) + + _last_update_rs_times_ms.get(i) + + _last_scan_rs_times_ms.get(i) + + _last_obj_copy_times_ms.get(i) + + _last_termination_times_ms.get(i); - double worker_other_time = worker_time - worker_known_time; - _last_gc_worker_other_times_ms.set(i, worker_other_time); - } + double worker_other_time = worker_time - worker_known_time; + _last_gc_worker_other_times_ms.set(i, worker_other_time); + } _last_gc_worker_times_ms.verify(); _last_gc_worker_other_times_ms.verify(); diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp index 99e35c63d43..b6e289ed22a 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1GCPhaseTimes.hpp @@ -35,6 +35,8 @@ class WorkerDataArray : public CHeapObj { const char* _print_format; bool _print_sum; + NOT_PRODUCT(static const T _uninitialized;) + // We are caching the sum and average to only have to calculate them once. // This is not done in an MT-safe way. It is intetened to allow single // threaded code to call sum() and average() multiple times in any order From dcc8fbec729b9ce27338c1cc6f1c492471a2fe8d Mon Sep 17 00:00:00 2001 From: Doug Lea Date: Wed, 16 Jan 2013 12:09:35 +0000 Subject: [PATCH 050/138] 8001666: Add lambda-compatible atomics and accumulators to the ActomicXXX classes Co-authored-by: Chris Hegarty Reviewed-by: dl, chegar, darcy, goetz --- .../util/concurrent/atomic/AtomicInteger.java | 88 +++++++++++++++++ .../concurrent/atomic/AtomicIntegerArray.java | 96 +++++++++++++++++++ .../atomic/AtomicIntegerFieldUpdater.java | 92 ++++++++++++++++++ .../util/concurrent/atomic/AtomicLong.java | 88 +++++++++++++++++ .../concurrent/atomic/AtomicLongArray.java | 96 +++++++++++++++++++ .../atomic/AtomicLongFieldUpdater.java | 92 ++++++++++++++++++ .../concurrent/atomic/AtomicReference.java | 88 +++++++++++++++++ .../atomic/AtomicReferenceArray.java | 96 +++++++++++++++++++ .../atomic/AtomicReferenceFieldUpdater.java | 92 ++++++++++++++++++ 9 files changed, 828 insertions(+) diff --git a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicInteger.java b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicInteger.java index f1c76b02bff..b48ef72dfc9 100644 --- a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicInteger.java +++ b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicInteger.java @@ -34,6 +34,8 @@ */ package java.util.concurrent.atomic; +import java.util.function.IntUnaryOperator; +import java.util.function.IntBinaryOperator; import sun.misc.Unsafe; /** @@ -203,6 +205,92 @@ public class AtomicInteger extends Number implements java.io.Serializable { return getAndAdd(delta) + delta; } + /** + * Atomically updates the current value with the results of + * applying the given function, returning the previous value. The + * function should be side-effect-free, since it may be re-applied + * when attempted updates fail due to contention among threads. + * + * @param updateFunction a side-effect-free function + * @return the previous value + * @since 1.8 + */ + public final int getAndUpdate(IntUnaryOperator updateFunction) { + int prev, next; + do { + prev = get(); + next = updateFunction.operateAsInt(prev); + } while (!compareAndSet(prev, next)); + return prev; + } + + /** + * Atomically updates the current value with the results of + * applying the given function, returning the updated value. The + * function should be side-effect-free, since it may be re-applied + * when attempted updates fail due to contention among threads. + * + * @param updateFunction a side-effect-free function + * @return the updated value + * @since 1.8 + */ + public final int updateAndGet(IntUnaryOperator updateFunction) { + int prev, next; + do { + prev = get(); + next = updateFunction.operateAsInt(prev); + } while (!compareAndSet(prev, next)); + return next; + } + + /** + * Atomically updates the current value with the results of + * applying the given function to the current and given values, + * returning the previous value. The function should be + * side-effect-free, since it may be re-applied when attempted + * updates fail due to contention among threads. The function + * is applied with the current value as its first argument, + * and the given update as the second argument. + * + * @param x the update value + * @param accumulatorFunction a side-effect-free function of two arguments + * @return the previous value + * @since 1.8 + */ + public final int getAndAccumulate(int x, + IntBinaryOperator accumulatorFunction) { + int prev, next; + do { + prev = get(); + next = accumulatorFunction.operateAsInt(prev, x); + } while (!compareAndSet(prev, next)); + return prev; + } + + /** + * Atomically updates the current value with the results of + * applying the given function to the current and given values, + * returning the updated value. The function should be + * side-effect-free, since it may be re-applied when attempted + * updates fail due to contention among threads. The function + * is applied with the current value as its first argument, + * and the given update as the second argument. + * + * @param x the update value + * @param accumulatorFunction a side-effect-free function of two arguments + * @return the updated value + * @since 1.8 + */ + public final int accumulateAndGet(int x, + IntBinaryOperator accumulatorFunction) { + int prev, next; + do { + prev = get(); + next = accumulatorFunction.operateAsInt(prev, x); + } while (!compareAndSet(prev, next)); + return next; + } + /** * Returns the String representation of the current value. * @return the String representation of the current value diff --git a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java index b5578ed93d8..6e4d226ba80 100644 --- a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java +++ b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java @@ -34,6 +34,8 @@ */ package java.util.concurrent.atomic; +import java.util.function.IntUnaryOperator; +import java.util.function.IntBinaryOperator; import sun.misc.Unsafe; /** @@ -245,6 +247,100 @@ public class AtomicIntegerArray implements java.io.Serializable { return getAndAdd(i, delta) + delta; } + /** + * Atomically updates the element at index {@code i} with the results + * of applying the given function, returning the previous value. The + * function should be side-effect-free, since it may be re-applied + * when attempted updates fail due to contention among threads. + * + * @param i the index + * @param updateFunction a side-effect-free function + * @return the previous value + * @since 1.8 + */ + public final int getAndUpdate(int i, IntUnaryOperator updateFunction) { + long offset = checkedByteOffset(i); + int prev, next; + do { + prev = getRaw(offset); + next = updateFunction.operateAsInt(prev); + } while (!compareAndSetRaw(offset, prev, next)); + return prev; + } + + /** + * Atomically updates the element at index {@code i} with the results + * of applying the given function, returning the updated value. The + * function should be side-effect-free, since it may be re-applied + * when attempted updates fail due to contention among threads. + * + * @param i the index + * @param updateFunction a side-effect-free function + * @return the updated value + * @since 1.8 + */ + public final int updateAndGet(int i, IntUnaryOperator updateFunction) { + long offset = checkedByteOffset(i); + int prev, next; + do { + prev = getRaw(offset); + next = updateFunction.operateAsInt(prev); + } while (!compareAndSetRaw(offset, prev, next)); + return next; + } + + /** + * Atomically updates the element at index {@code i} with the + * results of applying the given function to the current and + * given values, returning the previous value. The function should + * be side-effect-free, since it may be re-applied when attempted + * updates fail due to contention among threads. The function is + * applied with the current value at index {@code i} as its first + * argument, and the given update as the second argument. + * + * @param i the index + * @param x the update value + * @param accumulatorFunction a side-effect-free function of two arguments + * @return the previous value + * @since 1.8 + */ + public final int getAndAccumulate(int i, int x, + IntBinaryOperator accumulatorFunction) { + long offset = checkedByteOffset(i); + int prev, next; + do { + prev = getRaw(offset); + next = accumulatorFunction.operateAsInt(prev, x); + } while (!compareAndSetRaw(offset, prev, next)); + return prev; + } + + /** + * Atomically updates the element at index {@code i} with the + * results of applying the given function to the current and + * given values, returning the updated value. The function should + * be side-effect-free, since it may be re-applied when attempted + * updates fail due to contention among threads. The function is + * applied with the current value at index {@code i} as its first + * argument, and the given update as the second argument. + * + * @param i the index + * @param x the update value + * @param accumulatorFunction a side-effect-free function of two arguments + * @return the updated value + * @since 1.8 + */ + public final int accumulateAndGet(int i, int x, + IntBinaryOperator accumulatorFunction) { + long offset = checkedByteOffset(i); + int prev, next; + do { + prev = getRaw(offset); + next = accumulatorFunction.operateAsInt(prev, x); + } while (!compareAndSetRaw(offset, prev, next)); + return next; + } + /** * Returns the String representation of the current values of array. * @return the String representation of the current values of array diff --git a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java index e2ae9c4f5dd..fcd5dd05f28 100644 --- a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java +++ b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java @@ -34,6 +34,8 @@ */ package java.util.concurrent.atomic; +import java.util.function.IntUnaryOperator; +import java.util.function.IntBinaryOperator; import sun.misc.Unsafe; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -264,6 +266,96 @@ public abstract class AtomicIntegerFieldUpdater { return next; } + /** + * Atomically updates the field of the given object managed by this updater + * with the results of applying the given function, returning the previous + * value. The function should be side-effect-free, since it may be + * re-applied when attempted updates fail due to contention among threads. + * + * @param obj An object whose field to get and set + * @param updateFunction a side-effect-free function + * @return the previous value + * @since 1.8 + */ + public final int getAndUpdate(T obj, IntUnaryOperator updateFunction) { + int prev, next; + do { + prev = get(obj); + next = updateFunction.operateAsInt(prev); + } while (!compareAndSet(obj, prev, next)); + return prev; + } + + /** + * Atomically updates the field of the given object managed by this updater + * with the results of applying the given function, returning the updated + * value. The function should be side-effect-free, since it may be + * re-applied when attempted updates fail due to contention among threads. + * + * @param obj An object whose field to get and set + * @param updateFunction a side-effect-free function + * @return the updated value + * @since 1.8 + */ + public final int updateAndGet(T obj, IntUnaryOperator updateFunction) { + int prev, next; + do { + prev = get(obj); + next = updateFunction.operateAsInt(prev); + } while (!compareAndSet(obj, prev, next)); + return next; + } + + /** + * Atomically updates the field of the given object managed by this + * updater with the results of applying the given function to the + * current and given values, returning the previous value. The + * function should be side-effect-free, since it may be re-applied + * when attempted updates fail due to contention among threads. The + * function is applied with the current value as its first argument, + * and the given update as the second argument. + * + * @param obj An object whose field to get and set + * @param x the update value + * @param accumulatorFunction a side-effect-free function of two arguments + * @return the previous value + * @since 1.8 + */ + public final int getAndAccumulate(T obj, int x, + IntBinaryOperator accumulatorFunction) { + int prev, next; + do { + prev = get(obj); + next = accumulatorFunction.operateAsInt(prev, x); + } while (!compareAndSet(obj, prev, next)); + return prev; + } + + /** + * Atomically updates the field of the given object managed by this + * updater with the results of applying the given function to the + * current and given values, returning the updated value. The + * function should be side-effect-free, since it may be re-applied + * when attempted updates fail due to contention among threads. The + * function is applied with the current value as its first argument, + * and the given update as the second argument. + * + * @param obj An object whose field to get and set + * @param x the update value + * @param accumulatorFunction a side-effect-free function of two arguments + * @return the updated value + * @since 1.8 + */ + public final int accumulateAndGet(T obj, int x, + IntBinaryOperator accumulatorFunction) { + int prev, next; + do { + prev = get(obj); + next = accumulatorFunction.operateAsInt(prev, x); + } while (!compareAndSet(obj, prev, next)); + return next; + } + /** * Standard hotspot implementation using intrinsics */ diff --git a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicLong.java b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicLong.java index b38cf041fbb..2e58d29b211 100644 --- a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicLong.java +++ b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicLong.java @@ -34,6 +34,8 @@ */ package java.util.concurrent.atomic; +import java.util.function.LongUnaryOperator; +import java.util.function.LongBinaryOperator; import sun.misc.Unsafe; /** @@ -217,6 +219,92 @@ public class AtomicLong extends Number implements java.io.Serializable { return getAndAdd(delta) + delta; } + /** + * Atomically updates the current value with the results of + * applying the given function, returning the previous value. The + * function should be side-effect-free, since it may be re-applied + * when attempted updates fail due to contention among threads. + * + * @param updateFunction a side-effect-free function + * @return the previous value + * @since 1.8 + */ + public final long getAndUpdate(LongUnaryOperator updateFunction) { + long prev, next; + do { + prev = get(); + next = updateFunction.operateAsLong(prev); + } while (!compareAndSet(prev, next)); + return prev; + } + + /** + * Atomically updates the current value with the results of + * applying the given function, returning the updated value. The + * function should be side-effect-free, since it may be re-applied + * when attempted updates fail due to contention among threads. + * + * @param updateFunction a side-effect-free function + * @return the updated value + * @since 1.8 + */ + public final long updateAndGet(LongUnaryOperator updateFunction) { + long prev, next; + do { + prev = get(); + next = updateFunction.operateAsLong(prev); + } while (!compareAndSet(prev, next)); + return next; + } + + /** + * Atomically updates the current value with the results of + * applying the given function to the current and given values, + * returning the previous value. The function should be + * side-effect-free, since it may be re-applied when attempted + * updates fail due to contention among threads. The function + * is applied with the current value as its first argument, + * and the given update as the second argument. + * + * @param x the update value + * @param accumulatorFunction a side-effect-free function of two arguments + * @return the previous value + * @since 1.8 + */ + public final long getAndAccumulate(long x, + LongBinaryOperator accumulatorFunction) { + long prev, next; + do { + prev = get(); + next = accumulatorFunction.operateAsLong(prev, x); + } while (!compareAndSet(prev, next)); + return prev; + } + + /** + * Atomically updates the current value with the results of + * applying the given function to the current and given values, + * returning the updated value. The function should be + * side-effect-free, since it may be re-applied when attempted + * updates fail due to contention among threads. The function + * is applied with the current value as its first argument, + * and the given update as the second argument. + * + * @param x the update value + * @param accumulatorFunction a side-effect-free function of two arguments + * @return the updated value + * @since 1.8 + */ + public final long accumulateAndGet(long x, + LongBinaryOperator accumulatorFunction) { + long prev, next; + do { + prev = get(); + next = accumulatorFunction.operateAsLong(prev, x); + } while (!compareAndSet(prev, next)); + return next; + } + /** * Returns the String representation of the current value. * @return the String representation of the current value diff --git a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicLongArray.java b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicLongArray.java index 9651426c750..2536a9b8e93 100644 --- a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicLongArray.java +++ b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicLongArray.java @@ -34,6 +34,8 @@ */ package java.util.concurrent.atomic; +import java.util.function.LongUnaryOperator; +import java.util.function.LongBinaryOperator; import sun.misc.Unsafe; /** @@ -244,6 +246,100 @@ public class AtomicLongArray implements java.io.Serializable { return getAndAdd(i, delta) + delta; } + /** + * Atomically updates the element at index {@code i} with the results + * of applying the given function, returning the previous value. The + * function should be side-effect-free, since it may be re-applied + * when attempted updates fail due to contention among threads. + * + * @param i the index + * @param updateFunction a side-effect-free function + * @return the previous value + * @since 1.8 + */ + public final long getAndUpdate(int i, LongUnaryOperator updateFunction) { + long offset = checkedByteOffset(i); + long prev, next; + do { + prev = getRaw(offset); + next = updateFunction.operateAsLong(prev); + } while (!compareAndSetRaw(offset, prev, next)); + return prev; + } + + /** + * Atomically updates the element at index {@code i} with the results + * of applying the given function, returning the updated value. The + * function should be side-effect-free, since it may be re-applied + * when attempted updates fail due to contention among threads. + * + * @param i the index + * @param updateFunction a side-effect-free function + * @return the updated value + * @since 1.8 + */ + public final long updateAndGet(int i, LongUnaryOperator updateFunction) { + long offset = checkedByteOffset(i); + long prev, next; + do { + prev = getRaw(offset); + next = updateFunction.operateAsLong(prev); + } while (!compareAndSetRaw(offset, prev, next)); + return next; + } + + /** + * Atomically updates the element at index {@code i} with the + * results of applying the given function to the current and + * given values, returning the previous value. The function should + * be side-effect-free, since it may be re-applied when attempted + * updates fail due to contention among threads. The function is + * applied with the current value at index {@code i} as its first + * argument, and the given update as the second argument. + * + * @param i the index + * @param x the update value + * @param accumulatorFunction a side-effect-free function of two arguments + * @return the previous value + * @since 1.8 + */ + public final long getAndAccumulate(int i, int x, + LongBinaryOperator accumulatorFunction) { + long offset = checkedByteOffset(i); + long prev, next; + do { + prev = getRaw(offset); + next = accumulatorFunction.operateAsLong(prev, x); + } while (!compareAndSetRaw(offset, prev, next)); + return prev; + } + + /** + * Atomically updates the element at index {@code i} with the + * results of applying the given function to the current and + * given values, returning the updated value. The function should + * be side-effect-free, since it may be re-applied when attempted + * updates fail due to contention among threads. The function is + * applied with the current value at index {@code i} as its first + * argument, and the given update as the second argument. + * + * @param i the index + * @param x the update value + * @param accumulatorFunction a side-effect-free function of two arguments + * @return the updated value + * @since 1.8 + */ + public final long accumulateAndGet(int i, int x, + LongBinaryOperator accumulatorFunction) { + long offset = checkedByteOffset(i); + long prev, next; + do { + prev = getRaw(offset); + next = accumulatorFunction.operateAsLong(prev, x); + } while (!compareAndSetRaw(offset, prev, next)); + return next; + } + /** * Returns the String representation of the current values of array. * @return the String representation of the current values of array diff --git a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java index a013680ce7a..a7672263a77 100644 --- a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java +++ b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java @@ -34,6 +34,8 @@ */ package java.util.concurrent.atomic; +import java.util.function.LongUnaryOperator; +import java.util.function.LongBinaryOperator; import sun.misc.Unsafe; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -267,6 +269,96 @@ public abstract class AtomicLongFieldUpdater { return next; } + /** + * Atomically updates the field of the given object managed by this updater + * with the results of applying the given function, returning the previous + * value. The function should be side-effect-free, since it may be + * re-applied when attempted updates fail due to contention among threads. + * + * @param obj An object whose field to get and set + * @param updateFunction a side-effect-free function + * @return the previous value + * @since 1.8 + */ + public final long getAndUpdate(T obj, LongUnaryOperator updateFunction) { + long prev, next; + do { + prev = get(obj); + next = updateFunction.operateAsLong(prev); + } while (!compareAndSet(obj, prev, next)); + return prev; + } + + /** + * Atomically updates the field of the given object managed by this updater + * with the results of applying the given function, returning the updated + * value. The function should be side-effect-free, since it may be + * re-applied when attempted updates fail due to contention among threads. + * + * @param obj An object whose field to get and set + * @param updateFunction a side-effect-free function + * @return the updated value + * @since 1.8 + */ + public final long updateAndGet(T obj, LongUnaryOperator updateFunction) { + long prev, next; + do { + prev = get(obj); + next = updateFunction.operateAsLong(prev); + } while (!compareAndSet(obj, prev, next)); + return next; + } + + /** + * Atomically updates the field of the given object managed by this + * updater with the results of applying the given function to the + * current and given values, returning the previous value. The + * function should be side-effect-free, since it may be re-applied + * when attempted updates fail due to contention among threads. The + * function is applied with the current value as its first argument, + * and the given update as the second argument. + * + * @param obj An object whose field to get and set + * @param x the update value + * @param accumulatorFunction a side-effect-free function of two arguments + * @return the previous value + * @since 1.8 + */ + public final long getAndAccumulate(T obj, long x, + LongBinaryOperator accumulatorFunction) { + long prev, next; + do { + prev = get(obj); + next = accumulatorFunction.operateAsLong(prev, x); + } while (!compareAndSet(obj, prev, next)); + return prev; + } + + /** + * Atomically updates the field of the given object managed by this + * updater with the results of applying the given function to the + * current and given values, returning the updated value. The + * function should be side-effect-free, since it may be re-applied + * when attempted updates fail due to contention among threads. The + * function is applied with the current value as its first argument, + * and the given update as the second argument. + * + * @param obj An object whose field to get and set + * @param x the update value + * @param accumulatorFunction a side-effect-free function of two arguments + * @return the updated value + * @since 1.8 + */ + public final long accumulateAndGet(T obj, long x, + LongBinaryOperator accumulatorFunction) { + long prev, next; + do { + prev = get(obj); + next = accumulatorFunction.operateAsLong(prev, x); + } while (!compareAndSet(obj, prev, next)); + return next; + } + private static class CASUpdater extends AtomicLongFieldUpdater { private static final Unsafe unsafe = Unsafe.getUnsafe(); private final long offset; diff --git a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicReference.java b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicReference.java index 94c9edca52a..95b88d127e2 100644 --- a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicReference.java +++ b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicReference.java @@ -34,6 +34,8 @@ */ package java.util.concurrent.atomic; +import java.util.function.UnaryOperator; +import java.util.function.BinaryOperator; import sun.misc.Unsafe; /** @@ -141,6 +143,92 @@ public class AtomicReference implements java.io.Serializable { return (V)unsafe.getAndSetObject(this, valueOffset, newValue); } + /** + * Atomically updates the current value with the results of + * applying the given function, returning the previous value. The + * function should be side-effect-free, since it may be re-applied + * when attempted updates fail due to contention among threads. + * + * @param updateFunction a side-effect-free function + * @return the previous value + * @since 1.8 + */ + public final V getAndUpdate(UnaryOperator updateFunction) { + V prev, next; + do { + prev = get(); + next = updateFunction.operate(prev); + } while (!compareAndSet(prev, next)); + return prev; + } + + /** + * Atomically updates the current value with the results of + * applying the given function, returning the updated value. The + * function should be side-effect-free, since it may be re-applied + * when attempted updates fail due to contention among threads. + * + * @param updateFunction a side-effect-free function + * @return the updated value + * @since 1.8 + */ + public final V updateAndGet(UnaryOperator updateFunction) { + V prev, next; + do { + prev = get(); + next = updateFunction.operate(prev); + } while (!compareAndSet(prev, next)); + return next; + } + + /** + * Atomically updates the current value with the results of + * applying the given function to the current and given values, + * returning the previous value. The function should be + * side-effect-free, since it may be re-applied when attempted + * updates fail due to contention among threads. The function + * is applied with the current value as its first argument, + * and the given update as the second argument. + * + * @param x the update value + * @param accumulatorFunction a side-effect-free function of two arguments + * @return the previous value + * @since 1.8 + */ + public final V getAndAccumulate(V x, + BinaryOperator accumulatorFunction) { + V prev, next; + do { + prev = get(); + next = accumulatorFunction.operate(prev, x); + } while (!compareAndSet(prev, next)); + return prev; + } + + /** + * Atomically updates the current value with the results of + * applying the given function to the current and given values, + * returning the updated value. The function should be + * side-effect-free, since it may be re-applied when attempted + * updates fail due to contention among threads. The function + * is applied with the current value as its first argument, + * and the given update as the second argument. + * + * @param x the update value + * @param accumulatorFunction a side-effect-free function of two arguments + * @return the updated value + * @since 1.8 + */ + public final V accumulateAndGet(V x, + BinaryOperator accumulatorFunction) { + V prev, next; + do { + prev = get(); + next = accumulatorFunction.operate(prev, x); + } while (!compareAndSet(prev, next)); + return next; + } + /** * Returns the String representation of the current value. * @return the String representation of the current value diff --git a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java index 71308618541..3e6ce969847 100644 --- a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java +++ b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java @@ -36,6 +36,8 @@ package java.util.concurrent.atomic; import java.util.Arrays; +import java.util.function.UnaryOperator; +import java.util.function.BinaryOperator; import java.lang.reflect.Array; import sun.misc.Unsafe; @@ -199,6 +201,100 @@ public class AtomicReferenceArray implements java.io.Serializable { return compareAndSet(i, expect, update); } + /** + * Atomically updates the element at index {@code i} with the results + * of applying the given function, returning the previous value. The + * function should be side-effect-free, since it may be re-applied + * when attempted updates fail due to contention among threads. + * + * @param i the index + * @param updateFunction a side-effect-free function + * @return the previous value + * @since 1.8 + */ + public final E getAndUpdate(int i, UnaryOperator updateFunction) { + long offset = checkedByteOffset(i); + E prev, next; + do { + prev = getRaw(offset); + next = updateFunction.operate(prev); + } while (!compareAndSetRaw(offset, prev, next)); + return prev; + } + + /** + * Atomically updates the element at index {@code i} with the results + * of applying the given function, returning the updated value. The + * function should be side-effect-free, since it may be re-applied + * when attempted updates fail due to contention among threads. + * + * @param i the index + * @param updateFunction a side-effect-free function + * @return the updated value + * @since 1.8 + */ + public final E updateAndGet(int i, UnaryOperator updateFunction) { + long offset = checkedByteOffset(i); + E prev, next; + do { + prev = getRaw(offset); + next = updateFunction.operate(prev); + } while (!compareAndSetRaw(offset, prev, next)); + return next; + } + + /** + * Atomically updates the element at index {@code i} with the + * results of applying the given function to the current and + * given values, returning the previous value. The function should + * be side-effect-free, since it may be re-applied when attempted + * updates fail due to contention among threads. The function is + * applied with the current value at index {@code i} as its first + * argument, and the given update as the second argument. + * + * @param i the index + * @param x the update value + * @param accumulatorFunction a side-effect-free function of two arguments + * @return the previous value + * @since 1.8 + */ + public final E getAndAccumulate(int i, E x, + BinaryOperator accumulatorFunction) { + long offset = checkedByteOffset(i); + E prev, next; + do { + prev = getRaw(offset); + next = accumulatorFunction.operate(prev, x); + } while (!compareAndSetRaw(offset, prev, next)); + return prev; + } + + /** + * Atomically updates the element at index {@code i} with the + * results of applying the given function to the current and + * given values, returning the updated value. The function should + * be side-effect-free, since it may be re-applied when attempted + * updates fail due to contention among threads. The function is + * applied with the current value at index {@code i} as its first + * argument, and the given update as the second argument. + * + * @param i the index + * @param x the update value + * @param accumulatorFunction a side-effect-free function of two arguments + * @return the updated value + * @since 1.8 + */ + public final E accumulateAndGet(int i, E x, + BinaryOperator accumulatorFunction) { + long offset = checkedByteOffset(i); + E prev, next; + do { + prev = getRaw(offset); + next = accumulatorFunction.operate(prev, x); + } while (!compareAndSetRaw(offset, prev, next)); + return next; + } + /** * Returns the String representation of the current values of array. * @return the String representation of the current values of array diff --git a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java index 78c636956f6..a92d914ff77 100644 --- a/jdk/src/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java +++ b/jdk/src/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java @@ -34,6 +34,8 @@ */ package java.util.concurrent.atomic; +import java.util.function.UnaryOperator; +import java.util.function.BinaryOperator; import sun.misc.Unsafe; import java.lang.reflect.Field; import java.lang.reflect.Modifier; @@ -183,6 +185,96 @@ public abstract class AtomicReferenceFieldUpdater { return prev; } + /** + * Atomically updates the field of the given object managed by this updater + * with the results of applying the given function, returning the previous + * value. The function should be side-effect-free, since it may be + * re-applied when attempted updates fail due to contention among threads. + * + * @param obj An object whose field to get and set + * @param updateFunction a side-effect-free function + * @return the previous value + * @since 1.8 + */ + public final V getAndUpdate(T obj, UnaryOperator updateFunction) { + V prev, next; + do { + prev = get(obj); + next = updateFunction.operate(prev); + } while (!compareAndSet(obj, prev, next)); + return prev; + } + + /** + * Atomically updates the field of the given object managed by this updater + * with the results of applying the given function, returning the updated + * value. The function should be side-effect-free, since it may be + * re-applied when attempted updates fail due to contention among threads. + * + * @param obj An object whose field to get and set + * @param updateFunction a side-effect-free function + * @return the updated value + * @since 1.8 + */ + public final V updateAndGet(T obj, UnaryOperator updateFunction) { + V prev, next; + do { + prev = get(obj); + next = updateFunction.operate(prev); + } while (!compareAndSet(obj, prev, next)); + return next; + } + + /** + * Atomically updates the field of the given object managed by this + * updater with the results of applying the given function to the + * current and given values, returning the previous value. The + * function should be side-effect-free, since it may be re-applied + * when attempted updates fail due to contention among threads. The + * function is applied with the current value as its first argument, + * and the given update as the second argument. + * + * @param obj An object whose field to get and set + * @param x the update value + * @param accumulatorFunction a side-effect-free function of two arguments + * @return the previous value + * @since 1.8 + */ + public final V getAndAccumulate(T obj, V x, + BinaryOperator accumulatorFunction) { + V prev, next; + do { + prev = get(obj); + next = accumulatorFunction.operate(prev, x); + } while (!compareAndSet(obj, prev, next)); + return prev; + } + + /** + * Atomically updates the field of the given object managed by this + * updater with the results of applying the given function to the + * current and given values, returning the updated value. The + * function should be side-effect-free, since it may be re-applied + * when attempted updates fail due to contention among threads. The + * function is applied with the current value as its first argument, + * and the given update as the second argument. + * + * @param obj An object whose field to get and set + * @param x the update value + * @param accumulatorFunction a side-effect-free function of two arguments + * @return the updated value + * @since 1.8 + */ + public final V accumulateAndGet(T obj, V x, + BinaryOperator accumulatorFunction) { + V prev, next; + do { + prev = get(obj); + next = accumulatorFunction.operate(prev, x); + } while (!compareAndSet(obj, prev, next)); + return next; + } + private static final class AtomicReferenceFieldUpdaterImpl extends AtomicReferenceFieldUpdater { private static final Unsafe unsafe = Unsafe.getUnsafe(); From 9a4e15eaf6278b1659c51c9277948fbb9da2efbb Mon Sep 17 00:00:00 2001 From: Mikhail Cherkasov Date: Wed, 16 Jan 2013 17:26:41 +0400 Subject: [PATCH 051/138] 8005492: Reduce number of warnings in sun/awt/* classes Reviewed-by: art, anthony --- jdk/src/share/classes/java/awt/Button.java | 2 +- jdk/src/share/classes/java/awt/Checkbox.java | 2 +- jdk/src/share/classes/java/awt/Choice.java | 8 +++---- jdk/src/share/classes/java/awt/Component.java | 1 + jdk/src/share/classes/java/awt/Container.java | 22 ++++++++--------- jdk/src/share/classes/java/awt/Dialog.java | 12 +++++----- jdk/src/share/classes/java/awt/Frame.java | 4 ++-- .../java/awt/KeyboardFocusManager.java | 2 +- jdk/src/share/classes/java/awt/Scrollbar.java | 2 +- jdk/src/share/classes/java/awt/TextArea.java | 6 ++--- .../share/classes/java/awt/TextComponent.java | 2 +- jdk/src/share/classes/java/awt/TextField.java | 2 +- jdk/src/share/classes/java/awt/Toolkit.java | 2 +- jdk/src/share/classes/java/awt/Window.java | 24 +++++++++++-------- .../classes/sun/awt/image/SurfaceManager.java | 6 ++--- 15 files changed, 51 insertions(+), 46 deletions(-) diff --git a/jdk/src/share/classes/java/awt/Button.java b/jdk/src/share/classes/java/awt/Button.java index f0bad87430b..9fe42d41cbc 100644 --- a/jdk/src/share/classes/java/awt/Button.java +++ b/jdk/src/share/classes/java/awt/Button.java @@ -300,7 +300,7 @@ public class Button extends Component implements Accessible { * @since 1.4 */ public synchronized ActionListener[] getActionListeners() { - return (ActionListener[]) (getListeners(ActionListener.class)); + return getListeners(ActionListener.class); } /** diff --git a/jdk/src/share/classes/java/awt/Checkbox.java b/jdk/src/share/classes/java/awt/Checkbox.java index 5c8e65f8aac..f0486f35e45 100644 --- a/jdk/src/share/classes/java/awt/Checkbox.java +++ b/jdk/src/share/classes/java/awt/Checkbox.java @@ -470,7 +470,7 @@ public class Checkbox extends Component implements ItemSelectable, Accessible { * @since 1.4 */ public synchronized ItemListener[] getItemListeners() { - return (ItemListener[]) (getListeners(ItemListener.class)); + return getListeners(ItemListener.class); } /** diff --git a/jdk/src/share/classes/java/awt/Choice.java b/jdk/src/share/classes/java/awt/Choice.java index 895be89a615..9ef765818dc 100644 --- a/jdk/src/share/classes/java/awt/Choice.java +++ b/jdk/src/share/classes/java/awt/Choice.java @@ -85,7 +85,7 @@ public class Choice extends Component implements ItemSelectable, Accessible { * @see #insert(String, int) * @see #remove(String) */ - Vector pItems; + Vector pItems; /** * The index of the current choice for this Choice @@ -129,7 +129,7 @@ public class Choice extends Component implements ItemSelectable, Accessible { */ public Choice() throws HeadlessException { GraphicsEnvironment.checkHeadless(); - pItems = new Vector(); + pItems = new Vector<>(); } /** @@ -191,7 +191,7 @@ public class Choice extends Component implements ItemSelectable, Accessible { * be called on the toolkit thread. */ final String getItemImpl(int index) { - return (String)pItems.elementAt(index); + return pItems.elementAt(index); } /** @@ -524,7 +524,7 @@ public class Choice extends Component implements ItemSelectable, Accessible { * @since 1.4 */ public synchronized ItemListener[] getItemListeners() { - return (ItemListener[])(getListeners(ItemListener.class)); + return getListeners(ItemListener.class); } /** diff --git a/jdk/src/share/classes/java/awt/Component.java b/jdk/src/share/classes/java/awt/Component.java index e4f25f0da2b..42e422d3e93 100644 --- a/jdk/src/share/classes/java/awt/Component.java +++ b/jdk/src/share/classes/java/awt/Component.java @@ -7287,6 +7287,7 @@ public abstract class Component implements ImageObserver, MenuContainer, } final Set getFocusTraversalKeys_NoIDCheck(int id) { // Okay to return Set directly because it is an unmodifiable view + @SuppressWarnings("unchecked") Set keystrokes = (focusTraversalKeys != null) ? focusTraversalKeys[id] : null; diff --git a/jdk/src/share/classes/java/awt/Container.java b/jdk/src/share/classes/java/awt/Container.java index ce2a19138b1..78af6b10c62 100644 --- a/jdk/src/share/classes/java/awt/Container.java +++ b/jdk/src/share/classes/java/awt/Container.java @@ -161,7 +161,7 @@ public class Container extends Component { private boolean focusTraversalPolicyProvider; // keeps track of the threads that are printing this component - private transient Set printingThreads; + private transient Set printingThreads; // True if there is at least one thread that's printing this component private transient boolean printing = false; @@ -275,7 +275,7 @@ public class Container extends Component { */ public Container() { } - + @SuppressWarnings({"unchecked","rawtypes"}) void initializeFocusTraversalKeys() { focusTraversalKeys = new Set[4]; } @@ -2006,7 +2006,7 @@ public class Container extends Component { try { synchronized (getObjectLock()) { if (printingThreads == null) { - printingThreads = new HashSet(); + printingThreads = new HashSet<>(); } printingThreads.add(t); printing = true; @@ -2148,7 +2148,7 @@ public class Container extends Component { * @since 1.4 */ public synchronized ContainerListener[] getContainerListeners() { - return (ContainerListener[]) (getListeners(ContainerListener.class)); + return getListeners(ContainerListener.class); } /** @@ -2599,9 +2599,9 @@ public class Container extends Component { if (GraphicsEnvironment.isHeadless()) { throw new HeadlessException(); } - PointerInfo pi = (PointerInfo)java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction() { - public Object run() { + PointerInfo pi = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public PointerInfo run() { return MouseInfo.getPointerInfo(); } } @@ -2682,7 +2682,7 @@ public class Container extends Component { y - comp.y, ignoreEnabled); } else { - comp = comp.locate(x - comp.x, y - comp.y); + comp = comp.getComponentAt(x - comp.x, y - comp.y); } if (comp != null && comp.visible && (ignoreEnabled || comp.enabled)) @@ -2700,7 +2700,7 @@ public class Container extends Component { y - comp.y, ignoreEnabled); } else { - comp = comp.locate(x - comp.x, y - comp.y); + comp = comp.getComponentAt(x - comp.x, y - comp.y); } if (comp != null && comp.visible && (ignoreEnabled || comp.enabled)) @@ -4637,7 +4637,7 @@ class LightweightDispatcher implements java.io.Serializable, AWTEventListener { private void startListeningForOtherDrags() { //System.out.println("Adding AWTEventListener"); java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction() { + new java.security.PrivilegedAction() { public Object run() { nativeContainer.getToolkit().addAWTEventListener( LightweightDispatcher.this, @@ -4652,7 +4652,7 @@ class LightweightDispatcher implements java.io.Serializable, AWTEventListener { private void stopListeningForOtherDrags() { //System.out.println("Removing AWTEventListener"); java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction() { + new java.security.PrivilegedAction() { public Object run() { nativeContainer.getToolkit().removeAWTEventListener(LightweightDispatcher.this); return null; diff --git a/jdk/src/share/classes/java/awt/Dialog.java b/jdk/src/share/classes/java/awt/Dialog.java index 800d19c0200..622a4531d3a 100644 --- a/jdk/src/share/classes/java/awt/Dialog.java +++ b/jdk/src/share/classes/java/awt/Dialog.java @@ -1047,9 +1047,9 @@ public class Dialog extends Window { // if this dialog is toolkit-modal, the filter should be added // to all EDTs (for all AppContexts) if (modalityType == ModalityType.TOOLKIT_MODAL) { - Iterator it = AppContext.getAppContexts().iterator(); + Iterator it = AppContext.getAppContexts().iterator(); while (it.hasNext()) { - AppContext appContext = (AppContext)it.next(); + AppContext appContext = it.next(); if (appContext == showAppContext) { continue; } @@ -1084,9 +1084,9 @@ public class Dialog extends Window { // if this dialog is toolkit-modal, its filter must be removed // from all EDTs (for all AppContexts) if (modalityType == ModalityType.TOOLKIT_MODAL) { - Iterator it = AppContext.getAppContexts().iterator(); + Iterator it = AppContext.getAppContexts().iterator(); while (it.hasNext()) { - AppContext appContext = (AppContext)it.next(); + AppContext appContext = it.next(); if (appContext == showAppContext) { continue; } @@ -1396,7 +1396,7 @@ public class Dialog extends Window { if (d.shouldBlock(this)) { Window w = d; while ((w != null) && (w != this)) { - w = (Window)(w.getOwner_NoClientCode()); + w = w.getOwner_NoClientCode(); } if ((w == this) || !shouldBlock(d) || (modalityType.compareTo(d.getModalityType()) < 0)) { blockers.add(d); @@ -1611,7 +1611,7 @@ public class Dialog extends Window { setModal(modal); } - blockedWindows = new IdentityArrayList(); + blockedWindows = new IdentityArrayList<>(); } /* diff --git a/jdk/src/share/classes/java/awt/Frame.java b/jdk/src/share/classes/java/awt/Frame.java index 1a3173730e5..2513ddb75ef 100644 --- a/jdk/src/share/classes/java/awt/Frame.java +++ b/jdk/src/share/classes/java/awt/Frame.java @@ -353,7 +353,7 @@ public class Frame extends Window implements MenuContainer { * @serial * @see java.awt.Window#ownedWindowList */ - Vector ownedWindows; + Vector ownedWindows; private static final String base = "frame"; private static int nameCounter = 0; @@ -1242,7 +1242,7 @@ public class Frame extends Window implements MenuContainer { // if (ownedWindows != null) { for (int i = 0; i < ownedWindows.size(); i++) { - connectOwnedWindow((Window) ownedWindows.elementAt(i)); + connectOwnedWindow(ownedWindows.elementAt(i)); } ownedWindows = null; } diff --git a/jdk/src/share/classes/java/awt/KeyboardFocusManager.java b/jdk/src/share/classes/java/awt/KeyboardFocusManager.java index 36a5b9b7fae..af9dcb1b2c2 100644 --- a/jdk/src/share/classes/java/awt/KeyboardFocusManager.java +++ b/jdk/src/share/classes/java/awt/KeyboardFocusManager.java @@ -416,7 +416,7 @@ public abstract class KeyboardFocusManager } } - static Set initFocusTraversalKeysSet(String value, Set targetSet) { + static Set initFocusTraversalKeysSet(String value, Set targetSet) { StringTokenizer tokens = new StringTokenizer(value, ","); while (tokens.hasMoreTokens()) { targetSet.add(AWTKeyStroke.getAWTKeyStroke(tokens.nextToken())); diff --git a/jdk/src/share/classes/java/awt/Scrollbar.java b/jdk/src/share/classes/java/awt/Scrollbar.java index fb033305e51..b6a581d96a8 100644 --- a/jdk/src/share/classes/java/awt/Scrollbar.java +++ b/jdk/src/share/classes/java/awt/Scrollbar.java @@ -1012,7 +1012,7 @@ public class Scrollbar extends Component implements Adjustable, Accessible { * @since 1.4 */ public synchronized AdjustmentListener[] getAdjustmentListeners() { - return (AdjustmentListener[])(getListeners(AdjustmentListener.class)); + return getListeners(AdjustmentListener.class); } /** diff --git a/jdk/src/share/classes/java/awt/TextArea.java b/jdk/src/share/classes/java/awt/TextArea.java index af76af8cbfa..8b16d9ec61d 100644 --- a/jdk/src/share/classes/java/awt/TextArea.java +++ b/jdk/src/share/classes/java/awt/TextArea.java @@ -123,7 +123,7 @@ public class TextArea extends TextComponent { * Cache the Sets of forward and backward traversal keys so we need not * look them up each time. */ - private static Set forwardTraversalKeys, backwardTraversalKeys; + private static Set forwardTraversalKeys, backwardTraversalKeys; /* * JDK 1.1 serialVersionUID @@ -143,10 +143,10 @@ public class TextArea extends TextComponent { } forwardTraversalKeys = KeyboardFocusManager.initFocusTraversalKeysSet( "ctrl TAB", - new HashSet()); + new HashSet()); backwardTraversalKeys = KeyboardFocusManager.initFocusTraversalKeysSet( "ctrl shift TAB", - new HashSet()); + new HashSet()); } /** diff --git a/jdk/src/share/classes/java/awt/TextComponent.java b/jdk/src/share/classes/java/awt/TextComponent.java index c99cae403a4..ecc9b3fd47c 100644 --- a/jdk/src/share/classes/java/awt/TextComponent.java +++ b/jdk/src/share/classes/java/awt/TextComponent.java @@ -606,7 +606,7 @@ public class TextComponent extends Component implements Accessible { * @since 1.4 */ public synchronized TextListener[] getTextListeners() { - return (TextListener[])(getListeners(TextListener.class)); + return getListeners(TextListener.class); } /** diff --git a/jdk/src/share/classes/java/awt/TextField.java b/jdk/src/share/classes/java/awt/TextField.java index 268661c15f3..7c0e528f5f7 100644 --- a/jdk/src/share/classes/java/awt/TextField.java +++ b/jdk/src/share/classes/java/awt/TextField.java @@ -507,7 +507,7 @@ public class TextField extends TextComponent { * @since 1.4 */ public synchronized ActionListener[] getActionListeners() { - return (ActionListener[])(getListeners(ActionListener.class)); + return getListeners(ActionListener.class); } /** diff --git a/jdk/src/share/classes/java/awt/Toolkit.java b/jdk/src/share/classes/java/awt/Toolkit.java index 8509534734c..783706a72b4 100644 --- a/jdk/src/share/classes/java/awt/Toolkit.java +++ b/jdk/src/share/classes/java/awt/Toolkit.java @@ -863,7 +863,7 @@ public abstract class Toolkit { new java.security.PrivilegedAction() { public Void run() { String nm = null; - Class cls = null; + Class cls = null; try { nm = System.getProperty("awt.toolkit"); try { diff --git a/jdk/src/share/classes/java/awt/Window.java b/jdk/src/share/classes/java/awt/Window.java index 984e287bd50..4b9765443eb 100644 --- a/jdk/src/share/classes/java/awt/Window.java +++ b/jdk/src/share/classes/java/awt/Window.java @@ -441,7 +441,7 @@ public class Window extends Container implements Accessible { transient Object anchor = new Object(); static class WindowDisposerRecord implements sun.java2d.DisposerRecord { final WeakReference owner; - final WeakReference weakThis; + final WeakReference weakThis; final WeakReference context; WindowDisposerRecord(AppContext context, Window victim) { owner = new WeakReference(victim.getOwner()); @@ -1542,6 +1542,7 @@ public class Window extends Container implements Accessible { private static Window[] getWindows(AppContext appContext) { synchronized (Window.class) { Window realCopy[]; + @SuppressWarnings("unchecked") Vector> windowList = (Vector>)appContext.get(Window.class); if (windowList != null) { @@ -1866,7 +1867,7 @@ public class Window extends Container implements Accessible { * @since 1.4 */ public synchronized WindowListener[] getWindowListeners() { - return (WindowListener[])(getListeners(WindowListener.class)); + return getListeners(WindowListener.class); } /** @@ -1882,7 +1883,7 @@ public class Window extends Container implements Accessible { * @since 1.4 */ public synchronized WindowFocusListener[] getWindowFocusListeners() { - return (WindowFocusListener[])(getListeners(WindowFocusListener.class)); + return getListeners(WindowFocusListener.class); } /** @@ -1898,7 +1899,7 @@ public class Window extends Container implements Accessible { * @since 1.4 */ public synchronized WindowStateListener[] getWindowStateListeners() { - return (WindowStateListener[])(getListeners(WindowStateListener.class)); + return getListeners(WindowStateListener.class); } @@ -2014,7 +2015,6 @@ public class Window extends Container implements Accessible { break; case WindowEvent.WINDOW_STATE_CHANGED: processWindowStateEvent((WindowEvent)e); - default: break; } return; @@ -2382,12 +2382,14 @@ public class Window extends Container implements Accessible { * KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS * @since 1.4 */ + @SuppressWarnings("unchecked") public Set getFocusTraversalKeys(int id) { if (id < 0 || id >= KeyboardFocusManager.TRAVERSAL_KEY_LENGTH) { throw new IllegalArgumentException("invalid focus traversal key identifier"); } // Okay to return Set directly because it is an unmodifiable view + @SuppressWarnings("rawtypes") Set keystrokes = (focusTraversalKeys != null) ? focusTraversalKeys[id] : null; @@ -2765,7 +2767,7 @@ public class Window extends Container implements Accessible { /* * Support for tracking all windows owned by this window */ - void addOwnedWindow(WeakReference weakWindow) { + void addOwnedWindow(WeakReference weakWindow) { if (weakWindow != null) { synchronized(ownedWindowList) { // this if statement should really be an assert, but we don't @@ -2777,7 +2779,7 @@ public class Window extends Container implements Accessible { } } - void removeOwnedWindow(WeakReference weakWindow) { + void removeOwnedWindow(WeakReference weakWindow) { if (weakWindow != null) { // synchronized block not required since removeElement is // already synchronized @@ -2792,6 +2794,7 @@ public class Window extends Container implements Accessible { private void addToWindowList() { synchronized (Window.class) { + @SuppressWarnings("unchecked") Vector> windowList = (Vector>)appContext.get(Window.class); if (windowList == null) { windowList = new Vector>(); @@ -2801,8 +2804,9 @@ public class Window extends Container implements Accessible { } } - private static void removeFromWindowList(AppContext context, WeakReference weakThis) { + private static void removeFromWindowList(AppContext context, WeakReference weakThis) { synchronized (Window.class) { + @SuppressWarnings("unchecked") Vector> windowList = (Vector>)context.get(Window.class); if (windowList != null) { windowList.remove(weakThis); @@ -2945,7 +2949,7 @@ public class Window extends Container implements Accessible { // Deserialized Windows are not yet visible. visible = false; - weakThis = new WeakReference(this); + weakThis = new WeakReference<>(this); anchor = new Object(); sun.java2d.Disposer.addRecord(anchor, new WindowDisposerRecord(appContext, this)); @@ -2956,7 +2960,7 @@ public class Window extends Container implements Accessible { private void deserializeResources(ObjectInputStream s) throws ClassNotFoundException, IOException, HeadlessException { - ownedWindowList = new Vector(); + ownedWindowList = new Vector<>(); if (windowSerializedDataVersion < 2) { // Translate old-style focus tracking to new model. For 1.4 and diff --git a/jdk/src/share/classes/sun/awt/image/SurfaceManager.java b/jdk/src/share/classes/sun/awt/image/SurfaceManager.java index 9451f91e323..f10673a9423 100644 --- a/jdk/src/share/classes/sun/awt/image/SurfaceManager.java +++ b/jdk/src/share/classes/sun/awt/image/SurfaceManager.java @@ -88,7 +88,7 @@ public abstract class SurfaceManager { imgaccessor.setSurfaceManager(img, mgr); } - private ConcurrentHashMap cacheMap; + private ConcurrentHashMap cacheMap; /** * Return an arbitrary cached object for an arbitrary cache key. @@ -123,7 +123,7 @@ public abstract class SurfaceManager { if (cacheMap == null) { synchronized (this) { if (cacheMap == null) { - cacheMap = new ConcurrentHashMap(2); + cacheMap = new ConcurrentHashMap<>(2); } } } @@ -245,7 +245,7 @@ public abstract class SurfaceManager { synchronized void flush(boolean deaccelerate) { if (cacheMap != null) { - Iterator i = cacheMap.values().iterator(); + Iterator i = cacheMap.values().iterator(); while (i.hasNext()) { Object o = i.next(); if (o instanceof FlushableCacheData) { From 68eb431db8ff569cbfad651e4c419474ce782be5 Mon Sep 17 00:00:00 2001 From: Jason Uh Date: Wed, 16 Jan 2013 09:51:21 -0500 Subject: [PATCH 052/138] 8005389: Backout fix for JDK-6500133 Reviewed-by: mullan --- .../classes/sun/security/x509/URIName.java | 9 +-- jdk/test/sun/security/x509/URIName/Parse.java | 59 ++++++++++++------- 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/jdk/src/share/classes/sun/security/x509/URIName.java b/jdk/src/share/classes/sun/security/x509/URIName.java index 9345640ef57..d7dbea95843 100644 --- a/jdk/src/share/classes/sun/security/x509/URIName.java +++ b/jdk/src/share/classes/sun/security/x509/URIName.java @@ -30,7 +30,6 @@ import java.net.URI; import java.net.URISyntaxException; import sun.security.util.*; -import sun.net.www.ParseUtil; /** * This class implements the URIName as required by the GeneralNames @@ -107,13 +106,7 @@ public class URIName implements GeneralNameInterface { try { uri = new URI(name); } catch (URISyntaxException use) { - try { - // Try parsing the URI again after encoding/escaping - // any illegal characters - uri = new URI(ParseUtil.encodePath(name)); - } catch (URISyntaxException use2) { - throw new IOException("invalid URI name:" + name, use2); - } + throw new IOException("invalid URI name:" + name, use); } if (uri.getScheme() == null) { throw new IOException("URI name must include scheme:" + name); diff --git a/jdk/test/sun/security/x509/URIName/Parse.java b/jdk/test/sun/security/x509/URIName/Parse.java index d99aa30ac1f..c53450840a6 100644 --- a/jdk/test/sun/security/x509/URIName/Parse.java +++ b/jdk/test/sun/security/x509/URIName/Parse.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,11 +23,12 @@ /* * @test - * @bug 6500133 - * @summary CRL Distribution Point URIs with spaces or backslashes should be - * parseable + * @bug 8005389 + * @summary CRL Distribution Point URIs with spaces or backslashes should + * not be parseable */ import java.io.ByteArrayInputStream; +import java.io.IOException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import sun.security.util.DerValue; @@ -90,27 +91,45 @@ public class Parse { } public static void main(String[] args) throws Exception { - /* Parse a CRLDistributionPointsExtension URI with a space. */ - CRLDistributionPointsExtensionTest(certWithSpaceInCDPStr); - System.out.println("Parsed CRLDistributionPointsExtension uri with " - + "a space."); + /* Try to parse a CRLDistributionPointsExtension URI with a space. */ + try { + CRLDistributionPointsExtensionTest(certWithSpaceInCDPStr); + throw new RuntimeException("Illegally parsed a " + + "CRLDistributionPointsExtension uri with a space."); + } catch (IOException e) { + System.out.println("Caught the correct exception."); - /* Parse a CRLDistributionPointsExtension URI with backslashes. */ - CRLDistributionPointsExtensionTest(certWithBackslashesInCDPStr); - System.out.println("Parsed CRLDistributionPointsExtension uri with " - + "backslashes."); + } - /* Constructor a URIName from a uri with a space. */ + /* Try to parse a CRLDistributionPointsExtension URI with backslashes. */ + try { + CRLDistributionPointsExtensionTest(certWithBackslashesInCDPStr); + throw new RuntimeException("Illegally parsed a " + + "CRLDistributionPointsExtension uri with a backslashes."); + } catch (IOException e) { + System.out.println("Caught the correct exception."); + } + + /* Try to construct a URIName from a uri with a space. */ String uriWithSpace = "file://crl file.crl"; - URIName name = new URIName(uriWithSpace); - System.out.println("URI re-encoded from " + uriWithSpace - + " to " + name.getName()); + URIName name; + try { + name = new URIName(uriWithSpace); + throw new RuntimeException("Illegally created a URIName " + + "from a uri with a space."); + } catch (IOException e) { + System.out.println("Caught the correct exception."); + } - /* Construct a URIName from a uri with backslashes. */ + /* Try to construct a URIName from a uri with backslashes. */ String uriWithBackslashes = "file://\\\\CRL\\crl_file.crl"; - name = new URIName(uriWithBackslashes); - System.out.println("URI re-encoded from " + uriWithBackslashes - + " to " + name.getName()); + try { + name = new URIName(uriWithBackslashes); + throw new RuntimeException("Illegally created a URIName " + + "from a uri with backslashes."); + } catch (IOException e) { + System.out.println("Caught the correct exception."); + } System.out.println("Tests passed."); } From 07e2e8803a15230e63049d07beaa1c639a86cb70 Mon Sep 17 00:00:00 2001 From: Aleksey Shipilev Date: Wed, 16 Jan 2013 16:30:04 +0100 Subject: [PATCH 053/138] 8006403: Regression: jstack failed due to the FieldInfo regression in SA Reviewed-by: sla, dholmes --- .../src/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java | 4 +++- hotspot/src/share/vm/runtime/vmStructs.cpp | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java index 7052f8a2d18..cfa26eacdbd 100644 --- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java @@ -53,6 +53,7 @@ public class InstanceKlass extends Klass { private static int HIGH_OFFSET; private static int FIELD_SLOTS; private static short FIELDINFO_TAG_SIZE; + private static short FIELDINFO_TAG_MASK; private static short FIELDINFO_TAG_OFFSET; // ClassState constants @@ -102,6 +103,7 @@ public class InstanceKlass extends Klass { HIGH_OFFSET = db.lookupIntConstant("FieldInfo::high_packed_offset").intValue(); FIELD_SLOTS = db.lookupIntConstant("FieldInfo::field_slots").intValue(); FIELDINFO_TAG_SIZE = db.lookupIntConstant("FIELDINFO_TAG_SIZE").shortValue(); + FIELDINFO_TAG_MASK = db.lookupIntConstant("FIELDINFO_TAG_MASK").shortValue(); FIELDINFO_TAG_OFFSET = db.lookupIntConstant("FIELDINFO_TAG_OFFSET").shortValue(); // read ClassState constants @@ -321,7 +323,7 @@ public class InstanceKlass extends Klass { U2Array fields = getFields(); short lo = fields.at(index * FIELD_SLOTS + LOW_OFFSET); short hi = fields.at(index * FIELD_SLOTS + HIGH_OFFSET); - if ((lo & FIELDINFO_TAG_SIZE) == FIELDINFO_TAG_OFFSET) { + if ((lo & FIELDINFO_TAG_MASK) == FIELDINFO_TAG_OFFSET) { return VM.getVM().buildIntFromShorts(lo, hi) >> FIELDINFO_TAG_SIZE; } throw new RuntimeException("should not reach here"); diff --git a/hotspot/src/share/vm/runtime/vmStructs.cpp b/hotspot/src/share/vm/runtime/vmStructs.cpp index 8d454992cf8..791a317af17 100644 --- a/hotspot/src/share/vm/runtime/vmStructs.cpp +++ b/hotspot/src/share/vm/runtime/vmStructs.cpp @@ -2292,6 +2292,7 @@ typedef BinaryTreeDictionary MetablockTreeDictionary; /*************************************/ \ \ declare_preprocessor_constant("FIELDINFO_TAG_SIZE", FIELDINFO_TAG_SIZE) \ + declare_preprocessor_constant("FIELDINFO_TAG_MASK", FIELDINFO_TAG_MASK) \ declare_preprocessor_constant("FIELDINFO_TAG_OFFSET", FIELDINFO_TAG_OFFSET) \ \ /************************************************/ \ From 5d2a6972af5c85adc3cfffb8c7516b18cf1cd46c Mon Sep 17 00:00:00 2001 From: Jason Uh Date: Wed, 16 Jan 2013 13:35:17 -0500 Subject: [PATCH 054/138] 8005939: sun/security/x509/{X509CRLImplX509CertImpl}/Verify.java fail in confusing way when some providers not present Reviewed-by: mullan, weijun --- jdk/test/sun/security/x509/X509CRLImpl/Verify.java | 6 +++++- jdk/test/sun/security/x509/X509CertImpl/Verify.java | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/jdk/test/sun/security/x509/X509CRLImpl/Verify.java b/jdk/test/sun/security/x509/X509CRLImpl/Verify.java index f55f1bffb5e..0c1b30d74f1 100644 --- a/jdk/test/sun/security/x509/X509CRLImpl/Verify.java +++ b/jdk/test/sun/security/x509/X509CRLImpl/Verify.java @@ -95,7 +95,7 @@ public class Verify { * Should fail with NoSuchAlgorithmException. */ try { - verifyCRL(crlIssuerCertPubKey, "SunPCSC"); + verifyCRL(crlIssuerCertPubKey, "SunJCE"); throw new RuntimeException("Didn't catch the exception properly"); } catch (NoSuchAlgorithmException e) { System.out.println("Caught the correct exception."); @@ -148,6 +148,10 @@ public class Verify { throws CRLException, NoSuchAlgorithmException, InvalidKeyException, SignatureException { Provider provider = Security.getProvider(providerName); + if (provider == null) { + throw new RuntimeException("Provider " + providerName + + " not found."); + } crl.verify(key, provider); } } diff --git a/jdk/test/sun/security/x509/X509CertImpl/Verify.java b/jdk/test/sun/security/x509/X509CertImpl/Verify.java index 09c1e0e1b97..89dcbb9f1b9 100644 --- a/jdk/test/sun/security/x509/X509CertImpl/Verify.java +++ b/jdk/test/sun/security/x509/X509CertImpl/Verify.java @@ -86,7 +86,7 @@ public class Verify { * Should fail with NoSuchAlgorithmException. */ try { - verifyCert(selfSignedCertPubKey, "SunPCSC"); + verifyCert(selfSignedCertPubKey, "SunJCE"); throw new RuntimeException("Didn't catch the exception properly"); } catch (NoSuchAlgorithmException e) { System.out.println("Caught the correct exception."); @@ -134,6 +134,10 @@ public class Verify { throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, SignatureException { Provider provider = Security.getProvider(providerName); + if (provider == null) { + throw new RuntimeException("Provider " + providerName + + " not found."); + } cert.verify(key, provider); } } From c803a77fa8f02c256f2436c31f9b9352d9cacaa5 Mon Sep 17 00:00:00 2001 From: David Chase Date: Wed, 16 Jan 2013 14:55:18 -0800 Subject: [PATCH 055/138] 8006204: please JTREGify test/compiler/7190310/Test7190310.java Add proper jtreg annotations in the preceding comment, including an explicit timeout. Reviewed-by: kvn, twisti --- hotspot/test/compiler/7190310/Test7190310.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/hotspot/test/compiler/7190310/Test7190310.java b/hotspot/test/compiler/7190310/Test7190310.java index 57a89b93b39..b45c60bf196 100644 --- a/hotspot/test/compiler/7190310/Test7190310.java +++ b/hotspot/test/compiler/7190310/Test7190310.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,16 @@ */ /* - * Manual test + * @test + * @bug 7190310 + * @summary Inlining WeakReference.get(), and hoisting $referent may lead to non-terminating loops + * @run main/othervm/timeout=600 -Xbatch Test7190310 + */ + +/* + * Note bug exhibits as infinite loop, timeout is helpful. + * It should normally finish pretty quickly, but on some especially slow machines + * it may not. The companion _unsafe test lacks a timeout, but that is okay. */ import java.lang.ref.*; From 6799149f7d1bc04933b07466043f471892808a7b Mon Sep 17 00:00:00 2001 From: Stefan Karlsson Date: Thu, 17 Jan 2013 11:39:48 +0100 Subject: [PATCH 056/138] 8006513: Null pointer in DefaultMethods::generate_default_methods when merging annotations Reviewed-by: brutisso, jfranck --- .../src/share/vm/classfile/defaultMethods.cpp | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/hotspot/src/share/vm/classfile/defaultMethods.cpp b/hotspot/src/share/vm/classfile/defaultMethods.cpp index ac593a7ef24..ce516b84bc1 100644 --- a/hotspot/src/share/vm/classfile/defaultMethods.cpp +++ b/hotspot/src/share/vm/classfile/defaultMethods.cpp @@ -1285,13 +1285,15 @@ static void merge_in_new_methods(InstanceKlass* klass, enum { ANNOTATIONS, PARAMETERS, DEFAULTS, NUM_ARRAYS }; - Array* original_annots[NUM_ARRAYS]; + Array* original_annots[NUM_ARRAYS] = { NULL }; Array* original_methods = klass->methods(); Annotations* annots = klass->annotations(); - original_annots[ANNOTATIONS] = annots->methods_annotations(); - original_annots[PARAMETERS] = annots->methods_parameter_annotations(); - original_annots[DEFAULTS] = annots->methods_default_annotations(); + if (annots != NULL) { + original_annots[ANNOTATIONS] = annots->methods_annotations(); + original_annots[PARAMETERS] = annots->methods_parameter_annotations(); + original_annots[DEFAULTS] = annots->methods_default_annotations(); + } Array* original_ordering = klass->method_ordering(); Array* merged_ordering = Universe::the_empty_int_array(); @@ -1370,9 +1372,15 @@ static void merge_in_new_methods(InstanceKlass* klass, // Replace klass methods with new merged lists klass->set_methods(merged_methods); - annots->set_methods_annotations(merged_annots[ANNOTATIONS]); - annots->set_methods_parameter_annotations(merged_annots[PARAMETERS]); - annots->set_methods_default_annotations(merged_annots[DEFAULTS]); + if (annots != NULL) { + annots->set_methods_annotations(merged_annots[ANNOTATIONS]); + annots->set_methods_parameter_annotations(merged_annots[PARAMETERS]); + annots->set_methods_default_annotations(merged_annots[DEFAULTS]); + } else { + assert(merged_annots[ANNOTATIONS] == NULL, "Must be"); + assert(merged_annots[PARAMETERS] == NULL, "Must be"); + assert(merged_annots[DEFAULTS] == NULL, "Must be"); + } ClassLoaderData* cld = klass->class_loader_data(); MetadataFactory::free_array(cld, original_methods); From b7bcfe73b19eb6bcf7532f0c39b2506bc7c48c66 Mon Sep 17 00:00:00 2001 From: Konstantin Shefov Date: Thu, 17 Jan 2013 15:08:08 +0000 Subject: [PATCH 057/138] 7124209: [macosx] SpringLayout issue. BASELINE is not in the range: [NORTH, SOUTH] Reviewed-by: serb, alexsch --- .../SpringLayout/4726194/bug4726194.java | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 jdk/test/javax/swing/SpringLayout/4726194/bug4726194.java diff --git a/jdk/test/javax/swing/SpringLayout/4726194/bug4726194.java b/jdk/test/javax/swing/SpringLayout/4726194/bug4726194.java new file mode 100644 index 00000000000..2516910e07c --- /dev/null +++ b/jdk/test/javax/swing/SpringLayout/4726194/bug4726194.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4726194 7124209 + * @summary Tests for 4726194 + * @author Phil Milne + */ +import java.awt.*; +import java.lang.reflect.InvocationTargetException; +import java.util.*; +import java.util.List; +import javax.swing.*; + +public class bug4726194 { + + private static String[] hConstraints = {SpringLayout.WEST, "Width", SpringLayout.EAST, SpringLayout.HORIZONTAL_CENTER}; + private static String[] vConstraints = {SpringLayout.NORTH, "Height", SpringLayout.SOUTH, SpringLayout.VERTICAL_CENTER, SpringLayout.BASELINE}; + private static int[] FAIL = new int[3]; + private static boolean TEST_DUPLICATES = false; + + public static void main(String[] args) { + try { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + int minLevel = 2; + int maxLevel = 2; + for (int i = minLevel; i <= maxLevel; i++) { + test(i, true); + test(i, false); + } + } + }); + } catch (InterruptedException | InvocationTargetException ex) { + ex.printStackTrace(); + throw new RuntimeException("FAILED: SwingUtilities.invokeAndWait method failed!"); + } + } + + public static void test(int level, boolean horizontal) { + List result = new ArrayList(); + String[] constraints = horizontal ? hConstraints : vConstraints; + test(level, constraints, result, Arrays.asList(new Object[level])); + JTextField tf = new JTextField(""); + tf.setFont(new Font("Dialog", Font.PLAIN, 6)); + System.out.print("\t\t"); + for (int j = 0; j < constraints.length; j++) { + String constraint = constraints[j]; + System.out.print(constraint + " ".substring(constraint.length())); + } + System.out.println(""); + for (int i = 0; i < result.size(); i++) { + SpringLayout.Constraints c = new SpringLayout.Constraints(tf); + List cc = (List) result.get(i); + for (int j = 0; j < cc.size(); j++) { + String constraint = (String) cc.get(j); + c.setConstraint(constraint, Spring.constant((j + 1) * 10)); + } + System.out.print(" Input:\t\t"); + for (int j = 0; j < constraints.length; j++) { + String constraint = constraints[j]; + int jj = cc.indexOf(constraint); + String val = cc.contains(constraint) ? Integer.toString((jj + 1) * 10) : "?"; + System.out.print(val + "\t\t"); + } + System.out.println(""); + System.out.print("Output:\t\t"); + for (int j = 0; j < constraints.length; j++) { + String constraint = constraints[j]; + Spring spring = c.getConstraint(constraint); + String springVal = (spring == null) ? "?" : Integer.toString(spring.getValue()); + System.out.print(springVal); + System.out.print("\t\t"); + } + for (int j = 0; j < cc.size(); j++) { + String constraint = (String) cc.get(j); + Spring con = c.getConstraint(constraint); + if (con == null || con.getValue() != (j + 1) * 10) { + throw new RuntimeException("Values are wrong!!! "); + } + } + if (horizontal) { + int[] a1 = getValues(c, new String[]{SpringLayout.WEST, SpringLayout.WIDTH, SpringLayout.EAST}); + if (a1[0] + a1[1] != a1[2]) { + throw new RuntimeException("WEST + WIDTH != EAST!!! "); + } + int[] a2 = getValues(c, new String[]{SpringLayout.WEST, SpringLayout.WIDTH, SpringLayout.HORIZONTAL_CENTER}); + if (a2[0] + a2[1] / 2 != a2[2]) { + throw new RuntimeException("WEST + WIDTH/2 != HORIZONTAL_CENTER!!! "); + } + } else { + int[] a3 = getValues(c, new String[]{SpringLayout.NORTH, SpringLayout.HEIGHT, SpringLayout.SOUTH}); + if (a3[0] + a3[1] != a3[2]) { + throw new RuntimeException("NORTH + HEIGHT != SOUTH!!! "); + } + int[] a4 = getValues(c, new String[]{SpringLayout.NORTH, SpringLayout.HEIGHT, SpringLayout.VERTICAL_CENTER}); + int vcDiff = Math.abs(a4[0] + a4[1] / 2 - a4[2]); + if (vcDiff > 1) { + throw new RuntimeException("NORTH + HEIGHT/2 != VERTICAL_CENTER!!! "); + } + int[] a5 = getValues(c, new String[]{SpringLayout.NORTH, SpringLayout.BASELINE, SpringLayout.SOUTH}); + if (a5[0] > a5[1] != a5[1] > a5[2]) { + throw new RuntimeException("BASELINE is not in the range: [NORTH, SOUTH]!!!"); + } + } + System.out.println(""); + } + System.out.println(""); + } + + private static int[] getValues(SpringLayout.Constraints con, String[] cNames) { + int[] result = new int[cNames.length]; + for (int i = 0; i < cNames.length; i++) { + String name = cNames[i]; + Spring s = con.getConstraint(name); + if (s == null) { + System.out.print("Warning: " + name + " is undefined. "); + return FAIL; + } + result[i] = s.getValue(); + } + return result; + } + + public static void test(int level, String[] constraints, List result, List soFar) { + if (level == 0) { + result.add(soFar); + return; + } + for (int i = 0; i < constraints.length; i++) { + if (soFar.contains(constraints[i]) && !TEST_DUPLICATES) { + continue; + } + List child = new ArrayList(soFar); + child.set(level - 1, constraints[i]); + test(level - 1, constraints, result, child); + } + } +} From 68f3dd76c92ce3b0219ac2cc346718dd865645d9 Mon Sep 17 00:00:00 2001 From: Harold Seigel Date: Thu, 17 Jan 2013 10:25:16 -0500 Subject: [PATCH 058/138] 7102489: RFE: cleanup jlong typedef on __APPLE__and _LLP64 systems Define jlong as long on all LP64 platforms and add JLONG_FORMAT macro. Reviewed-by: dholmes, coleenp, mikael, kvn --- hotspot/src/cpu/x86/vm/jni_x86.h | 9 ++------- hotspot/src/os/bsd/vm/os_bsd.inline.hpp | 10 +--------- hotspot/src/os/linux/vm/os_linux.inline.hpp | 10 +--------- hotspot/src/os/posix/launcher/java_md.c | 7 +------ hotspot/src/os/posix/launcher/java_md.h | 8 +++++++- hotspot/src/os/solaris/vm/os_solaris.inline.hpp | 5 +---- hotspot/src/os/windows/launcher/java_md.c | 7 +------ hotspot/src/os/windows/launcher/java_md.h | 4 +++- hotspot/src/os/windows/vm/os_windows.cpp | 8 ++++---- hotspot/src/os/windows/vm/os_windows.inline.hpp | 5 +---- hotspot/src/share/tools/launcher/java.c | 4 ++-- hotspot/src/share/tools/launcher/java.h | 3 +-- hotspot/src/share/vm/c1/c1_InstructionPrinter.cpp | 4 ++-- hotspot/src/share/vm/c1/c1_LIR.cpp | 4 ++-- hotspot/src/share/vm/ci/ciReplay.cpp | 4 ++-- .../concurrentMarkSweep/compactibleFreeListSpace.cpp | 4 ++-- .../concurrentMarkSweepGeneration.cpp | 6 +++--- .../parallelScavenge/psScavenge.cpp | 4 ++-- .../share/vm/gc_implementation/shared/ageTable.cpp | 4 ++-- hotspot/src/share/vm/memory/universe.cpp | 4 ++-- hotspot/src/share/vm/opto/idealGraphPrinter.cpp | 4 ++-- hotspot/src/share/vm/opto/type.cpp | 12 ++++++------ hotspot/src/share/vm/runtime/aprofiler.cpp | 4 ++-- hotspot/src/share/vm/runtime/arguments.cpp | 4 ++-- hotspot/src/share/vm/runtime/os.hpp | 6 +----- hotspot/src/share/vm/runtime/perfData.cpp | 4 ++-- hotspot/src/share/vm/runtime/virtualspace.cpp | 6 +++--- hotspot/src/share/vm/services/diagnosticArgument.cpp | 6 +++--- hotspot/src/share/vm/services/heapDumper.cpp | 4 ++-- hotspot/src/share/vm/services/lowMemoryDetector.cpp | 4 ++-- hotspot/src/share/vm/utilities/globalDefinitions.hpp | 10 +++++++++- .../src/share/vm/utilities/globalDefinitions_gcc.hpp | 6 +++++- hotspot/src/share/vm/utilities/ostream.cpp | 8 +++----- hotspot/src/share/vm/utilities/taskqueue.cpp | 6 +++--- 34 files changed, 87 insertions(+), 111 deletions(-) diff --git a/hotspot/src/cpu/x86/vm/jni_x86.h b/hotspot/src/cpu/x86/vm/jni_x86.h index d724c86007a..2cd7abd304e 100644 --- a/hotspot/src/cpu/x86/vm/jni_x86.h +++ b/hotspot/src/cpu/x86/vm/jni_x86.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,14 +38,9 @@ #define JNICALL typedef int jint; -#if defined(_LP64) && !defined(__APPLE__) +#if defined(_LP64) typedef long jlong; #else - /* - * On _LP64 __APPLE__ "long" and "long long" are both 64 bits, - * but we use the "long long" typedef to avoid complaints from - * the __APPLE__ compiler about fprintf formats. - */ typedef long long jlong; #endif diff --git a/hotspot/src/os/bsd/vm/os_bsd.inline.hpp b/hotspot/src/os/bsd/vm/os_bsd.inline.hpp index 6a7cdc08b5f..8fc3c2b0878 100644 --- a/hotspot/src/os/bsd/vm/os_bsd.inline.hpp +++ b/hotspot/src/os/bsd/vm/os_bsd.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -59,14 +59,6 @@ inline const char* os::path_separator() { return ":"; } -inline const char* os::jlong_format_specifier() { - return "%lld"; -} - -inline const char* os::julong_format_specifier() { - return "%llu"; -} - // File names are case-sensitive on windows only inline int os::file_name_strcmp(const char* s1, const char* s2) { return strcmp(s1, s2); diff --git a/hotspot/src/os/linux/vm/os_linux.inline.hpp b/hotspot/src/os/linux/vm/os_linux.inline.hpp index c779f8a6aa5..3a8d8d97d9b 100644 --- a/hotspot/src/os/linux/vm/os_linux.inline.hpp +++ b/hotspot/src/os/linux/vm/os_linux.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -68,14 +68,6 @@ inline const char* os::path_separator() { return ":"; } -inline const char* os::jlong_format_specifier() { - return "%lld"; -} - -inline const char* os::julong_format_specifier() { - return "%llu"; -} - // File names are case-sensitive on windows only inline int os::file_name_strcmp(const char* s1, const char* s2) { return strcmp(s1, s2); diff --git a/hotspot/src/os/posix/launcher/java_md.c b/hotspot/src/os/posix/launcher/java_md.c index 970222c4fd3..b5fc949813c 100644 --- a/hotspot/src/os/posix/launcher/java_md.c +++ b/hotspot/src/os/posix/launcher/java_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1876,11 +1876,6 @@ void SplashFreeLibrary() { } } -const char * -jlong_format_specifier() { - return "%lld"; -} - /* * Block current thread and continue execution in a new thread */ diff --git a/hotspot/src/os/posix/launcher/java_md.h b/hotspot/src/os/posix/launcher/java_md.h index ed36fd1af67..63c449a2926 100644 --- a/hotspot/src/os/posix/launcher/java_md.h +++ b/hotspot/src/os/posix/launcher/java_md.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -64,6 +64,12 @@ #define Counter2Micros(counts) (1) #endif /* HAVE_GETHRTIME */ +#ifdef _LP64 +#define JLONG_FORMAT "%ld" +#else +#define JLONG_FORMAT "%lld" +#endif + /* * Function prototypes. */ diff --git a/hotspot/src/os/solaris/vm/os_solaris.inline.hpp b/hotspot/src/os/solaris/vm/os_solaris.inline.hpp index a3f09d01d32..61691fd384a 100644 --- a/hotspot/src/os/solaris/vm/os_solaris.inline.hpp +++ b/hotspot/src/os/solaris/vm/os_solaris.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,9 +50,6 @@ inline const char* os::file_separator() { return "/"; } inline const char* os::line_separator() { return "\n"; } inline const char* os::path_separator() { return ":"; } -inline const char* os::jlong_format_specifier() { return "%lld"; } -inline const char* os::julong_format_specifier() { return "%llu"; } - // File names are case-sensitive on windows only inline int os::file_name_strcmp(const char* s1, const char* s2) { return strcmp(s1, s2); diff --git a/hotspot/src/os/windows/launcher/java_md.c b/hotspot/src/os/windows/launcher/java_md.c index 2fde40ad205..3e28755009c 100644 --- a/hotspot/src/os/windows/launcher/java_md.c +++ b/hotspot/src/os/windows/launcher/java_md.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1323,11 +1323,6 @@ void SplashFreeLibrary() { } } -const char * -jlong_format_specifier() { - return "%I64d"; -} - /* * Block current thread and continue execution in a new thread */ diff --git a/hotspot/src/os/windows/launcher/java_md.h b/hotspot/src/os/windows/launcher/java_md.h index 111be1ee13a..763e2457644 100644 --- a/hotspot/src/os/windows/launcher/java_md.h +++ b/hotspot/src/os/windows/launcher/java_md.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -69,6 +69,8 @@ extern jlong Counter2Micros(jlong counts); extern int _main(int argc, char **argv); #endif +#define JLONG_FORMAT "%I64d" + /* * Function prototypes. */ diff --git a/hotspot/src/os/windows/vm/os_windows.cpp b/hotspot/src/os/windows/vm/os_windows.cpp index b8498d5a1e2..a10b83f8940 100644 --- a/hotspot/src/os/windows/vm/os_windows.cpp +++ b/hotspot/src/os/windows/vm/os_windows.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2946,7 +2946,7 @@ char* os::pd_reserve_memory(size_t bytes, char* addr, size_t alignment_hint) { } if( Verbose && PrintMiscellaneous ) { reserveTimer.stop(); - tty->print_cr("reserve_memory of %Ix bytes took %ld ms (%ld ticks)", bytes, + tty->print_cr("reserve_memory of %Ix bytes took " JLONG_FORMAT " ms (" JLONG_FORMAT " ticks)", bytes, reserveTimer.milliseconds(), reserveTimer.ticks()); } } @@ -4305,7 +4305,7 @@ char* os::pd_map_memory(int fd, const char* file_name, size_t file_offset, if (hFile == NULL) { if (PrintMiscellaneous && Verbose) { DWORD err = GetLastError(); - tty->print_cr("CreateFile() failed: GetLastError->%ld."); + tty->print_cr("CreateFile() failed: GetLastError->%ld.", err); } return NULL; } @@ -4355,7 +4355,7 @@ char* os::pd_map_memory(int fd, const char* file_name, size_t file_offset, if (hMap == NULL) { if (PrintMiscellaneous && Verbose) { DWORD err = GetLastError(); - tty->print_cr("CreateFileMapping() failed: GetLastError->%ld."); + tty->print_cr("CreateFileMapping() failed: GetLastError->%ld.", err); } CloseHandle(hFile); return NULL; diff --git a/hotspot/src/os/windows/vm/os_windows.inline.hpp b/hotspot/src/os/windows/vm/os_windows.inline.hpp index 8f8c3c72fd2..1df8694d9dd 100644 --- a/hotspot/src/os/windows/vm/os_windows.inline.hpp +++ b/hotspot/src/os/windows/vm/os_windows.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,9 +38,6 @@ inline const char* os::line_separator() { return "\r\n"; } inline const char* os::path_separator() { return ";"; } inline const char* os::dll_file_extension() { return ".dll"; } -inline const char* os::jlong_format_specifier() { return "%I64d"; } -inline const char* os::julong_format_specifier() { return "%I64u"; } - inline const int os::default_file_open_flags() { return O_BINARY | O_NOINHERIT;} // File names are case-insensitive on windows only diff --git a/hotspot/src/share/tools/launcher/java.c b/hotspot/src/share/tools/launcher/java.c index 5ebfb9a8dc3..63f11d77c86 100644 --- a/hotspot/src/share/tools/launcher/java.c +++ b/hotspot/src/share/tools/launcher/java.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -808,7 +808,7 @@ CheckJvmType(int *pargc, char ***argv, jboolean speculative) { static int parse_stack_size(const char *s, jlong *result) { jlong n = 0; - int args_read = sscanf(s, jlong_format_specifier(), &n); + int args_read = sscanf(s, JLONG_FORMAT, &n); if (args_read != 1) { return 0; } diff --git a/hotspot/src/share/tools/launcher/java.h b/hotspot/src/share/tools/launcher/java.h index 97fba2184f7..1dd79618c84 100644 --- a/hotspot/src/share/tools/launcher/java.h +++ b/hotspot/src/share/tools/launcher/java.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -86,7 +86,6 @@ void ReportExceptionDescription(JNIEnv * env); jboolean RemovableMachineDependentOption(char * option); void PrintMachineDependentOptions(); -const char *jlong_format_specifier(); /* * Block current thread and continue execution in new thread */ diff --git a/hotspot/src/share/vm/c1/c1_InstructionPrinter.cpp b/hotspot/src/share/vm/c1/c1_InstructionPrinter.cpp index a2f6f86fd85..68e0feb5b83 100644 --- a/hotspot/src/share/vm/c1/c1_InstructionPrinter.cpp +++ b/hotspot/src/share/vm/c1/c1_InstructionPrinter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -360,7 +360,7 @@ void InstructionPrinter::do_Constant(Constant* x) { ValueType* t = x->type(); switch (t->tag()) { case intTag : output()->print("%d" , t->as_IntConstant ()->value()); break; - case longTag : output()->print(os::jlong_format_specifier(), t->as_LongConstant()->value()); output()->print("L"); break; + case longTag : output()->print(JLONG_FORMAT, t->as_LongConstant()->value()); output()->print("L"); break; case floatTag : output()->print("%g" , t->as_FloatConstant ()->value()); break; case doubleTag : output()->print("%gD" , t->as_DoubleConstant()->value()); break; case objectTag : print_object(x); break; diff --git a/hotspot/src/share/vm/c1/c1_LIR.cpp b/hotspot/src/share/vm/c1/c1_LIR.cpp index 178d1867179..7a0563c6a20 100644 --- a/hotspot/src/share/vm/c1/c1_LIR.cpp +++ b/hotspot/src/share/vm/c1/c1_LIR.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1563,7 +1563,7 @@ void LIR_Const::print_value_on(outputStream* out) const { switch (type()) { case T_ADDRESS:out->print("address:%d",as_jint()); break; case T_INT: out->print("int:%d", as_jint()); break; - case T_LONG: out->print("lng:%lld", as_jlong()); break; + case T_LONG: out->print("lng:" JLONG_FORMAT, as_jlong()); break; case T_FLOAT: out->print("flt:%f", as_jfloat()); break; case T_DOUBLE: out->print("dbl:%f", as_jdouble()); break; case T_OBJECT: out->print("obj:0x%x", as_jobject()); break; diff --git a/hotspot/src/share/vm/ci/ciReplay.cpp b/hotspot/src/share/vm/ci/ciReplay.cpp index f2e77241960..9b532b8e26b 100644 --- a/hotspot/src/share/vm/ci/ciReplay.cpp +++ b/hotspot/src/share/vm/ci/ciReplay.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -645,7 +645,7 @@ class CompileReplay : public StackObj { java_mirror->bool_field_put(fd.offset(), value); } else if (strcmp(field_signature, "J") == 0) { jlong value; - if (sscanf(string_value, INT64_FORMAT, &value) != 1) { + if (sscanf(string_value, JLONG_FORMAT, &value) != 1) { fprintf(stderr, "Error parsing long: %s\n", string_value); return; } diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp index 2f43c987669..9fb347a05c5 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -555,7 +555,7 @@ void CompactibleFreeListSpace::reportFreeListStatistics() const { reportIndexedFreeListStatistics(); size_t total_size = totalSizeInIndexedFreeLists() + _dictionary->total_chunk_size(DEBUG_ONLY(freelistLock())); - gclog_or_tty->print(" free=%ld frag=%1.4f\n", total_size, flsFrag()); + gclog_or_tty->print(" free=" SIZE_FORMAT " frag=%1.4f\n", total_size, flsFrag()); } } diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp index 475f2b8fee1..2ed7e465ad0 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -3338,7 +3338,7 @@ bool ConcurrentMarkSweepGeneration::grow_by(size_t bytes) { if (Verbose && PrintGC) { size_t new_mem_size = _virtual_space.committed_size(); size_t old_mem_size = new_mem_size - bytes; - gclog_or_tty->print_cr("Expanding %s from %ldK by %ldK to %ldK", + gclog_or_tty->print_cr("Expanding %s from " SIZE_FORMAT "K by " SIZE_FORMAT "K to " SIZE_FORMAT "K", name(), old_mem_size/K, bytes/K, new_mem_size/K); } } @@ -9203,7 +9203,7 @@ void ASConcurrentMarkSweepGeneration::shrink_by(size_t desired_bytes) { if (Verbose && PrintGCDetails) { size_t new_mem_size = _virtual_space.committed_size(); size_t old_mem_size = new_mem_size + bytes; - gclog_or_tty->print_cr("Shrinking %s from %ldK by %ldK to %ldK", + gclog_or_tty->print_cr("Shrinking %s from " SIZE_FORMAT "K by " SIZE_FORMAT "K to " SIZE_FORMAT "K", name(), old_mem_size/K, bytes/K, new_mem_size/K); } } diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp index f68b1b5a446..078bd62aff7 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -529,7 +529,7 @@ bool PSScavenge::invoke_no_policy() { if (PrintTenuringDistribution) { gclog_or_tty->cr(); - gclog_or_tty->print_cr("Desired survivor size %ld bytes, new threshold %u (max %u)", + gclog_or_tty->print_cr("Desired survivor size " SIZE_FORMAT " bytes, new threshold %u (max %u)", size_policy->calculated_survivor_size_in_bytes(), _tenuring_threshold, MaxTenuringThreshold); } diff --git a/hotspot/src/share/vm/gc_implementation/shared/ageTable.cpp b/hotspot/src/share/vm/gc_implementation/shared/ageTable.cpp index 50162a0adb1..311fd7771dd 100644 --- a/hotspot/src/share/vm/gc_implementation/shared/ageTable.cpp +++ b/hotspot/src/share/vm/gc_implementation/shared/ageTable.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -96,7 +96,7 @@ uint ageTable::compute_tenuring_threshold(size_t survivor_capacity) { if (PrintTenuringDistribution) { gclog_or_tty->cr(); - gclog_or_tty->print_cr("Desired survivor size %ld bytes, new threshold %u (max %u)", + gclog_or_tty->print_cr("Desired survivor size " SIZE_FORMAT " bytes, new threshold %u (max %u)", desired_survivor_size*oopSize, result, MaxTenuringThreshold); } diff --git a/hotspot/src/share/vm/memory/universe.cpp b/hotspot/src/share/vm/memory/universe.cpp index c528d270c64..c9199228cb9 100644 --- a/hotspot/src/share/vm/memory/universe.cpp +++ b/hotspot/src/share/vm/memory/universe.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -228,7 +228,7 @@ void Universe::check_alignment(uintx size, uintx alignment, const char* name) { if (size < alignment || size % alignment != 0) { ResourceMark rm; stringStream st; - st.print("Size of %s (%ld bytes) must be aligned to %ld bytes", name, size, alignment); + st.print("Size of %s (" UINTX_FORMAT " bytes) must be aligned to " UINTX_FORMAT " bytes", name, size, alignment); char* error = st.as_string(); vm_exit_during_initialization(error); } diff --git a/hotspot/src/share/vm/opto/idealGraphPrinter.cpp b/hotspot/src/share/vm/opto/idealGraphPrinter.cpp index 0928704d9cb..1f811b8f4b8 100644 --- a/hotspot/src/share/vm/opto/idealGraphPrinter.cpp +++ b/hotspot/src/share/vm/opto/idealGraphPrinter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -547,7 +547,7 @@ void IdealGraphPrinter::visit_node(Node *n, bool edges, VectorSet* temp_set) { // max. 2 chars allowed if (value >= -9 && value <= 99) { - sprintf(buffer, INT64_FORMAT, value); + sprintf(buffer, JLONG_FORMAT, value); print_prop(short_name, buffer); } else { print_prop(short_name, "L"); diff --git a/hotspot/src/share/vm/opto/type.cpp b/hotspot/src/share/vm/opto/type.cpp index 1a8ee2597dd..3928c23aff3 100644 --- a/hotspot/src/share/vm/opto/type.cpp +++ b/hotspot/src/share/vm/opto/type.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1542,10 +1542,10 @@ bool TypeLong::is_finite() const { static const char* longnamenear(jlong x, const char* xname, char* buf, jlong n) { if (n > x) { if (n >= x + 10000) return NULL; - sprintf(buf, "%s+" INT64_FORMAT, xname, n - x); + sprintf(buf, "%s+" JLONG_FORMAT, xname, n - x); } else if (n < x) { if (n <= x - 10000) return NULL; - sprintf(buf, "%s-" INT64_FORMAT, xname, x - n); + sprintf(buf, "%s-" JLONG_FORMAT, xname, x - n); } else { return xname; } @@ -1557,11 +1557,11 @@ static const char* longname(char* buf, jlong n) { if (n == min_jlong) return "min"; else if (n < min_jlong + 10000) - sprintf(buf, "min+" INT64_FORMAT, n - min_jlong); + sprintf(buf, "min+" JLONG_FORMAT, n - min_jlong); else if (n == max_jlong) return "max"; else if (n > max_jlong - 10000) - sprintf(buf, "max-" INT64_FORMAT, max_jlong - n); + sprintf(buf, "max-" JLONG_FORMAT, max_jlong - n); else if ((str = longnamenear(max_juint, "maxuint", buf, n)) != NULL) return str; else if ((str = longnamenear(max_jint, "maxint", buf, n)) != NULL) @@ -1569,7 +1569,7 @@ static const char* longname(char* buf, jlong n) { else if ((str = longnamenear(min_jint, "minint", buf, n)) != NULL) return str; else - sprintf(buf, INT64_FORMAT, n); + sprintf(buf, JLONG_FORMAT, n); return buf; } diff --git a/hotspot/src/share/vm/runtime/aprofiler.cpp b/hotspot/src/share/vm/runtime/aprofiler.cpp index 6c4e9cc428d..e71bfb587ef 100644 --- a/hotspot/src/share/vm/runtime/aprofiler.cpp +++ b/hotspot/src/share/vm/runtime/aprofiler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -129,7 +129,7 @@ void AllocationProfiler::print(size_t cutoff) { assert(!is_active(), "AllocationProfiler cannot be active while printing profile"); tty->cr(); - tty->print_cr("Allocation profile (sizes in bytes, cutoff = %ld bytes):", cutoff * BytesPerWord); + tty->print_cr("Allocation profile (sizes in bytes, cutoff = " SIZE_FORMAT " bytes):", cutoff * BytesPerWord); tty->cr(); // Print regular instance klasses and basic type array klasses diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp index 65f6e03b7ff..d5d700441df 100644 --- a/hotspot/src/share/vm/runtime/arguments.cpp +++ b/hotspot/src/share/vm/runtime/arguments.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -532,7 +532,7 @@ char* SysClassPath::add_jars_to_path(char* path, const char* directory) { // Parses a memory size specification string. static bool atomull(const char *s, julong* result) { julong n = 0; - int args_read = sscanf(s, os::julong_format_specifier(), &n); + int args_read = sscanf(s, JULONG_FORMAT, &n); if (args_read != 1) { return false; } diff --git a/hotspot/src/share/vm/runtime/os.hpp b/hotspot/src/share/vm/runtime/os.hpp index 235005f9890..d061a0848c6 100644 --- a/hotspot/src/share/vm/runtime/os.hpp +++ b/hotspot/src/share/vm/runtime/os.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -641,10 +641,6 @@ class os: AllStatic { static struct hostent* get_host_by_name(char* name); - // Printing 64 bit integers - static const char* jlong_format_specifier(); - static const char* julong_format_specifier(); - // Support for signals (see JVM_RaiseSignal, JVM_RegisterSignal) static void signal_init(); static void signal_init_pd(); diff --git a/hotspot/src/share/vm/runtime/perfData.cpp b/hotspot/src/share/vm/runtime/perfData.cpp index ddb565b074c..777ea27f906 100644 --- a/hotspot/src/share/vm/runtime/perfData.cpp +++ b/hotspot/src/share/vm/runtime/perfData.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -192,7 +192,7 @@ PerfLong::PerfLong(CounterNS ns, const char* namep, Units u, Variability v) } int PerfLong::format(char* buffer, int length) { - return jio_snprintf(buffer, length,"%lld", *(jlong*)_valuep); + return jio_snprintf(buffer, length, JLONG_FORMAT, *(jlong*)_valuep); } PerfLongVariant::PerfLongVariant(CounterNS ns, const char* namep, Units u, diff --git a/hotspot/src/share/vm/runtime/virtualspace.cpp b/hotspot/src/share/vm/runtime/virtualspace.cpp index f33c1995e34..dffb588dbae 100644 --- a/hotspot/src/share/vm/runtime/virtualspace.cpp +++ b/hotspot/src/share/vm/runtime/virtualspace.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -868,8 +868,8 @@ void VirtualSpace::print() { tty->print ("Virtual space:"); if (special()) tty->print(" (pinned in memory)"); tty->cr(); - tty->print_cr(" - committed: %ld", committed_size()); - tty->print_cr(" - reserved: %ld", reserved_size()); + tty->print_cr(" - committed: " SIZE_FORMAT, committed_size()); + tty->print_cr(" - reserved: " SIZE_FORMAT, reserved_size()); tty->print_cr(" - [low, high]: [" INTPTR_FORMAT ", " INTPTR_FORMAT "]", low(), high()); tty->print_cr(" - [low_b, high_b]: [" INTPTR_FORMAT ", " INTPTR_FORMAT "]", low_boundary(), high_boundary()); } diff --git a/hotspot/src/share/vm/services/diagnosticArgument.cpp b/hotspot/src/share/vm/services/diagnosticArgument.cpp index 23267fae520..5d0eb756b28 100644 --- a/hotspot/src/share/vm/services/diagnosticArgument.cpp +++ b/hotspot/src/share/vm/services/diagnosticArgument.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2013 Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -86,7 +86,7 @@ void GenDCmdArgument::to_string(StringArrayArgument* f, char* buf, size_t len) { template <> void DCmdArgument::parse_value(const char* str, size_t len, TRAPS) { - if (str == NULL || sscanf(str, INT64_FORMAT, &_value) != 1) { + if (str == NULL || sscanf(str, JLONG_FORMAT, &_value) != 1) { THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "Integer parsing error in diagnostic command arguments\n"); } @@ -171,7 +171,7 @@ template <> void DCmdArgument::parse_value(const char* str, "Integer parsing error nanotime value: syntax error"); } - int argc = sscanf(str, INT64_FORMAT , &_value._time); + int argc = sscanf(str, JLONG_FORMAT, &_value._time); if (argc != 1) { THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "Integer parsing error nanotime value: syntax error"); diff --git a/hotspot/src/share/vm/services/heapDumper.cpp b/hotspot/src/share/vm/services/heapDumper.cpp index 0dbe2e86589..65ee27f457a 100644 --- a/hotspot/src/share/vm/services/heapDumper.cpp +++ b/hotspot/src/share/vm/services/heapDumper.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1866,7 +1866,7 @@ int HeapDumper::dump(const char* path) { if (error() == NULL) { char msg[256]; sprintf(msg, "Heap dump file created [%s bytes in %3.3f secs]", - os::jlong_format_specifier(), timer()->seconds()); + JLONG_FORMAT, timer()->seconds()); tty->print_cr(msg, writer.bytes_written()); } else { tty->print_cr("Dump file is incomplete: %s", writer.error()); diff --git a/hotspot/src/share/vm/services/lowMemoryDetector.cpp b/hotspot/src/share/vm/services/lowMemoryDetector.cpp index babd93ac85b..199a342dd77 100644 --- a/hotspot/src/share/vm/services/lowMemoryDetector.cpp +++ b/hotspot/src/share/vm/services/lowMemoryDetector.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -353,7 +353,7 @@ void SensorInfo::clear(int count, TRAPS) { #ifndef PRODUCT void SensorInfo::print() { - tty->print_cr("%s count = %ld pending_triggers = %ld pending_clears = %ld", + tty->print_cr("%s count = " SIZE_FORMAT " pending_triggers = %ld pending_clears = %ld", (_sensor_on ? "on" : "off"), _sensor_count, _pending_trigger_count, _pending_clear_count); } diff --git a/hotspot/src/share/vm/utilities/globalDefinitions.hpp b/hotspot/src/share/vm/utilities/globalDefinitions.hpp index e00be90320e..5c10cf018be 100644 --- a/hotspot/src/share/vm/utilities/globalDefinitions.hpp +++ b/hotspot/src/share/vm/utilities/globalDefinitions.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1250,6 +1250,14 @@ inline int build_int_from_shorts( jushort low, jushort high ) { #define PTR64_FORMAT "0x%016" PRIx64 +// Format jlong, if necessary +#ifndef JLONG_FORMAT +#define JLONG_FORMAT INT64_FORMAT +#endif +#ifndef JULONG_FORMAT +#define JULONG_FORMAT UINT64_FORMAT +#endif + // Format pointers which change size between 32- and 64-bit. #ifdef _LP64 #define INTPTR_FORMAT "0x%016" PRIxPTR diff --git a/hotspot/src/share/vm/utilities/globalDefinitions_gcc.hpp b/hotspot/src/share/vm/utilities/globalDefinitions_gcc.hpp index e103816de93..a69708f8c8f 100644 --- a/hotspot/src/share/vm/utilities/globalDefinitions_gcc.hpp +++ b/hotspot/src/share/vm/utilities/globalDefinitions_gcc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -306,4 +306,8 @@ inline int wcslen(const jchar* x) { return wcslen((const wchar_t*)x); } #endif #define offsetof(klass,field) offset_of(klass,field) +#if defined(_LP64) && defined(__APPLE__) +#define JLONG_FORMAT "%ld" +#endif // _LP64 && __APPLE__ + #endif // SHARE_VM_UTILITIES_GLOBALDEFINITIONS_GCC_HPP diff --git a/hotspot/src/share/vm/utilities/ostream.cpp b/hotspot/src/share/vm/utilities/ostream.cpp index 2b6e2eeb853..ea0a0c25bec 100644 --- a/hotspot/src/share/vm/utilities/ostream.cpp +++ b/hotspot/src/share/vm/utilities/ostream.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -243,13 +243,11 @@ outputStream& outputStream::indent() { } void outputStream::print_jlong(jlong value) { - // N.B. Same as INT64_FORMAT - print(os::jlong_format_specifier(), value); + print(JLONG_FORMAT, value); } void outputStream::print_julong(julong value) { - // N.B. Same as UINT64_FORMAT - print(os::julong_format_specifier(), value); + print(JULONG_FORMAT, value); } /** diff --git a/hotspot/src/share/vm/utilities/taskqueue.cpp b/hotspot/src/share/vm/utilities/taskqueue.cpp index fbb035adf0c..862c9b304fb 100644 --- a/hotspot/src/share/vm/utilities/taskqueue.cpp +++ b/hotspot/src/share/vm/utilities/taskqueue.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -239,8 +239,8 @@ ParallelTaskTerminator::offer_termination(TerminatorTerminator* terminator) { #ifdef TRACESPINNING void ParallelTaskTerminator::print_termination_counts() { - gclog_or_tty->print_cr("ParallelTaskTerminator Total yields: %lld " - "Total spins: %lld Total peeks: %lld", + gclog_or_tty->print_cr("ParallelTaskTerminator Total yields: " UINT32_FORMAT + " Total spins: " UINT32_FORMAT " Total peeks: " UINT32_FORMAT, total_yields(), total_spins(), total_peeks()); From e508ba9b0bd7553f7cf5e65858efafe26b56a956 Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Thu, 17 Jan 2013 13:40:31 -0500 Subject: [PATCH 059/138] 7174978: NPG: Fix bactrace builder for class redefinition Remove Method* from backtrace but save version so redefine classes doesn't give inaccurate line numbers. Removed old Merlin API with duplicate code. Reviewed-by: dholmes, sspitsyn --- hotspot/make/bsd/makefiles/mapfile-vers-debug | 3 +- .../make/bsd/makefiles/mapfile-vers-product | 3 +- .../make/linux/makefiles/mapfile-vers-debug | 3 +- .../make/linux/makefiles/mapfile-vers-product | 3 +- hotspot/make/solaris/makefiles/mapfile-vers | 3 +- .../src/share/vm/classfile/javaClasses.cpp | 530 +++++++++--------- .../src/share/vm/classfile/javaClasses.hpp | 17 +- hotspot/src/share/vm/oops/constantPool.cpp | 4 +- hotspot/src/share/vm/oops/constantPool.hpp | 13 +- hotspot/src/share/vm/prims/jvm.cpp | 11 +- hotspot/src/share/vm/prims/jvm.h | 5 +- .../share/vm/prims/jvmtiRedefineClasses.cpp | 43 +- 12 files changed, 297 insertions(+), 341 deletions(-) diff --git a/hotspot/make/bsd/makefiles/mapfile-vers-debug b/hotspot/make/bsd/makefiles/mapfile-vers-debug index 00fd1892716..ef827302c54 100644 --- a/hotspot/make/bsd/makefiles/mapfile-vers-debug +++ b/hotspot/make/bsd/makefiles/mapfile-vers-debug @@ -3,7 +3,7 @@ # # -# Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -205,7 +205,6 @@ SUNWprivate_1.1 { JVM_NewMultiArray; JVM_OnExit; JVM_Open; - JVM_PrintStackTrace; JVM_RaiseSignal; JVM_RawMonitorCreate; JVM_RawMonitorDestroy; diff --git a/hotspot/make/bsd/makefiles/mapfile-vers-product b/hotspot/make/bsd/makefiles/mapfile-vers-product index dfa459f0316..0d2b04ca774 100644 --- a/hotspot/make/bsd/makefiles/mapfile-vers-product +++ b/hotspot/make/bsd/makefiles/mapfile-vers-product @@ -3,7 +3,7 @@ # # -# Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -205,7 +205,6 @@ SUNWprivate_1.1 { JVM_NewMultiArray; JVM_OnExit; JVM_Open; - JVM_PrintStackTrace; JVM_RaiseSignal; JVM_RawMonitorCreate; JVM_RawMonitorDestroy; diff --git a/hotspot/make/linux/makefiles/mapfile-vers-debug b/hotspot/make/linux/makefiles/mapfile-vers-debug index d6ebedcb9bd..ae4e2f9bcc3 100644 --- a/hotspot/make/linux/makefiles/mapfile-vers-debug +++ b/hotspot/make/linux/makefiles/mapfile-vers-debug @@ -1,5 +1,5 @@ # -# Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -201,7 +201,6 @@ SUNWprivate_1.1 { JVM_NewMultiArray; JVM_OnExit; JVM_Open; - JVM_PrintStackTrace; JVM_RaiseSignal; JVM_RawMonitorCreate; JVM_RawMonitorDestroy; diff --git a/hotspot/make/linux/makefiles/mapfile-vers-product b/hotspot/make/linux/makefiles/mapfile-vers-product index 733e3af4b11..9a3028d2a09 100644 --- a/hotspot/make/linux/makefiles/mapfile-vers-product +++ b/hotspot/make/linux/makefiles/mapfile-vers-product @@ -1,5 +1,5 @@ # -# Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -201,7 +201,6 @@ SUNWprivate_1.1 { JVM_NewMultiArray; JVM_OnExit; JVM_Open; - JVM_PrintStackTrace; JVM_RaiseSignal; JVM_RawMonitorCreate; JVM_RawMonitorDestroy; diff --git a/hotspot/make/solaris/makefiles/mapfile-vers b/hotspot/make/solaris/makefiles/mapfile-vers index a10d5c1b4e6..bf21253062e 100644 --- a/hotspot/make/solaris/makefiles/mapfile-vers +++ b/hotspot/make/solaris/makefiles/mapfile-vers @@ -1,5 +1,5 @@ # -# Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -201,7 +201,6 @@ SUNWprivate_1.1 { JVM_NewMultiArray; JVM_OnExit; JVM_Open; - JVM_PrintStackTrace; JVM_RaiseSignal; JVM_RawMonitorCreate; JVM_RawMonitorDestroy; diff --git a/hotspot/src/share/vm/classfile/javaClasses.cpp b/hotspot/src/share/vm/classfile/javaClasses.cpp index cb30a83d455..d63659fb867 100644 --- a/hotspot/src/share/vm/classfile/javaClasses.cpp +++ b/hotspot/src/share/vm/classfile/javaClasses.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1158,179 +1158,43 @@ void java_lang_Throwable::print(Handle throwable, outputStream* st) { } } -// Print stack trace element to resource allocated buffer -char* java_lang_Throwable::print_stack_element_to_buffer(Method* method, int bci) { - // Get strings and string lengths - InstanceKlass* klass = method->method_holder(); - const char* klass_name = klass->external_name(); - int buf_len = (int)strlen(klass_name); - char* source_file_name; - if (klass->source_file_name() == NULL) { - source_file_name = NULL; - } else { - source_file_name = klass->source_file_name()->as_C_string(); - buf_len += (int)strlen(source_file_name); - } - char* method_name = method->name()->as_C_string(); - buf_len += (int)strlen(method_name); +// After this many redefines, the stack trace is unreliable. +const int MAX_VERSION = USHRT_MAX; - // Allocate temporary buffer with extra space for formatting and line number - char* buf = NEW_RESOURCE_ARRAY(char, buf_len + 64); +// Helper backtrace functions to store bci|version together. +static inline int merge_bci_and_version(int bci, int version) { + // only store u2 for version, checking for overflow. + if (version > USHRT_MAX || version < 0) version = MAX_VERSION; + assert((jushort)bci == bci, "bci should be short"); + return build_int_from_shorts(version, bci); +} - // Print stack trace line in buffer - sprintf(buf, "\tat %s.%s", klass_name, method_name); +static inline int bci_at(unsigned int merged) { + return extract_high_short_from_int(merged); +} +static inline int version_at(unsigned int merged) { + return extract_low_short_from_int(merged); +} + +static inline bool version_matches(Method* method, int version) { + return (method->constants()->version() == version && version < MAX_VERSION); +} + +static inline int get_line_number(Method* method, int bci) { + int line_number = 0; if (method->is_native()) { - strcat(buf, "(Native Method)"); + // Negative value different from -1 below, enabling Java code in + // class java.lang.StackTraceElement to distinguish "native" from + // "no LineNumberTable". JDK tests for -2. + line_number = -2; } else { - int line_number = method->line_number_from_bci(bci); - if (source_file_name != NULL && (line_number != -1)) { - // Sourcename and linenumber - sprintf(buf + (int)strlen(buf), "(%s:%d)", source_file_name, line_number); - } else if (source_file_name != NULL) { - // Just sourcename - sprintf(buf + (int)strlen(buf), "(%s)", source_file_name); - } else { - // Neither soucename and linenumber - sprintf(buf + (int)strlen(buf), "(Unknown Source)"); - } - nmethod* nm = method->code(); - if (WizardMode && nm != NULL) { - sprintf(buf + (int)strlen(buf), "(nmethod " INTPTR_FORMAT ")", (intptr_t)nm); + // Returns -1 if no LineNumberTable, and otherwise actual line number + line_number = method->line_number_from_bci(bci); + if (line_number == -1 && ShowHiddenFrames) { + line_number = bci + 1000000; } } - - return buf; -} - - -void java_lang_Throwable::print_stack_element(Handle stream, Method* method, int bci) { - ResourceMark rm; - char* buf = print_stack_element_to_buffer(method, bci); - print_to_stream(stream, buf); -} - -void java_lang_Throwable::print_stack_element(outputStream *st, Method* method, int bci) { - ResourceMark rm; - char* buf = print_stack_element_to_buffer(method, bci); - st->print_cr("%s", buf); -} - -void java_lang_Throwable::print_to_stream(Handle stream, const char* str) { - if (stream.is_null()) { - tty->print_cr("%s", str); - } else { - EXCEPTION_MARK; - JavaValue result(T_VOID); - Handle arg (THREAD, oopFactory::new_charArray(str, THREAD)); - if (!HAS_PENDING_EXCEPTION) { - JavaCalls::call_virtual(&result, - stream, - KlassHandle(THREAD, stream->klass()), - vmSymbols::println_name(), - vmSymbols::char_array_void_signature(), - arg, - THREAD); - } - // Ignore any exceptions. we are in the middle of exception handling. Same as classic VM. - if (HAS_PENDING_EXCEPTION) CLEAR_PENDING_EXCEPTION; - } - -} - - -const char* java_lang_Throwable::no_stack_trace_message() { - return "\t<>"; -} - - -// Currently used only for exceptions occurring during startup -void java_lang_Throwable::print_stack_trace(oop throwable, outputStream* st) { - Thread *THREAD = Thread::current(); - Handle h_throwable(THREAD, throwable); - while (h_throwable.not_null()) { - objArrayHandle result (THREAD, objArrayOop(backtrace(h_throwable()))); - if (result.is_null()) { - st->print_cr(no_stack_trace_message()); - return; - } - - while (result.not_null()) { - typeArrayHandle methods (THREAD, - typeArrayOop(result->obj_at(trace_methods_offset))); - typeArrayHandle bcis (THREAD, - typeArrayOop(result->obj_at(trace_bcis_offset))); - - if (methods.is_null() || bcis.is_null()) { - st->print_cr(no_stack_trace_message()); - return; - } - - int length = methods()->length(); - for (int index = 0; index < length; index++) { - Method* method = ((Method*)methods()->metadata_at(index)); - if (method == NULL) goto handle_cause; - int bci = bcis->ushort_at(index); - print_stack_element(st, method, bci); - } - result = objArrayHandle(THREAD, objArrayOop(result->obj_at(trace_next_offset))); - } - handle_cause: - { - EXCEPTION_MARK; - JavaValue result(T_OBJECT); - JavaCalls::call_virtual(&result, - h_throwable, - KlassHandle(THREAD, h_throwable->klass()), - vmSymbols::getCause_name(), - vmSymbols::void_throwable_signature(), - THREAD); - // Ignore any exceptions. we are in the middle of exception handling. Same as classic VM. - if (HAS_PENDING_EXCEPTION) { - CLEAR_PENDING_EXCEPTION; - h_throwable = Handle(); - } else { - h_throwable = Handle(THREAD, (oop) result.get_jobject()); - if (h_throwable.not_null()) { - st->print("Caused by: "); - print(h_throwable, st); - st->cr(); - } - } - } - } -} - - -void java_lang_Throwable::print_stack_trace(oop throwable, oop print_stream) { - // Note: this is no longer used in Merlin, but we support it for compatibility. - Thread *thread = Thread::current(); - Handle stream(thread, print_stream); - objArrayHandle result (thread, objArrayOop(backtrace(throwable))); - if (result.is_null()) { - print_to_stream(stream, no_stack_trace_message()); - return; - } - - while (result.not_null()) { - typeArrayHandle methods(thread, - typeArrayOop(result->obj_at(trace_methods_offset))); - typeArrayHandle bcis (thread, - typeArrayOop(result->obj_at(trace_bcis_offset))); - - if (methods.is_null() || bcis.is_null()) { - print_to_stream(stream, no_stack_trace_message()); - return; - } - - int length = methods()->length(); - for (int index = 0; index < length; index++) { - Method* method = ((Method*)methods()->metadata_at(index)); - if (method == NULL) return; - int bci = bcis->ushort_at(index); - print_stack_element(stream, method, bci); - } - result = objArrayHandle(thread, objArrayOop(result->obj_at(trace_next_offset))); - } + return line_number; } // This class provides a simple wrapper over the internal structure of @@ -1350,13 +1214,30 @@ class BacktraceBuilder: public StackObj { enum { trace_methods_offset = java_lang_Throwable::trace_methods_offset, - trace_bcis_offset = java_lang_Throwable::trace_bcis_offset, + trace_bcis_offset = java_lang_Throwable::trace_bcis_offset, trace_mirrors_offset = java_lang_Throwable::trace_mirrors_offset, trace_next_offset = java_lang_Throwable::trace_next_offset, trace_size = java_lang_Throwable::trace_size, trace_chunk_size = java_lang_Throwable::trace_chunk_size }; + // get info out of chunks + static typeArrayOop get_methods(objArrayHandle chunk) { + typeArrayOop methods = typeArrayOop(chunk->obj_at(trace_methods_offset)); + assert(methods != NULL, "method array should be initialized in backtrace"); + return methods; + } + static typeArrayOop get_bcis(objArrayHandle chunk) { + typeArrayOop bcis = typeArrayOop(chunk->obj_at(trace_bcis_offset)); + assert(bcis != NULL, "bci array should be initialized in backtrace"); + return bcis; + } + static objArrayOop get_mirrors(objArrayHandle chunk) { + objArrayOop mirrors = objArrayOop(chunk->obj_at(trace_mirrors_offset)); + assert(mirrors != NULL, "mirror array should be initialized in backtrace"); + return mirrors; + } + // constructor for new backtrace BacktraceBuilder(TRAPS): _methods(NULL), _bcis(NULL), _head(NULL), _mirrors(NULL) { expand(CHECK); @@ -1364,6 +1245,19 @@ class BacktraceBuilder: public StackObj { _index = 0; } + BacktraceBuilder(objArrayHandle backtrace) { + _methods = get_methods(backtrace); + _bcis = get_bcis(backtrace); + _mirrors = get_mirrors(backtrace); + assert(_methods->length() == _bcis->length() && + _methods->length() == _mirrors->length(), + "method and source information arrays should match"); + + // head is the preallocated backtrace + _backtrace = _head = backtrace(); + _index = 0; + } + void expand(TRAPS) { objArrayHandle old_head(THREAD, _head); Pause_No_Safepoint_Verifier pnsv(&_nsv); @@ -1371,10 +1265,10 @@ class BacktraceBuilder: public StackObj { objArrayOop head = oopFactory::new_objectArray(trace_size, CHECK); objArrayHandle new_head(THREAD, head); - typeArrayOop methods = oopFactory::new_metaDataArray(trace_chunk_size, CHECK); + typeArrayOop methods = oopFactory::new_shortArray(trace_chunk_size, CHECK); typeArrayHandle new_methods(THREAD, methods); - typeArrayOop bcis = oopFactory::new_shortArray(trace_chunk_size, CHECK); + typeArrayOop bcis = oopFactory::new_intArray(trace_chunk_size, CHECK); typeArrayHandle new_bcis(THREAD, bcis); objArrayOop mirrors = oopFactory::new_objectArray(trace_chunk_size, CHECK); @@ -1389,7 +1283,7 @@ class BacktraceBuilder: public StackObj { _head = new_head(); _methods = new_methods(); - _bcis = new_bcis(); + _bcis = new_bcis(); _mirrors = new_mirrors(); _index = 0; } @@ -1403,7 +1297,6 @@ class BacktraceBuilder: public StackObj { // shorts. The later line number lookup would just smear the -1 // to a 0 even if it could be recorded. if (bci == SynchronizationEntryBCI) bci = 0; - assert(bci == (jushort)bci, "doesn't fit"); if (_index >= trace_chunk_size) { methodHandle mhandle(THREAD, method); @@ -1411,26 +1304,148 @@ class BacktraceBuilder: public StackObj { method = mhandle(); } - _methods->metadata_at_put(_index, method); - _bcis->ushort_at_put(_index, bci); - // we need to save the mirrors in the backtrace to keep the methods from - // being unloaded if their class loader is unloaded while we still have - // this stack trace. + _methods->short_at_put(_index, method->method_idnum()); + _bcis->int_at_put(_index, merge_bci_and_version(bci, method->constants()->version())); + + // We need to save the mirrors in the backtrace to keep the class + // from being unloaded while we still have this stack trace. + assert(method->method_holder()->java_mirror() != NULL, "never push null for mirror"); _mirrors->obj_at_put(_index, method->method_holder()->java_mirror()); _index++; } - Method* current_method() { - assert(_index >= 0 && _index < trace_chunk_size, "out of range"); - return ((Method*)_methods->metadata_at(_index)); - } - - jushort current_bci() { - assert(_index >= 0 && _index < trace_chunk_size, "out of range"); - return _bcis->ushort_at(_index); - } }; +// Print stack trace element to resource allocated buffer +char* java_lang_Throwable::print_stack_element_to_buffer(Handle mirror, + int method_id, int version, int bci) { + + // Get strings and string lengths + InstanceKlass* holder = InstanceKlass::cast(java_lang_Class::as_Klass(mirror())); + const char* klass_name = holder->external_name(); + int buf_len = (int)strlen(klass_name); + + // pushing to the stack trace added one. + Method* method = holder->method_with_idnum(method_id); + char* method_name = method->name()->as_C_string(); + buf_len += (int)strlen(method_name); + + char* source_file_name = NULL; + if (version_matches(method, version)) { + Symbol* source = holder->source_file_name(); + if (source != NULL) { + source_file_name = source->as_C_string(); + buf_len += (int)strlen(source_file_name); + } + } + + // Allocate temporary buffer with extra space for formatting and line number + char* buf = NEW_RESOURCE_ARRAY(char, buf_len + 64); + + // Print stack trace line in buffer + sprintf(buf, "\tat %s.%s", klass_name, method_name); + + if (!version_matches(method, version)) { + strcat(buf, "(Redefined)"); + } else { + int line_number = get_line_number(method, bci); + if (line_number == -2) { + strcat(buf, "(Native Method)"); + } else { + if (source_file_name != NULL && (line_number != -1)) { + // Sourcename and linenumber + sprintf(buf + (int)strlen(buf), "(%s:%d)", source_file_name, line_number); + } else if (source_file_name != NULL) { + // Just sourcename + sprintf(buf + (int)strlen(buf), "(%s)", source_file_name); + } else { + // Neither sourcename nor linenumber + sprintf(buf + (int)strlen(buf), "(Unknown Source)"); + } + nmethod* nm = method->code(); + if (WizardMode && nm != NULL) { + sprintf(buf + (int)strlen(buf), "(nmethod " INTPTR_FORMAT ")", (intptr_t)nm); + } + } + } + + return buf; +} + +void java_lang_Throwable::print_stack_element(outputStream *st, Handle mirror, + int method_id, int version, int bci) { + ResourceMark rm; + char* buf = print_stack_element_to_buffer(mirror, method_id, version, bci); + st->print_cr("%s", buf); +} + +void java_lang_Throwable::print_stack_element(outputStream *st, methodHandle method, int bci) { + Handle mirror = method->method_holder()->java_mirror(); + int method_id = method->method_idnum(); + int version = method->constants()->version(); + print_stack_element(st, mirror, method_id, version, bci); +} + +const char* java_lang_Throwable::no_stack_trace_message() { + return "\t<>"; +} + + +// Currently used only for exceptions occurring during startup +void java_lang_Throwable::print_stack_trace(oop throwable, outputStream* st) { + Thread *THREAD = Thread::current(); + Handle h_throwable(THREAD, throwable); + while (h_throwable.not_null()) { + objArrayHandle result (THREAD, objArrayOop(backtrace(h_throwable()))); + if (result.is_null()) { + st->print_cr(no_stack_trace_message()); + return; + } + + while (result.not_null()) { + + // Get method id, bci, version and mirror from chunk + typeArrayHandle methods (THREAD, BacktraceBuilder::get_methods(result)); + typeArrayHandle bcis (THREAD, BacktraceBuilder::get_bcis(result)); + objArrayHandle mirrors (THREAD, BacktraceBuilder::get_mirrors(result)); + + int length = methods()->length(); + for (int index = 0; index < length; index++) { + Handle mirror(THREAD, mirrors->obj_at(index)); + // NULL mirror means end of stack trace + if (mirror.is_null()) goto handle_cause; + int method = methods->short_at(index); + int version = version_at(bcis->int_at(index)); + int bci = bci_at(bcis->int_at(index)); + print_stack_element(st, mirror, method, version, bci); + } + result = objArrayHandle(THREAD, objArrayOop(result->obj_at(trace_next_offset))); + } + handle_cause: + { + EXCEPTION_MARK; + JavaValue cause(T_OBJECT); + JavaCalls::call_virtual(&cause, + h_throwable, + KlassHandle(THREAD, h_throwable->klass()), + vmSymbols::getCause_name(), + vmSymbols::void_throwable_signature(), + THREAD); + // Ignore any exceptions. we are in the middle of exception handling. Same as classic VM. + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + h_throwable = Handle(); + } else { + h_throwable = Handle(THREAD, (oop) cause.get_jobject()); + if (h_throwable.not_null()) { + st->print("Caused by: "); + print(h_throwable, st); + st->cr(); + } + } + } + } +} void java_lang_Throwable::fill_in_stack_trace(Handle throwable, methodHandle method, TRAPS) { if (!StackTraceInThrowable) return; @@ -1591,21 +1606,8 @@ void java_lang_Throwable::allocate_backtrace(Handle throwable, TRAPS) { // No-op if stack trace is disabled if (!StackTraceInThrowable) return; - - objArrayOop h_oop = oopFactory::new_objectArray(trace_size, CHECK); - objArrayHandle backtrace (THREAD, h_oop); - typeArrayOop m_oop = oopFactory::new_metaDataArray(trace_chunk_size, CHECK); - typeArrayHandle methods (THREAD, m_oop); - typeArrayOop b = oopFactory::new_shortArray(trace_chunk_size, CHECK); - typeArrayHandle bcis(THREAD, b); - objArrayOop mirror_oop = oopFactory::new_objectArray(trace_chunk_size, CHECK); - objArrayHandle mirrors (THREAD, mirror_oop); - - // backtrace has space for one chunk (next is NULL) - backtrace->obj_at_put(trace_methods_offset, methods()); - backtrace->obj_at_put(trace_bcis_offset, bcis()); - backtrace->obj_at_put(trace_mirrors_offset, mirrors()); - set_backtrace(throwable(), backtrace()); + BacktraceBuilder bt(CHECK); // creates a backtrace + set_backtrace(throwable(), bt.backtrace()); } @@ -1617,48 +1619,26 @@ void java_lang_Throwable::fill_in_stack_trace_of_preallocated_backtrace(Handle t assert(throwable->is_a(SystemDictionary::Throwable_klass()), "sanity check"); - objArrayOop backtrace = (objArrayOop)java_lang_Throwable::backtrace(throwable()); - assert(backtrace != NULL, "backtrace not preallocated"); + JavaThread* THREAD = JavaThread::current(); - oop m = backtrace->obj_at(trace_methods_offset); - typeArrayOop methods = typeArrayOop(m); - assert(methods != NULL && methods->length() > 0, "method array not preallocated"); + objArrayHandle backtrace (THREAD, (objArrayOop)java_lang_Throwable::backtrace(throwable())); + assert(backtrace.not_null(), "backtrace should have been preallocated"); - oop b = backtrace->obj_at(trace_bcis_offset); - typeArrayOop bcis = typeArrayOop(b); - assert(bcis != NULL, "bci array not preallocated"); + ResourceMark rm(THREAD); + vframeStream st(THREAD); - oop mr = backtrace->obj_at(trace_mirrors_offset); - objArrayOop mirrors = objArrayOop(mr); - assert(mirrors != NULL, "bci array not preallocated"); - - assert(methods->length() == bcis->length() && - methods->length() == mirrors->length(), - "method and bci arrays should match"); - - JavaThread* thread = JavaThread::current(); - ResourceMark rm(thread); - vframeStream st(thread); + BacktraceBuilder bt(backtrace); // Unlike fill_in_stack_trace we do not skip fillInStackTrace or throwable init // methods as preallocated errors aren't created by "java" code. // fill in as much stack trace as possible + typeArrayOop methods = BacktraceBuilder::get_methods(backtrace); int max_chunks = MIN2(methods->length(), (int)MaxJavaStackTraceDepth); int chunk_count = 0; for (;!st.at_end(); st.next()) { - // Add entry and smear the -1 bci to 0 since the array only holds - // unsigned shorts. The later line number lookup would just smear - // the -1 to a 0 even if it could be recorded. - int bci = st.bci(); - if (bci == SynchronizationEntryBCI) bci = 0; - assert(bci == (jushort)bci, "doesn't fit"); - bcis->ushort_at_put(chunk_count, bci); - methods->metadata_at_put(chunk_count, st.method()); - mirrors->obj_at_put(chunk_count, - st.method()->method_holder()->java_mirror()); - + bt.push(st.method(), st.bci(), CHECK); chunk_count++; // Bail-out for deep stacks @@ -1672,7 +1652,6 @@ void java_lang_Throwable::fill_in_stack_trace_of_preallocated_backtrace(Handle t java_lang_Throwable::set_stacktrace(throwable(), java_lang_Throwable::unassigned_stacktrace()); assert(java_lang_Throwable::unassigned_stacktrace() != NULL, "not initialized"); } - } @@ -1691,12 +1670,12 @@ int java_lang_Throwable::get_stack_trace_depth(oop throwable, TRAPS) { chunk = next; } assert(chunk != NULL && chunk->obj_at(trace_next_offset) == NULL, "sanity check"); - // Count element in remaining partial chunk - typeArrayOop methods = typeArrayOop(chunk->obj_at(trace_methods_offset)); - typeArrayOop bcis = typeArrayOop(chunk->obj_at(trace_bcis_offset)); - assert(methods != NULL && bcis != NULL, "sanity check"); - for (int i = 0; i < methods->length(); i++) { - if (methods->metadata_at(i) == NULL) break; + // Count element in remaining partial chunk. NULL value for mirror + // marks the end of the stack trace elements that are saved. + objArrayOop mirrors = BacktraceBuilder::get_mirrors(chunk); + assert(mirrors != NULL, "sanity check"); + for (int i = 0; i < mirrors->length(); i++) { + if (mirrors->obj_at(i) == NULL) break; depth++; } } @@ -1722,25 +1701,28 @@ oop java_lang_Throwable::get_stack_trace_element(oop throwable, int index, TRAPS if (chunk == NULL) { THROW_(vmSymbols::java_lang_IndexOutOfBoundsException(), NULL); } - // Get method,bci from chunk - typeArrayOop methods = typeArrayOop(chunk->obj_at(trace_methods_offset)); - typeArrayOop bcis = typeArrayOop(chunk->obj_at(trace_bcis_offset)); - assert(methods != NULL && bcis != NULL, "sanity check"); - methodHandle method(THREAD, ((Method*)methods->metadata_at(chunk_index))); - int bci = bcis->ushort_at(chunk_index); + // Get method id, bci, version and mirror from chunk + typeArrayOop methods = BacktraceBuilder::get_methods(chunk); + typeArrayOop bcis = BacktraceBuilder::get_bcis(chunk); + objArrayOop mirrors = BacktraceBuilder::get_mirrors(chunk); + + assert(methods != NULL && bcis != NULL && mirrors != NULL, "sanity check"); + + int method = methods->short_at(chunk_index); + int version = version_at(bcis->int_at(chunk_index)); + int bci = bci_at(bcis->int_at(chunk_index)); + Handle mirror(THREAD, mirrors->obj_at(chunk_index)); + // Chunk can be partial full - if (method.is_null()) { + if (mirror.is_null()) { THROW_(vmSymbols::java_lang_IndexOutOfBoundsException(), NULL); } - oop element = java_lang_StackTraceElement::create(method, bci, CHECK_0); + oop element = java_lang_StackTraceElement::create(mirror, method, version, bci, CHECK_0); return element; } -oop java_lang_StackTraceElement::create(methodHandle method, int bci, TRAPS) { - // SystemDictionary::stackTraceElement_klass() will be null for pre-1.4 JDKs - assert(JDK_Version::is_gte_jdk14x_version(), "should only be called in >= 1.4"); - +oop java_lang_StackTraceElement::create(Handle mirror, int method_id, int version, int bci, TRAPS) { // Allocate java.lang.StackTraceElement instance Klass* k = SystemDictionary::StackTraceElement_klass(); assert(k != NULL, "must be loaded in 1.4+"); @@ -1752,37 +1734,39 @@ oop java_lang_StackTraceElement::create(methodHandle method, int bci, TRAPS) { Handle element = ik->allocate_instance_handle(CHECK_0); // Fill in class name ResourceMark rm(THREAD); - const char* str = method->method_holder()->external_name(); + InstanceKlass* holder = InstanceKlass::cast(java_lang_Class::as_Klass(mirror())); + const char* str = holder->external_name(); oop classname = StringTable::intern((char*) str, CHECK_0); java_lang_StackTraceElement::set_declaringClass(element(), classname); + // Fill in method name + Method* method = holder->method_with_idnum(method_id); oop methodname = StringTable::intern(method->name(), CHECK_0); java_lang_StackTraceElement::set_methodName(element(), methodname); - // Fill in source file name - Symbol* source = method->method_holder()->source_file_name(); - if (ShowHiddenFrames && source == NULL) - source = vmSymbols::unknown_class_name(); - oop filename = StringTable::intern(source, CHECK_0); - java_lang_StackTraceElement::set_fileName(element(), filename); - // File in source line number - int line_number; - if (method->is_native()) { - // Negative value different from -1 below, enabling Java code in - // class java.lang.StackTraceElement to distinguish "native" from - // "no LineNumberTable". - line_number = -2; - } else { - // Returns -1 if no LineNumberTable, and otherwise actual line number - line_number = method->line_number_from_bci(bci); - if (line_number == -1 && ShowHiddenFrames) { - line_number = bci + 1000000; - } - } - java_lang_StackTraceElement::set_lineNumber(element(), line_number); + if (!version_matches(method, version)) { + // The method was redefined, accurate line number information isn't available + java_lang_StackTraceElement::set_fileName(element(), NULL); + java_lang_StackTraceElement::set_lineNumber(element(), -1); + } else { + // Fill in source file name and line number. + Symbol* source = holder->source_file_name(); + if (ShowHiddenFrames && source == NULL) + source = vmSymbols::unknown_class_name(); + oop filename = StringTable::intern(source, CHECK_0); + java_lang_StackTraceElement::set_fileName(element(), filename); + + int line_number = get_line_number(method, bci); + java_lang_StackTraceElement::set_lineNumber(element(), line_number); + } return element(); } +oop java_lang_StackTraceElement::create(methodHandle method, int bci, TRAPS) { + Handle mirror (THREAD, method->method_holder()->java_mirror()); + int method_id = method->method_idnum(); + return create(mirror, method_id, method->constants()->version(), bci, THREAD); +} void java_lang_reflect_AccessibleObject::compute_offsets() { Klass* k = SystemDictionary::reflect_AccessibleObject_klass(); diff --git a/hotspot/src/share/vm/classfile/javaClasses.hpp b/hotspot/src/share/vm/classfile/javaClasses.hpp index 5ab16251c10..51ee95da145 100644 --- a/hotspot/src/share/vm/classfile/javaClasses.hpp +++ b/hotspot/src/share/vm/classfile/javaClasses.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -469,8 +469,7 @@ class java_lang_Throwable: AllStatic { static int static_unassigned_stacktrace_offset; // Printing - static char* print_stack_element_to_buffer(Method* method, int bci); - static void print_to_stream(Handle stream, const char* str); + static char* print_stack_element_to_buffer(Handle mirror, int method, int version, int bci); // StackTrace (programmatic access, new since 1.4) static void clear_stacktrace(oop throwable); // No stack trace available @@ -490,12 +489,9 @@ class java_lang_Throwable: AllStatic { static oop message(oop throwable); static oop message(Handle throwable); static void set_message(oop throwable, oop value); - // Print stack trace stored in exception by call-back to Java - // Note: this is no longer used in Merlin, but we still suppport - // it for compatibility. - static void print_stack_trace(oop throwable, oop print_stream); - static void print_stack_element(Handle stream, Method* method, int bci); - static void print_stack_element(outputStream *st, Method* method, int bci); + static void print_stack_element(outputStream *st, Handle mirror, int method, + int version, int bci); + static void print_stack_element(outputStream *st, methodHandle method, int bci); static void print_stack_usage(Handle stream); // Allocate space for backtrace (created but stack trace not filled in) @@ -1263,7 +1259,8 @@ class java_lang_StackTraceElement: AllStatic { static void set_lineNumber(oop element, int value); // Create an instance of StackTraceElement - static oop create(methodHandle m, int bci, TRAPS); + static oop create(Handle mirror, int method, int version, int bci, TRAPS); + static oop create(methodHandle method, int bci, TRAPS); // Debugging friend class JavaClasses; diff --git a/hotspot/src/share/vm/oops/constantPool.cpp b/hotspot/src/share/vm/oops/constantPool.cpp index ff8472d3d79..770510c7880 100644 --- a/hotspot/src/share/vm/oops/constantPool.cpp +++ b/hotspot/src/share/vm/oops/constantPool.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -66,7 +66,7 @@ ConstantPool::ConstantPool(Array* tags) { set_pool_holder(NULL); set_flags(0); // only set to non-zero if constant pool is merged by RedefineClasses - set_orig_length(0); + set_version(0); set_lock(new Monitor(Monitor::nonleaf + 2, "A constant pool lock")); // all fields are initialized; needed for GC set_on_stack(false); diff --git a/hotspot/src/share/vm/oops/constantPool.hpp b/hotspot/src/share/vm/oops/constantPool.hpp index 768cd39a65c..3872dfb81e3 100644 --- a/hotspot/src/share/vm/oops/constantPool.hpp +++ b/hotspot/src/share/vm/oops/constantPool.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -103,8 +103,8 @@ class ConstantPool : public Metadata { union { // set for CDS to restore resolved references int _resolved_reference_length; - // only set to non-zero if constant pool is merged by RedefineClasses - int _orig_length; + // keeps version number for redefined classes (used in backtrace) + int _version; } _saved; Monitor* _lock; @@ -784,8 +784,11 @@ class ConstantPool : public Metadata { static void copy_cp_to_impl(constantPoolHandle from_cp, int start_i, int end_i, constantPoolHandle to_cp, int to_i, TRAPS); static void copy_entry_to(constantPoolHandle from_cp, int from_i, constantPoolHandle to_cp, int to_i, TRAPS); int find_matching_entry(int pattern_i, constantPoolHandle search_cp, TRAPS); - int orig_length() const { return _saved._orig_length; } - void set_orig_length(int orig_length) { _saved._orig_length = orig_length; } + int version() const { return _saved._version; } + void set_version(int version) { _saved._version = version; } + void increment_and_save_version(int version) { + _saved._version = version >= 0 ? version++ : version; // keep overflow + } void set_resolved_reference_length(int length) { _saved._resolved_reference_length = length; } int resolved_reference_length() const { return _saved._resolved_reference_length; } diff --git a/hotspot/src/share/vm/prims/jvm.cpp b/hotspot/src/share/vm/prims/jvm.cpp index 8b7b2583c5f..9b15947a7f1 100644 --- a/hotspot/src/share/vm/prims/jvm.cpp +++ b/hotspot/src/share/vm/prims/jvm.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -484,15 +484,6 @@ JVM_ENTRY(void, JVM_FillInStackTrace(JNIEnv *env, jobject receiver)) JVM_END -JVM_ENTRY(void, JVM_PrintStackTrace(JNIEnv *env, jobject receiver, jobject printable)) - JVMWrapper("JVM_PrintStackTrace"); - // Note: This is no longer used in Merlin, but we still support it for compatibility. - oop exception = JNIHandles::resolve_non_null(receiver); - oop stream = JNIHandles::resolve_non_null(printable); - java_lang_Throwable::print_stack_trace(exception, stream); -JVM_END - - JVM_ENTRY(jint, JVM_GetStackTraceDepth(JNIEnv *env, jobject throwable)) JVMWrapper("JVM_GetStackTraceDepth"); oop exception = JNIHandles::resolve(throwable); diff --git a/hotspot/src/share/vm/prims/jvm.h b/hotspot/src/share/vm/prims/jvm.h index 899138ede9e..c081f872afa 100644 --- a/hotspot/src/share/vm/prims/jvm.h +++ b/hotspot/src/share/vm/prims/jvm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -212,9 +212,6 @@ JVM_IsNaN(jdouble d); JNIEXPORT void JNICALL JVM_FillInStackTrace(JNIEnv *env, jobject throwable); -JNIEXPORT void JNICALL -JVM_PrintStackTrace(JNIEnv *env, jobject throwable, jobject printable); - JNIEXPORT jint JNICALL JVM_GetStackTraceDepth(JNIEnv *env, jobject throwable); diff --git a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp index a9d3d5f1e39..7e68f2b1059 100644 --- a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp +++ b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1334,20 +1334,8 @@ jvmtiError VM_RedefineClasses::merge_cp_and_rewrite( return JVMTI_ERROR_INTERNAL; } - int orig_length = old_cp->orig_length(); - if (orig_length == 0) { - // This old_cp is an actual original constant pool. We save - // the original length in the merged constant pool so that - // merge_constant_pools() can be more efficient. If a constant - // pool has a non-zero orig_length() value, then that constant - // pool was created by a merge operation in RedefineClasses. - merge_cp->set_orig_length(old_cp->length()); - } else { - // This old_cp is a merged constant pool from a previous - // RedefineClasses() calls so just copy the orig_length() - // value. - merge_cp->set_orig_length(old_cp->orig_length()); - } + // Update the version number of the constant pool + merge_cp->increment_and_save_version(old_cp->version()); ResourceMark rm(THREAD); _index_map_count = 0; @@ -2417,18 +2405,19 @@ void VM_RedefineClasses::set_new_constant_pool( int scratch_cp_length, TRAPS) { assert(scratch_cp->length() >= scratch_cp_length, "sanity check"); - // scratch_cp is a merged constant pool and has enough space for a - // worst case merge situation. We want to associate the minimum - // sized constant pool with the klass to save space. - constantPoolHandle smaller_cp(THREAD, - ConstantPool::allocate(loader_data, scratch_cp_length, - THREAD)); - // preserve orig_length() value in the smaller copy - int orig_length = scratch_cp->orig_length(); - assert(orig_length != 0, "sanity check"); - smaller_cp->set_orig_length(orig_length); - scratch_cp->copy_cp_to(1, scratch_cp_length - 1, smaller_cp, 1, THREAD); - scratch_cp = smaller_cp; + // scratch_cp is a merged constant pool and has enough space for a + // worst case merge situation. We want to associate the minimum + // sized constant pool with the klass to save space. + constantPoolHandle smaller_cp(THREAD, + ConstantPool::allocate(loader_data, scratch_cp_length, THREAD)); + + // preserve version() value in the smaller copy + int version = scratch_cp->version(); + assert(version != 0, "sanity check"); + smaller_cp->set_version(version); + + scratch_cp->copy_cp_to(1, scratch_cp_length - 1, smaller_cp, 1, THREAD); + scratch_cp = smaller_cp; // attach new constant pool to klass scratch_cp->set_pool_holder(scratch_class()); From fe16fc39d621ef68aa83b12c0db80b5d57f74baf Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Thu, 17 Jan 2013 12:49:33 -0800 Subject: [PATCH 060/138] 8006090: Formatter asserts with -esa Removed the offending assert Reviewed-by: alanb, darcy --- jdk/src/share/classes/java/util/Formatter.java | 1 - jdk/test/ProblemList.txt | 3 --- 2 files changed, 4 deletions(-) diff --git a/jdk/src/share/classes/java/util/Formatter.java b/jdk/src/share/classes/java/util/Formatter.java index 59056d0d1be..c13e4ea3ed5 100644 --- a/jdk/src/share/classes/java/util/Formatter.java +++ b/jdk/src/share/classes/java/util/Formatter.java @@ -3827,7 +3827,6 @@ public final class Formatter implements Closeable, Flushable { Locale l) throws IOException { - assert(width == -1); if (sb == null) sb = new StringBuilder(); switch (c) { diff --git a/jdk/test/ProblemList.txt b/jdk/test/ProblemList.txt index 6813cce7aeb..7b763477192 100644 --- a/jdk/test/ProblemList.txt +++ b/jdk/test/ProblemList.txt @@ -347,9 +347,6 @@ com/sun/jdi/ProcessAttachTest.sh generic-all # jdk_util -# 8006090 -java/util/Formatter/Basic.java generic-all - # Filed 6933803 java/util/concurrent/ThreadPoolExecutor/CoreThreadTimeOut.java generic-all From 8f2bd71dc9244a82dffd14a208b7d83523a34732 Mon Sep 17 00:00:00 2001 From: Kurchi Subhra Hazra Date: Thu, 17 Jan 2013 14:50:02 -0800 Subject: [PATCH 061/138] 7171415: java.net.URI.equals/hashCode not consistent for some URIs Rewrite URI.hashCode() to consider encoded characters, also reviewed by vitalyd@gmail.com, schlosna@gmail.com Reviewed-by: chegar --- jdk/src/share/classes/java/net/URI.java | 28 ++++++++++++++++++++++++- jdk/test/java/net/URI/Test.java | 25 ++++++++++++---------- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/jdk/src/share/classes/java/net/URI.java b/jdk/src/share/classes/java/net/URI.java index 6997a427467..b4b22ded16b 100644 --- a/jdk/src/share/classes/java/net/URI.java +++ b/jdk/src/share/classes/java/net/URI.java @@ -1694,6 +1694,13 @@ public final class URI return c; } + // US-ASCII only + private static int toUpper(char c) { + if ((c >= 'a') && (c <= 'z')) + return c - ('a' - 'A'); + return c; + } + private static boolean equal(String s, String t) { if (s == t) return true; if ((s != null) && (t != null)) { @@ -1744,7 +1751,26 @@ public final class URI private static int hash(int hash, String s) { if (s == null) return hash; - return hash * 127 + s.hashCode(); + return s.indexOf('%') < 0 ? hash * 127 + s.hashCode() + : normalizedHash(hash, s); + } + + + private static int normalizedHash(int hash, String s) { + int h = 0; + for (int index = 0; index < s.length(); index++) { + char ch = s.charAt(index); + h = 31 * h + ch; + if (ch == '%') { + /* + * Process the next two encoded characters + */ + for (int i = index + 1; i < index + 3; i++) + h = 31 * h + toUpper(s.charAt(i)); + index += 2; + } + } + return hash * 127 + h; } // US-ASCII only diff --git a/jdk/test/java/net/URI/Test.java b/jdk/test/java/net/URI/Test.java index b24bedf19f3..dfc01c9d388 100644 --- a/jdk/test/java/net/URI/Test.java +++ b/jdk/test/java/net/URI/Test.java @@ -24,6 +24,7 @@ /* @test * @summary Unit test for java.net.URI * @bug 4464135 4505046 4503239 4438319 4991359 4866303 7023363 7041800 + * 7171415 * @author Mark Reinhold */ @@ -1337,7 +1338,7 @@ public class Test { } - static void eq0(Comparable u, Comparable v) throws URISyntaxException { + static void eq0(URI u, URI v) throws URISyntaxException { testCount++; if (!u.equals(v)) throw new RuntimeException("Not equal: " + u + " " + v); @@ -1352,7 +1353,7 @@ public class Test { + " [" + Integer.toHexString(uh) + "]"); } - static void cmp0(Comparable u, Comparable v, boolean same) + static void cmp0(URI u, URI v, boolean same) throws URISyntaxException { int c = u.compareTo(v); @@ -1361,18 +1362,18 @@ public class Test { + " " + c); } - static void eq(Comparable u, Comparable v) throws URISyntaxException { + static void eq(URI u, URI v) throws URISyntaxException { eq0(u, v); cmp0(u, v, true); } - static void eqeq(Comparable u, Comparable v) { + static void eqeq(URI u, URI v) { testCount++; if (u != v) throw new RuntimeException("Not ==: " + u + " " + v); } - static void ne0(Comparable u, Comparable v) throws URISyntaxException { + static void ne0(URI u, URI v) throws URISyntaxException { testCount++; if (u.equals(v)) throw new RuntimeException("Equal: " + u + " " + v); @@ -1383,17 +1384,17 @@ public class Test { + "]"); } - static void ne(Comparable u, Comparable v) throws URISyntaxException { + static void ne(URI u, URI v) throws URISyntaxException { ne0(u, v); cmp0(u, v, false); } - static void lt(Comparable u, Comparable v) throws URISyntaxException { + static void lt(URI u, URI v) throws URISyntaxException { ne0(u, v); int c = u.compareTo(v); if (c >= 0) { - show((URI)u); - show((URI)v); + show(u); + show(v); throw new RuntimeException("Not less than: " + u + " " + v + " " + c); } @@ -1404,7 +1405,7 @@ public class Test { lt(new URI(s), new URI(t)); } - static void gt(Comparable u, Comparable v) throws URISyntaxException { + static void gt(URI u, URI v) throws URISyntaxException { lt(v, u); } @@ -1430,6 +1431,8 @@ public class Test { lt(s, new URI("http://jag:cafebabe@java.sun.com:94/b/c/d?q#g")); eq(new URI("http://host/a%00bcd"), new URI("http://host/a%00bcd")); ne(new URI("http://host/a%00bcd"), new URI("http://host/aZ00bcd")); + eq0(new URI("http://host/abc%e2def%C3ghi"), + new URI("http://host/abc%E2def%c3ghi")); lt("p", "s:p"); lt("s:p", "T:p"); @@ -1465,7 +1468,7 @@ public class Test { ObjectInputStream oi = new ObjectInputStream(bi); try { Object o = oi.readObject(); - eq(u, (Comparable)o); + eq(u, (URI)o); } catch (ClassNotFoundException x) { x.printStackTrace(); throw new RuntimeException(x.toString()); From a7f43eaad5c3fbaf2d2c8ac183972429a11c2310 Mon Sep 17 00:00:00 2001 From: Jim Gish Date: Thu, 17 Jan 2013 15:09:46 -0500 Subject: [PATCH 062/138] 8006534: CLONE - TestLibrary.getUnusedRandomPort() fails intermittently-doesn't retry enough times Increase number of retries to twice the number of ports in the reserved range Reviewed-by: mduigou --- jdk/test/java/rmi/testlibrary/TestLibrary.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jdk/test/java/rmi/testlibrary/TestLibrary.java b/jdk/test/java/rmi/testlibrary/TestLibrary.java index 07ed308355c..10b84c639b1 100644 --- a/jdk/test/java/rmi/testlibrary/TestLibrary.java +++ b/jdk/test/java/rmi/testlibrary/TestLibrary.java @@ -93,7 +93,7 @@ public class TestLibrary { public final static int INHERITEDCHANNELNOTSERVERSOCKET_ACTIVATION_PORT = 64003; public final static int INHERITEDCHANNELNOTSERVERSOCKET_REGISTRY_PORT = 64004; public final static int READTEST_REGISTRY_PORT = 64005; - private final static int MAX_SERVER_SOCKET_TRIES = 10; + private final static int MAX_SERVER_SOCKET_TRIES = 2*(FIXED_PORT_MAX-FIXED_PORT_MIN+1); static void mesg(Object mesg) { System.err.println("TEST_LIBRARY: " + mesg.toString()); From 59f020900809ea97e9af7ced48c1f1f712ee1268 Mon Sep 17 00:00:00 2001 From: Jon Masamitsu Date: Thu, 17 Jan 2013 19:04:48 -0800 Subject: [PATCH 063/138] 8006537: Assert when dumping archive with default methods Reviewed-by: coleenp --- hotspot/src/share/vm/classfile/classLoaderData.cpp | 1 + hotspot/src/share/vm/memory/metadataFactory.hpp | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/hotspot/src/share/vm/classfile/classLoaderData.cpp b/hotspot/src/share/vm/classfile/classLoaderData.cpp index 40c397de887..22cc8cf9bb1 100644 --- a/hotspot/src/share/vm/classfile/classLoaderData.cpp +++ b/hotspot/src/share/vm/classfile/classLoaderData.cpp @@ -318,6 +318,7 @@ ClassLoaderData::~ClassLoaderData() { } Metaspace* ClassLoaderData::metaspace_non_null() { + assert(!DumpSharedSpaces, "wrong metaspace!"); // If the metaspace has not been allocated, create a new one. Might want // to create smaller arena for Reflection class loaders also. // The reason for the delayed allocation is because some class loaders are diff --git a/hotspot/src/share/vm/memory/metadataFactory.hpp b/hotspot/src/share/vm/memory/metadataFactory.hpp index 3c4689c479a..fce8d9b34d7 100644 --- a/hotspot/src/share/vm/memory/metadataFactory.hpp +++ b/hotspot/src/share/vm/memory/metadataFactory.hpp @@ -66,7 +66,11 @@ class MetadataFactory : AllStatic { if (data != NULL) { assert(loader_data != NULL, "shouldn't pass null"); int size = data->size(); - loader_data->metaspace_non_null()->deallocate((MetaWord*)data, size, false); + if (DumpSharedSpaces) { + loader_data->ro_metaspace()->deallocate((MetaWord*)data, size, false); + } else { + loader_data->metaspace_non_null()->deallocate((MetaWord*)data, size, false); + } } } @@ -77,6 +81,7 @@ class MetadataFactory : AllStatic { assert(loader_data != NULL, "shouldn't pass null"); int size = md->size(); // Call metadata's deallocate function which will call deallocate fields + assert(!DumpSharedSpaces, "cannot deallocate metadata when dumping CDS archive"); assert(!md->on_stack(), "can't deallocate things on stack"); md->deallocate_contents(loader_data); loader_data->metaspace_non_null()->deallocate((MetaWord*)md, size, md->is_klass()); From 833b7fcfff80ba06c7e6ace5aefde4329baabfda Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Thu, 17 Jan 2013 22:11:57 -0500 Subject: [PATCH 064/138] 8006548: version wrong in new constantPool code Fix increment problem with saved_version Reviewed-by: dholmes --- hotspot/src/share/vm/oops/constantPool.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hotspot/src/share/vm/oops/constantPool.hpp b/hotspot/src/share/vm/oops/constantPool.hpp index 3872dfb81e3..65546021553 100644 --- a/hotspot/src/share/vm/oops/constantPool.hpp +++ b/hotspot/src/share/vm/oops/constantPool.hpp @@ -787,7 +787,7 @@ class ConstantPool : public Metadata { int version() const { return _saved._version; } void set_version(int version) { _saved._version = version; } void increment_and_save_version(int version) { - _saved._version = version >= 0 ? version++ : version; // keep overflow + _saved._version = version >= 0 ? (version + 1) : version; // keep overflow } void set_resolved_reference_length(int length) { _saved._resolved_reference_length = length; } From 04fc62c5ed1586ba8cc1ac5ef4fd9d814d523473 Mon Sep 17 00:00:00 2001 From: Anthony Petrov Date: Fri, 18 Jan 2013 14:17:11 +0400 Subject: [PATCH 065/138] 8005465: [macosx] Evaluate if checking for the -XstartOnFirstThread is still needed in awt.m Allow one to start AWT on the main thread w/o exceptions Reviewed-by: art, serb --- jdk/src/macosx/native/sun/awt/awt.m | 157 +++++++++++++--------------- 1 file changed, 73 insertions(+), 84 deletions(-) diff --git a/jdk/src/macosx/native/sun/awt/awt.m b/jdk/src/macosx/native/sun/awt/awt.m index 8ca56bd117c..bf8705f039e 100644 --- a/jdk/src/macosx/native/sun/awt/awt.m +++ b/jdk/src/macosx/native/sun/awt/awt.m @@ -42,6 +42,15 @@ // The symbol is defined in libosxapp.dylib (ThreadUtilities.m) extern JavaVM *jvm; +// Indicates if AWT is running embedded (in SWT, FX, elsewhere) +static BOOL isEmbedded = NO; + +// Indicates that the app has been started with -XstartOnFirstThread +// (directly or via WebStart settings), and AWT should not run its +// own event loop in this mode. Even if a loop isn't running yet, +// we expect an embedder (e.g. SWT) to start it some time later. +static BOOL forceEmbeddedMode = NO; + static bool ShouldPrintVerboseDebugging() { static int debug = -1; if (debug == -1) { @@ -63,31 +72,29 @@ static void AWT_NSUncaughtExceptionHandler(NSException *exception); static CFRunLoopObserverRef busyObserver = NULL; static CFRunLoopObserverRef notBusyObserver = NULL; -static void setUpAWTAppKit(BOOL swt_mode, BOOL headless) { -AWT_ASSERT_APPKIT_THREAD; +static void setUpAWTAppKit() +{ BOOL verbose = ShouldPrintVerboseDebugging(); if (verbose) AWT_DEBUG_LOG(@"setting up busy observers"); - JNIEnv *env = [ThreadUtilities getJNIEnv]; - // Add CFRunLoopObservers to call into AWT so that AWT knows that the // AWT thread (which is the AppKit main thread) is alive. This way AWT // will not automatically shutdown. busyObserver = CFRunLoopObserverCreate( - NULL, // CFAllocator - kCFRunLoopAfterWaiting, // CFOptionFlags - true, // repeats - NSIntegerMax, // order - &BusyObserver, // CFRunLoopObserverCallBack - NULL); // CFRunLoopObserverContext + NULL, // CFAllocator + kCFRunLoopAfterWaiting, // CFOptionFlags + true, // repeats + NSIntegerMax, // order + &BusyObserver, // CFRunLoopObserverCallBack + NULL); // CFRunLoopObserverContext notBusyObserver = CFRunLoopObserverCreate( - NULL, // CFAllocator - kCFRunLoopBeforeWaiting, // CFOptionFlags - true, // repeats - NSIntegerMin, // order - &NotBusyObserver, // CFRunLoopObserverCallBack - NULL); // CFRunLoopObserverContext + NULL, // CFAllocator + kCFRunLoopBeforeWaiting, // CFOptionFlags + true, // repeats + NSIntegerMin, // order + &NotBusyObserver, // CFRunLoopObserverCallBack + NULL); // CFRunLoopObserverContext CFRunLoopRef runLoop = [[NSRunLoop currentRunLoop] getCFRunLoop]; CFRunLoopAddObserver(runLoop, busyObserver, kCFRunLoopDefaultMode); @@ -95,29 +102,33 @@ AWT_ASSERT_APPKIT_THREAD; CFRelease(busyObserver); CFRelease(notBusyObserver); - - if (!headless) setBusy(YES); + + setBusy(YES); +} + +static void setUpAppKitThreadName() +{ + BOOL verbose = ShouldPrintVerboseDebugging(); + JNIEnv *env = [ThreadUtilities getJNIEnv]; // Set the java name of the AppKit main thread appropriately. jclass threadClass = NULL; jstring name = NULL; jobject curThread = NULL; - if (!swt_mode) { - threadClass = (*env)->FindClass(env, "java/lang/Thread"); - if (threadClass == NULL || (*env)->ExceptionCheck(env)) goto cleanup; - jmethodID currentThreadID = (*env)->GetStaticMethodID(env, threadClass, "currentThread", "()Ljava/lang/Thread;"); - if (currentThreadID == NULL || (*env)->ExceptionCheck(env)) goto cleanup; - jmethodID setName = (*env)->GetMethodID(env, threadClass, "setName", "(Ljava/lang/String;)V"); - if (setName == NULL || (*env)->ExceptionCheck(env)) goto cleanup; + threadClass = (*env)->FindClass(env, "java/lang/Thread"); + if (threadClass == NULL || (*env)->ExceptionCheck(env)) goto cleanup; + jmethodID currentThreadID = (*env)->GetStaticMethodID(env, threadClass, "currentThread", "()Ljava/lang/Thread;"); + if (currentThreadID == NULL || (*env)->ExceptionCheck(env)) goto cleanup; + jmethodID setName = (*env)->GetMethodID(env, threadClass, "setName", "(Ljava/lang/String;)V"); + if (setName == NULL || (*env)->ExceptionCheck(env)) goto cleanup; - curThread = (*env)->CallStaticObjectMethod(env, threadClass, currentThreadID); // AWT_THREADING Safe (known object) - if (curThread == NULL || (*env)->ExceptionCheck(env)) goto cleanup; - name = (*env)->NewStringUTF(env, "AWT-AppKit"); - if (name == NULL || (*env)->ExceptionCheck(env)) goto cleanup; - (*env)->CallVoidMethod(env, curThread, setName, name); // AWT_THREADING Safe (known object) - if ((*env)->ExceptionCheck(env)) goto cleanup; - } + curThread = (*env)->CallStaticObjectMethod(env, threadClass, currentThreadID); // AWT_THREADING Safe (known object) + if (curThread == NULL || (*env)->ExceptionCheck(env)) goto cleanup; + name = (*env)->NewStringUTF(env, "AWT-AppKit"); + if (name == NULL || (*env)->ExceptionCheck(env)) goto cleanup; + (*env)->CallVoidMethod(env, curThread, setName, name); // AWT_THREADING Safe (known object) + if ((*env)->ExceptionCheck(env)) goto cleanup; cleanup: if (threadClass != NULL) { @@ -134,14 +145,10 @@ cleanup: (*env)->ExceptionClear(env); } - // Add the exception handler of last resort - NSSetUncaughtExceptionHandler(AWT_NSUncaughtExceptionHandler); - if (verbose) AWT_DEBUG_LOG(@"finished setting thread name"); } - // Returns true if java believes it is running headless BOOL isHeadless(JNIEnv *env) { // Just access the property directly, instead of using GraphicsEnvironment.isHeadless. @@ -200,7 +207,7 @@ static void AWT_NSUncaughtExceptionHandler(NSException *exception) { // This is an empty Obj-C object just so that -peformSelectorOnMainThread can be used. @interface AWTStarter : NSObject { } -+ (void)start:(BOOL)headless swtMode:(BOOL)swtMode swtModeForWebStart:(BOOL)swtModeForWebStart; ++ (void)start:(BOOL)headless; - (void)starter:(NSArray*)args; + (void)appKitIsRunning:(id)arg; @end @@ -242,7 +249,7 @@ AWT_ASSERT_APPKIT_THREAD; if (verbose) AWT_DEBUG_LOG(@"finished messaging AppKit started"); } -+ (void)start:(BOOL)headless swtMode:(BOOL)swtMode swtModeForWebStart:(BOOL)swtModeForWebStart ++ (void)start:(BOOL)headless { BOOL verbose = ShouldPrintVerboseDebugging(); @@ -258,7 +265,7 @@ AWT_ASSERT_APPKIT_THREAD; BOOL onMainThread = (pthread_main_np() != 0); if (verbose) { - NSString *msg = [NSString stringWithFormat:@"+[AWTStarter start headless:%d swtMode:%d swtModeForWebStart:%d] { onMainThread:%d }", headless, swtMode, swtModeForWebStart, onMainThread]; + NSString *msg = [NSString stringWithFormat:@"+[AWTStarter start headless:%d] { onMainThread:%d }", headless, onMainThread]; AWT_DEBUG_LOG(msg); } @@ -280,9 +287,7 @@ AWT_ASSERT_APPKIT_THREAD; NSArray * args = [NSArray arrayWithObjects: [NSNumber numberWithBool: onMainThread], - [NSNumber numberWithBool: swtMode], [NSNumber numberWithBool: headless], - [NSNumber numberWithBool: swtModeForWebStart], [NSNumber numberWithBool: verbose], nil]; @@ -324,25 +329,29 @@ AWT_ASSERT_APPKIT_THREAD; NSAutoreleasePool *pool = [NSAutoreleasePool new]; BOOL onMainThread = [[args objectAtIndex:0] boolValue]; - BOOL swtMode = [[args objectAtIndex:1] boolValue]; - BOOL headless = [[args objectAtIndex:2] boolValue]; - BOOL swtModeForWebStart = [[args objectAtIndex:3] boolValue]; - BOOL verbose = [[args objectAtIndex:4] boolValue]; + BOOL headless = [[args objectAtIndex:1] boolValue]; + BOOL verbose = [[args objectAtIndex:2] boolValue]; BOOL wasOnMainThread = onMainThread; - setUpAWTAppKit(swtMode, headless); + // Add the exception handler of last resort + NSSetUncaughtExceptionHandler(AWT_NSUncaughtExceptionHandler); // Headless mode trumps either ordinary AWT or SWT-in-AWT mode. Declare us a daemon and return. if (headless) { - BOOL didBecomeDaemon = [AWTStarter markAppAsDaemon]; + if (!forceEmbeddedMode) { + setUpAppKitThreadName(); + } + [AWTStarter markAppAsDaemon]; return; } - if (swtMode || swtModeForWebStart) { + if (forceEmbeddedMode) { if (verbose) NSLog(@"in SWT or SWT/WebStart mode"); - // The SWT should call NSApplicationLoad, but they don't know a priori that they will be using the AWT, so they don't. + // Init a default NSApplication instance instead of the NSApplicationAWT. + // Note that [NSApp isRunning] will return YES after that, though + // this behavior isn't specified anywhere. We rely on that. NSApplicationLoad(); } @@ -351,6 +360,13 @@ AWT_ASSERT_APPKIT_THREAD; // and -[NSApplication isRunning] returns YES, AWT is embedded inside another // AppKit Application. NSApplication *app = [NSApplicationAWT sharedApplication]; + isEmbedded = ![NSApp isKindOfClass:[NSApplicationAWT class]]; + + if (!isEmbedded) { + // Install run loop observers and set the AppKit Java thread name + setUpAWTAppKit(); + setUpAppKitThreadName(); + } // AWT gets to this point BEFORE NSApplicationDidFinishLaunchingNotification is sent. if (![app isRunning]) { @@ -360,17 +376,11 @@ AWT_ASSERT_APPKIT_THREAD; [NSApplicationAWT runAWTLoopWithApp: app]; } else { // We're either embedded, or showing a splash screen - if (![NSApp isKindOfClass:[NSApplicationAWT class]]) { + if (isEmbedded) { if (verbose) AWT_DEBUG_LOG(@"running embedded"); - // Since we're embedded, no need to be swamping the runloop with the observers. - CFRunLoopRef runLoop = [[NSRunLoop currentRunLoop] getCFRunLoop]; - CFRunLoopRemoveObserver(runLoop, busyObserver, kCFRunLoopDefaultMode); - CFRunLoopRemoveObserver(runLoop, notBusyObserver, kCFRunLoopDefaultMode); // We don't track if the runloop is busy, so set it free to let AWT finish when it needs setBusy(NO); - busyObserver = NULL; - notBusyObserver = NULL; } else { if (verbose) AWT_DEBUG_LOG(@"running after showing a splash screen"); } @@ -408,49 +418,28 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { return JNI_VERSION_1_4; } - // The following is true when AWT is attempting to connect to the window server - // when it isn't set up properly to do so. - // BOOL AWTLoadFailure = YES; For now we are skipping this check so i'm commenting out this variable as unused JNF_COCOA_ENTER(env); - // If -XstartOnFirstThread was used at invocation time, an environment variable will be set. - // (See java_md.c for the matching setenv call.) When that happens, we assume the SWT will be in use. - BOOL swt_compatible_mode = NO; - + // Launcher sets this env variable if -XstartOnFirstThread is specified char envVar[80]; snprintf(envVar, sizeof(envVar), "JAVA_STARTED_ON_FIRST_THREAD_%d", getpid()); if (getenv(envVar) != NULL) { - swt_compatible_mode = YES; + forceEmbeddedMode = YES; unsetenv(envVar); } - BOOL swt_in_webstart = isSWTInWebStart(env); - BOOL headless = isHeadless(env); - - // Make sure we're on the right thread. If not, we still need the JNIEnv to throw an exception. - if (pthread_main_np() != 0 && !swt_compatible_mode && !headless) { - AWT_DEBUG_LOG(@"Apple AWT Java VM was loaded on first thread -- can't start AWT."); - [JNFException raise:env as:kInternalError reason:"Can't start the AWT because Java was started on the first thread. Make sure StartOnFirstThread is " - "not specified in your application's Info.plist or on the command line"]; - return JNI_VERSION_1_4; + if (isSWTInWebStart(env)) { + forceEmbeddedMode = YES; } + BOOL headless = isHeadless(env); + // We need to let Foundation know that this is a multithreaded application, if it isn't already. if (![NSThread isMultiThreaded]) { [NSThread detachNewThreadSelector:nil toTarget:nil withObject:nil]; } -// if (swt_compatible_mode || headless || [AWTStarter isConnectedToWindowServer] || [AWTStarter isRemoteSession]) { -// No need in this check - we will try to launch AWTStarter anyways - to be able to run GUI application remotely -// AWTLoadFailure = NO; - - [AWTStarter start:headless swtMode:swt_compatible_mode swtModeForWebStart:swt_in_webstart]; - -// } - -/* if (AWTLoadFailure) { // We will not reach this code anyways - [JNFException raise:env as:kInternalError reason:"Can't connect to window server - not enough permissions."]; - } */ + [AWTStarter start:headless]; JNF_COCOA_EXIT(env); From ab8570e77f9fb331334159a96af6899029e5ba45 Mon Sep 17 00:00:00 2001 From: Alejandro Murillo Date: Fri, 18 Jan 2013 05:19:07 -0800 Subject: [PATCH 066/138] Added tag hs25-b16 for changeset bf71fcc9d682 --- hotspot/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/hotspot/.hgtags b/hotspot/.hgtags index 617e00e138a..e0c5d99132b 100644 --- a/hotspot/.hgtags +++ b/hotspot/.hgtags @@ -308,3 +308,4 @@ e94068d4ff52849c8aa0786a53a59b63d1312a39 jdk8-b70 d5cb5830f570d1304ea4b196dde672a291b55f29 jdk8-b72 1e129851479e4f5df439109fca2c7be1f1613522 hs25-b15 11619f33cd683c2f1d6ef72f1c6ff3dacf5a9f1c jdk8-b73 +70c89bd6b895a10d25ca70e08093c09ff2005fda hs25-b16 From 1c9730cfb8cdcc2ccaa3ebaae88cdd83cc78e86d Mon Sep 17 00:00:00 2001 From: Alejandro Murillo Date: Fri, 18 Jan 2013 05:33:32 -0800 Subject: [PATCH 067/138] 8006511: new hotspot build - hs25-b17 Reviewed-by: jcoomes --- hotspot/make/hotspot_version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hotspot/make/hotspot_version b/hotspot/make/hotspot_version index 946cb5c1aa8..c47d3d18289 100644 --- a/hotspot/make/hotspot_version +++ b/hotspot/make/hotspot_version @@ -35,7 +35,7 @@ HOTSPOT_VM_COPYRIGHT=Copyright 2013 HS_MAJOR_VER=25 HS_MINOR_VER=0 -HS_BUILD_NUMBER=16 +HS_BUILD_NUMBER=17 JDK_MAJOR_VER=1 JDK_MINOR_VER=8 From db0f450d531c27cc93ee83f821e168572f31eb42 Mon Sep 17 00:00:00 2001 From: Petr Pchelko Date: Fri, 18 Jan 2013 18:17:02 +0400 Subject: [PATCH 068/138] 7179050: [macosx] Make LWAWT be able to run on AppKit thread Removed irrelevant assertions from the LWAWT native methods Reviewed-by: serb, anthony --- .../sun/lwawt/macosx/CPlatformWindow.java | 1 - .../macosx/native/sun/awt/AWTSurfaceLayers.m | 4 +- jdk/src/macosx/native/sun/awt/AWTView.m | 16 +- jdk/src/macosx/native/sun/awt/AWTWindow.m | 80 ++-------- .../native/sun/awt/ApplicationDelegate.m | 64 +++----- jdk/src/macosx/native/sun/awt/CClipboard.m | 4 - .../macosx/native/sun/awt/CCursorManager.m | 16 +- jdk/src/macosx/native/sun/awt/CDesktopPeer.m | 4 - .../native/sun/awt/CDragSourceContextPeer.m | 4 +- jdk/src/macosx/native/sun/awt/CImage.m | 10 +- jdk/src/macosx/native/sun/awt/CInputMethod.m | 12 +- jdk/src/macosx/native/sun/awt/CMenu.m | 4 - .../macosx/native/sun/awt/CMenuComponent.m | 8 +- jdk/src/macosx/native/sun/awt/CMenuItem.m | 25 +-- jdk/src/macosx/native/sun/awt/CPopupMenu.m | 4 +- jdk/src/macosx/native/sun/awt/CTrayIcon.m | 14 +- jdk/src/macosx/native/sun/awt/CWrapper.m | 142 +++++++++--------- .../sun/awt/JavaComponentAccessibility.m | 4 - jdk/src/macosx/native/sun/awt/LWCToolkit.m | 8 +- jdk/src/macosx/native/sun/awt/awt.m | 9 +- .../native/sun/osxapp/ThreadUtilities.h | 8 +- .../native/sun/osxapp/ThreadUtilities.m | 98 ++---------- 22 files changed, 155 insertions(+), 384 deletions(-) diff --git a/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java b/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java index b489eae96b6..c20f0a97ec8 100644 --- a/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java +++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java @@ -58,7 +58,6 @@ public final class CPlatformWindow extends CFRetainedResource implements Platfor private static native void nativeRevalidateNSWindowShadow(long nsWindowPtr); private static native void nativeSetNSWindowMinimizedIcon(long nsWindowPtr, long nsImage); private static native void nativeSetNSWindowRepresentedFilename(long nsWindowPtr, String representedFilename); - private static native void nativeSetNSWindowSecurityWarningPositioning(long nsWindowPtr, double x, double y, float biasX, float biasY); private static native void nativeSetEnabled(long nsWindowPtr, boolean isEnabled); private static native void nativeSynthesizeMouseEnteredExitedEvents(); private static native void nativeDispose(long nsWindowPtr); diff --git a/jdk/src/macosx/native/sun/awt/AWTSurfaceLayers.m b/jdk/src/macosx/native/sun/awt/AWTSurfaceLayers.m index c6fec1fc392..6e9fbbca5ab 100644 --- a/jdk/src/macosx/native/sun/awt/AWTSurfaceLayers.m +++ b/jdk/src/macosx/native/sun/awt/AWTSurfaceLayers.m @@ -101,8 +101,7 @@ Java_sun_lwawt_macosx_CPlatformComponent_nativeCreateComponent JNF_COCOA_ENTER(env); [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ - AWT_ASSERT_APPKIT_THREAD; - + CALayer *windowLayer = jlong_to_ptr(windowLayerPtr); surfaceLayers = [[AWTSurfaceLayers alloc] initWithWindowLayer: windowLayer]; CFRetain(surfaceLayers); @@ -127,7 +126,6 @@ JNF_COCOA_ENTER(env); AWTSurfaceLayers *surfaceLayers = OBJC(surfaceLayersPtr); [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ - AWT_ASSERT_APPKIT_THREAD; CGRect rect = CGRectMake(x, y, width, height); [surfaceLayers setBounds: rect]; diff --git a/jdk/src/macosx/native/sun/awt/AWTView.m b/jdk/src/macosx/native/sun/awt/AWTView.m index 3770287a32a..cc16a2026ee 100644 --- a/jdk/src/macosx/native/sun/awt/AWTView.m +++ b/jdk/src/macosx/native/sun/awt/AWTView.m @@ -1243,8 +1243,7 @@ JNF_COCOA_ENTER(env); jobject cPlatformView = (*env)->NewGlobalRef(env, obj); [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ - AWT_ASSERT_APPKIT_THREAD; - + CALayer *windowLayer = jlong_to_ptr(windowLayerPtr); AWTView *view = [[AWTView alloc] initWithRect:rect platformView:cPlatformView @@ -1274,8 +1273,7 @@ JNF_COCOA_ENTER(env); NSView *view = (NSView *)jlong_to_ptr(viewPtr); [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ - AWT_ASSERT_APPKIT_THREAD; - + if (toResize) { [view setAutoresizingMask: NSViewHeightSizable | NSViewWidthSizable]; } else { @@ -1308,8 +1306,7 @@ JNF_COCOA_ENTER(env); NSWindow *window = [view window]; [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ - AWT_ASSERT_APPKIT_THREAD; - + ret = (jint)[[AWTWindow getNSWindowDisplayID_AppKitThread: window] intValue]; }]; @@ -1336,8 +1333,7 @@ JNF_COCOA_ENTER(env); NSView *view = (NSView *)jlong_to_ptr(viewPtr); [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ - AWT_ASSERT_APPKIT_THREAD; - + NSRect viewBounds = [view bounds]; NSRect frameInWindow = [view convertRect:viewBounds toView:nil]; rect = [[view window] convertRectToScreen:frameInWindow]; @@ -1366,9 +1362,7 @@ JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CPlatformView_nativeIsViewUnder JNF_COCOA_ENTER(env); NSView *nsView = OBJC(viewPtr); - [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ - AWT_ASSERT_APPKIT_THREAD; - + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ NSPoint ptWindowCoords = [[nsView window] mouseLocationOutsideOfEventStream]; NSPoint ptViewCoords = [nsView convertPoint:ptWindowCoords fromView:nil]; underMouse = [nsView hitTest:ptViewCoords] != nil; diff --git a/jdk/src/macosx/native/sun/awt/AWTWindow.m b/jdk/src/macosx/native/sun/awt/AWTWindow.m index fa27a3bf761..3af7c6c736f 100644 --- a/jdk/src/macosx/native/sun/awt/AWTWindow.m +++ b/jdk/src/macosx/native/sun/awt/AWTWindow.m @@ -738,14 +738,12 @@ JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeCreateNSWind __block AWTWindow *window = nil; JNF_COCOA_ENTER(env); -AWT_ASSERT_NOT_APPKIT_THREAD; JNFWeakJObjectWrapper *platformWindow = [JNFWeakJObjectWrapper wrapperWithJObject:obj withEnv:env]; NSView *contentView = OBJC(contentViewPtr); NSRect frameRect = NSMakeRect(x, y, w, h); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ window = [[AWTWindow alloc] initWithPlatformWindow:platformWindow styleBits:styleBits @@ -770,11 +768,9 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetNSWindowSt (JNIEnv *env, jclass clazz, jlong windowPtr, jint mask, jint bits) { JNF_COCOA_ENTER(env); -AWT_ASSERT_NOT_APPKIT_THREAD; NSWindow *nsWindow = OBJC(windowPtr); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ AWTWindow *window = (AWTWindow*)[nsWindow delegate]; @@ -807,12 +803,10 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetNSWindowMe (JNIEnv *env, jclass clazz, jlong windowPtr, jlong menuBarPtr) { JNF_COCOA_ENTER(env); -AWT_ASSERT_NOT_APPKIT_THREAD; NSWindow *nsWindow = OBJC(windowPtr); CMenuBar *menuBar = OBJC(menuBarPtr); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ AWTWindow *window = (AWTWindow*)[nsWindow delegate]; @@ -838,14 +832,12 @@ JNIEXPORT jobject JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeGetNSWindo jobject ret = NULL; JNF_COCOA_ENTER(env); -AWT_ASSERT_NOT_APPKIT_THREAD; NSWindow *nsWindow = OBJC(windowPtr); __block NSRect contentRect = NSZeroRect; __block NSRect frame = NSZeroRect; - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ frame = [nsWindow frame]; contentRect = [NSWindow contentRectForFrameRect:frame styleMask:[nsWindow styleMask]]; @@ -873,14 +865,12 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetNSWindowBo (JNIEnv *env, jclass clazz, jlong windowPtr, jdouble originX, jdouble originY, jdouble width, jdouble height) { JNF_COCOA_ENTER(env); -AWT_ASSERT_NOT_APPKIT_THREAD; NSRect jrect = NSMakeRect(originX, originY, width, height); // TODO: not sure we need displayIfNeeded message in our view NSWindow *nsWindow = OBJC(windowPtr); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ AWTWindow *window = (AWTWindow*)[nsWindow delegate]; @@ -913,7 +903,6 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetNSWindowMi (JNIEnv *env, jclass clazz, jlong windowPtr, jdouble minW, jdouble minH, jdouble maxW, jdouble maxH) { JNF_COCOA_ENTER(env); -AWT_ASSERT_NOT_APPKIT_THREAD; if (minW < 1) minW = 1; if (minH < 1) minH = 1; @@ -921,8 +910,7 @@ AWT_ASSERT_NOT_APPKIT_THREAD; if (maxH < 1) maxH = 1; NSWindow *nsWindow = OBJC(windowPtr); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ AWTWindow *window = (AWTWindow*)[nsWindow delegate]; @@ -949,12 +937,9 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativePushNSWindowT (JNIEnv *env, jclass clazz, jlong windowPtr) { JNF_COCOA_ENTER(env); -AWT_ASSERT_NOT_APPKIT_THREAD; NSWindow *nsWindow = OBJC(windowPtr); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; - + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [nsWindow orderBack:nil]; }]; @@ -970,11 +955,9 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativePushNSWindowT (JNIEnv *env, jclass clazz, jlong windowPtr) { JNF_COCOA_ENTER(env); -AWT_ASSERT_NOT_APPKIT_THREAD; NSWindow *nsWindow = OBJC(windowPtr); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ if (![nsWindow isKeyWindow]) { [nsWindow makeKeyAndOrderFront:nsWindow]; @@ -995,7 +978,6 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetNSWindowTi (JNIEnv *env, jclass clazz, jlong windowPtr, jstring jtitle) { JNF_COCOA_ENTER(env); -AWT_ASSERT_NOT_APPKIT_THREAD; NSWindow *nsWindow = OBJC(windowPtr); [nsWindow performSelectorOnMainThread:@selector(setTitle:) @@ -1016,15 +998,9 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeRevalidateNSW JNF_COCOA_ENTER(env); NSWindow *nsWindow = OBJC(windowPtr); - if ([NSThread isMainThread]) { + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [nsWindow invalidateShadow]; - } else { - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; - - [nsWindow invalidateShadow]; - }]; - } + }]; JNF_COCOA_EXIT(env); } @@ -1060,13 +1036,10 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetNSWindowMi (JNIEnv *env, jclass clazz, jlong windowPtr, jlong nsImagePtr) { JNF_COCOA_ENTER(env); -AWT_ASSERT_NOT_APPKIT_THREAD; NSWindow *nsWindow = OBJC(windowPtr); NSImage *image = OBJC(nsImagePtr); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; - + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [nsWindow setMiniwindowImage:image]; }]; @@ -1082,35 +1055,16 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetNSWindowRe (JNIEnv *env, jclass clazz, jlong windowPtr, jstring filename) { JNF_COCOA_ENTER(env); -AWT_ASSERT_NOT_APPKIT_THREAD; NSWindow *nsWindow = OBJC(windowPtr); NSURL *url = (filename == NULL) ? nil : [NSURL fileURLWithPath:JNFNormalizedNSStringForPath(env, filename)]; - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; - + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [nsWindow setRepresentedURL:url]; }]; JNF_COCOA_EXIT(env); } -/* - * Class: sun_lwawt_macosx_CPlatformWindow - * Method: nativeSetNSWindowSecurityWarningPositioning - * Signature: (JDDFF)V - */ -JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetNSWindowSecurityWarningPositioning -(JNIEnv *env, jclass clazz, jlong windowPtr, jdouble x, jdouble y, jfloat biasX, jfloat biasY) -{ -JNF_COCOA_ENTER(env); -AWT_ASSERT_NOT_APPKIT_THREAD; - - [JNFException raise:env as:kRuntimeException reason:"unimplemented"]; - -JNF_COCOA_EXIT(env); -} - /* * Class: sun_lwawt_macosx_CPlatformWindow * Method: nativeGetTopmostPlatformWindowUnderMouse @@ -1144,10 +1098,8 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSynthesizeMou (JNIEnv *env, jclass clazz) { JNF_COCOA_ENTER(env); - AWT_ASSERT_NOT_APPKIT_THREAD; - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [AWTWindow synthesizeMouseEnteredExitedEventsForAllWindows]; }]; @@ -1168,7 +1120,7 @@ JNF_COCOA_ENTER(env); SEL toggleFullScreenSelector = @selector(toggleFullScreen:); if (![nsWindow respondsToSelector:toggleFullScreenSelector]) return; - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [nsWindow performSelector:toggleFullScreenSelector withObject:nil]; }]; @@ -1181,7 +1133,7 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetEnabled JNF_COCOA_ENTER(env); NSWindow *nsWindow = OBJC(windowPtr); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ AWTWindow *window = (AWTWindow*)[nsWindow delegate]; [window setEnabled: isEnabled]; @@ -1196,7 +1148,7 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeDispose JNF_COCOA_ENTER(env); NSWindow *nsWindow = OBJC(windowPtr); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ AWTWindow *window = (AWTWindow*)[nsWindow delegate]; if ([AWTWindow lastKeyWindow] == window) { diff --git a/jdk/src/macosx/native/sun/awt/ApplicationDelegate.m b/jdk/src/macosx/native/sun/awt/ApplicationDelegate.m index 7ba5fcfe804..9a5aee94d44 100644 --- a/jdk/src/macosx/native/sun/awt/ApplicationDelegate.m +++ b/jdk/src/macosx/native/sun/awt/ApplicationDelegate.m @@ -515,10 +515,9 @@ AWT_ASSERT_APPKIT_THREAD; JNIEXPORT void JNICALL Java_com_apple_eawt_Application_nativeInitializeApplicationDelegate (JNIEnv *env, jclass clz) { -AWT_ASSERT_NOT_APPKIT_THREAD; JNF_COCOA_ENTER(env); // Force initialization to happen on AppKit thread! - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [ApplicationDelegate sharedDelegate]; }]; JNF_COCOA_EXIT(env); @@ -532,10 +531,9 @@ JNF_COCOA_EXIT(env); JNIEXPORT void JNICALL Java_com_apple_eawt__1AppEventHandler_nativeOpenCocoaAboutWindow (JNIEnv *env, jclass clz) { -AWT_ASSERT_NOT_APPKIT_THREAD; JNF_COCOA_ENTER(env); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [NSApp orderFrontStandardAboutPanel:nil]; }]; @@ -550,10 +548,9 @@ JNF_COCOA_EXIT(env); JNIEXPORT void JNICALL Java_com_apple_eawt__1AppEventHandler_nativeReplyToAppShouldTerminate (JNIEnv *env, jclass clz, jboolean doTerminate) { -AWT_ASSERT_NOT_APPKIT_THREAD; JNF_COCOA_ENTER(env); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [NSApp replyToApplicationShouldTerminate:doTerminate]; }]; @@ -568,7 +565,6 @@ JNF_COCOA_EXIT(env); JNIEXPORT void JNICALL Java_com_apple_eawt__1AppEventHandler_nativeRegisterForNotification (JNIEnv *env, jclass clz, jint notificationType) { -AWT_ASSERT_NOT_APPKIT_THREAD; JNF_COCOA_ENTER(env); [ThreadUtilities performOnMainThread:@selector(_registerForNotification:) onObject:[ApplicationDelegate class] @@ -586,13 +582,10 @@ JNF_COCOA_EXIT(env); JNIEXPORT void JNICALL Java_com_apple_eawt__1AppDockIconHandler_nativeSetDockMenu (JNIEnv *env, jclass clz, jlong nsMenuPtr) { -AWT_ASSERT_NOT_APPKIT_THREAD; JNF_COCOA_ENTER(env); NSMenu *menu = (NSMenu *)jlong_to_ptr(nsMenuPtr); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; - + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ [ApplicationDelegate sharedDelegate].fDockMenu = menu; }]; @@ -607,14 +600,13 @@ JNF_COCOA_EXIT(env); JNIEXPORT void JNICALL Java_com_apple_eawt__1AppDockIconHandler_nativeSetDockIconImage (JNIEnv *env, jclass clz, jlong nsImagePtr) { -AWT_ASSERT_NOT_APPKIT_THREAD; JNF_COCOA_ENTER(env); NSImage *_image = (NSImage *)jlong_to_ptr(nsImagePtr); - [JNFRunLoop performOnMainThread:@selector(_setDockIconImage:) - on:[ApplicationDelegate class] - withObject:_image - waitUntilDone:NO]; + [ThreadUtilities performOnMainThread:@selector(_setDockIconImage:) + on:[ApplicationDelegate class] + withObject:_image + waitUntilDone:NO]; JNF_COCOA_EXIT(env); } @@ -629,12 +621,9 @@ JNIEXPORT jlong JNICALL Java_com_apple_eawt__1AppDockIconHandler_nativeGetDockIc { __block NSImage *image = nil; -AWT_ASSERT_NOT_APPKIT_THREAD; JNF_COCOA_ENTER(env); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; - + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ image = [ApplicationDelegate _dockIconImage]; CFRetain(image); }]; @@ -652,13 +641,10 @@ JNF_COCOA_EXIT(env); JNIEXPORT void JNICALL Java_com_apple_eawt__1AppDockIconHandler_nativeSetDockIconBadge (JNIEnv *env, jclass clz, jstring badge) { -AWT_ASSERT_NOT_APPKIT_THREAD; JNF_COCOA_ENTER(env); NSString *badgeString = JNFJavaToNSString(env, badge); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; - + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ NSDockTile *dockTile = [NSApp dockTile]; [dockTile setBadgeLabel:badgeString]; [dockTile display]; @@ -675,12 +661,9 @@ JNF_COCOA_EXIT(env); JNIEXPORT void JNICALL Java_com_apple_eawt__1AppMiscHandlers_nativeRequestActivation (JNIEnv *env, jclass clz, jboolean allWindows) { -AWT_ASSERT_NOT_APPKIT_THREAD; JNF_COCOA_ENTER(env); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; - + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ NSApplicationActivationOptions options = allWindows ? NSApplicationActivateAllWindows : 0; options |= NSApplicationActivateIgnoringOtherApps; // without this, nothing happens! [[NSRunningApplication currentApplication] activateWithOptions:options]; @@ -697,12 +680,9 @@ JNF_COCOA_EXIT(env); JNIEXPORT void JNICALL Java_com_apple_eawt__1AppMiscHandlers_nativeRequestUserAttention (JNIEnv *env, jclass clz, jboolean critical) { -AWT_ASSERT_NOT_APPKIT_THREAD; JNF_COCOA_ENTER(env); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; - + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [NSApp requestUserAttention:critical ? NSCriticalRequest : NSInformationalRequest]; }]; @@ -717,13 +697,12 @@ JNF_COCOA_EXIT(env); JNIEXPORT void JNICALL Java_com_apple_eawt__1AppMiscHandlers_nativeOpenHelpViewer (JNIEnv *env, jclass clz) { -AWT_ASSERT_NOT_APPKIT_THREAD; JNF_COCOA_ENTER(env); - [JNFRunLoop performOnMainThread:@selector(showHelp:) - on:NSApp - withObject:nil - waitUntilDone:NO]; + [ThreadUtilities performOnMainThread:@selector(showHelp:) + on:NSApp + withObject:nil + waitUntilDone:NO]; JNF_COCOA_EXIT(env); } @@ -736,7 +715,6 @@ JNF_COCOA_EXIT(env); JNIEXPORT void JNICALL Java_com_apple_eawt__1AppMiscHandlers_nativeEnableSuddenTermination (JNIEnv *env, jclass clz) { -AWT_ASSERT_NOT_APPKIT_THREAD; JNF_COCOA_ENTER(env); [[NSProcessInfo processInfo] enableSuddenTermination]; // Foundation thread-safe @@ -752,7 +730,6 @@ JNF_COCOA_EXIT(env); JNIEXPORT void JNICALL Java_com_apple_eawt__1AppMiscHandlers_nativeDisableSuddenTermination (JNIEnv *env, jclass clz) { -AWT_ASSERT_NOT_APPKIT_THREAD; JNF_COCOA_ENTER(env); [[NSProcessInfo processInfo] disableSuddenTermination]; // Foundation thread-safe @@ -768,12 +745,9 @@ JNF_COCOA_EXIT(env); JNIEXPORT void JNICALL Java_com_apple_eawt__1AppMenuBarHandler_nativeSetMenuState (JNIEnv *env, jclass clz, jint menuID, jboolean visible, jboolean enabled) { -AWT_ASSERT_NOT_APPKIT_THREAD; JNF_COCOA_ENTER(env); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; - + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ ApplicationDelegate *delegate = [ApplicationDelegate sharedDelegate]; switch (menuID) { case com_apple_eawt__AppMenuBarHandler_MENU_ABOUT: @@ -796,12 +770,10 @@ JNF_COCOA_EXIT(env); JNIEXPORT void JNICALL Java_com_apple_eawt__1AppMenuBarHandler_nativeSetDefaultMenuBar (JNIEnv *env, jclass clz, jlong cMenuBarPtr) { -AWT_ASSERT_NOT_APPKIT_THREAD; JNF_COCOA_ENTER(env); CMenuBar *menu = (CMenuBar *)jlong_to_ptr(cMenuBarPtr); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [ApplicationDelegate sharedDelegate].fDefaultMenuBar = menu; }]; diff --git a/jdk/src/macosx/native/sun/awt/CClipboard.m b/jdk/src/macosx/native/sun/awt/CClipboard.m index 36675974705..fb300f72971 100644 --- a/jdk/src/macosx/native/sun/awt/CClipboard.m +++ b/jdk/src/macosx/native/sun/awt/CClipboard.m @@ -109,7 +109,6 @@ static CClipboard *sClipboard = nil; } - (void) javaDeclareTypes:(NSArray *)inTypes withOwner:(jobject)inClipboard jniEnv:(JNIEnv *)inEnv { - AWT_ASSERT_NOT_APPKIT_THREAD; //NSLog(@"CClipboard javaDeclareTypes %@ withOwner", inTypes); @@ -134,7 +133,6 @@ static CClipboard *sClipboard = nil; - (NSArray *) javaGetTypes { - AWT_ASSERT_NOT_APPKIT_THREAD; NSMutableArray *args = [NSMutableArray arrayWithCapacity:1]; [ThreadUtilities performOnMainThread:@selector(_nativeGetTypes:) onObject:self withObject:args waitUntilDone:YES awtMode:YES]; @@ -152,7 +150,6 @@ static CClipboard *sClipboard = nil; } - (void) javaSetData:(NSData *)inData forType:(NSString *) inFormat { - AWT_ASSERT_NOT_APPKIT_THREAD; CClipboardUpdate *newUpdate = [[CClipboardUpdate alloc] initWithData:inData withFormat:inFormat]; [ThreadUtilities performOnMainThread:@selector(_nativeSetData:) onObject:self withObject:newUpdate waitUntilDone:YES awtMode:YES]; @@ -171,7 +168,6 @@ static CClipboard *sClipboard = nil; } - (NSData *) javaGetDataForType:(NSString *) inFormat { - AWT_ASSERT_NOT_APPKIT_THREAD; NSMutableArray *args = [NSMutableArray arrayWithObject:inFormat]; [ThreadUtilities performOnMainThread:@selector(_nativeGetDataForType:) onObject:self withObject:args waitUntilDone:YES awtMode:YES]; diff --git a/jdk/src/macosx/native/sun/awt/CCursorManager.m b/jdk/src/macosx/native/sun/awt/CCursorManager.m index 601ea91e782..232a31621ac 100644 --- a/jdk/src/macosx/native/sun/awt/CCursorManager.m +++ b/jdk/src/macosx/native/sun/awt/CCursorManager.m @@ -74,7 +74,6 @@ Java_sun_lwawt_macosx_CCursorManager_nativeSetBuiltInCursor (JNIEnv *env, jclass class, jint type, jstring name) { JNF_COCOA_ENTER(env); -AWT_ASSERT_NOT_APPKIT_THREAD; NSString *cursorName = JNFJavaToNSString(env, name); SEL cursorSelector = (type == sun_lwawt_macosx_CCursorManager_NAMED_CURSOR) ? lookupCursorSelectorForName(cursorName) : lookupCursorSelectorForType(type); @@ -87,9 +86,7 @@ AWT_ASSERT_NOT_APPKIT_THREAD; [JNFException raise:env as:kNoSuchMethodException reason:"missing NSCursor selector"]; } - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; - + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ setCursorOnAppKitThread([[NSCursor class] performSelector:cursorSelector]); }]; @@ -101,12 +98,9 @@ Java_sun_lwawt_macosx_CCursorManager_nativeSetCustomCursor (JNIEnv *env, jclass class, jlong imgPtr, jdouble x, jdouble y) { JNF_COCOA_ENTER(env); -AWT_ASSERT_NOT_APPKIT_THREAD; NSImage *image = (NSImage *)jlong_to_ptr(imgPtr); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; - + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ NSCursor *cursor = [[NSCursor alloc] initWithImage:image hotSpot:(NSPoint){ x, y }]; setCursorOnAppKitThread(cursor); @@ -127,8 +121,6 @@ JNF_COCOA_ENTER(env); __block NSPoint pt = NSZeroPoint; [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ - AWT_ASSERT_APPKIT_THREAD; - pt = ConvertNSScreenPoint(env, [NSEvent mouseLocation]); }]; @@ -144,13 +136,11 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CCursorManager_nativeSetAllowsCursorSetInBackground (JNIEnv *env, jclass class, jboolean allows) { - JNF_COCOA_ENTER(env); -AWT_ASSERT_NOT_APPKIT_THREAD; SEL allowsSetInBackground_SEL = @selector(javaSetAllowsCursorSetInBackground:); if ([[NSCursor class] respondsToSelector:allowsSetInBackground_SEL]) { - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ NSMethodSignature *allowsSetInBackground_sig = [[NSCursor class] methodSignatureForSelector:allowsSetInBackground_SEL]; NSInvocation *invocation = diff --git a/jdk/src/macosx/native/sun/awt/CDesktopPeer.m b/jdk/src/macosx/native/sun/awt/CDesktopPeer.m index af9f3192d91..9dd121059af 100644 --- a/jdk/src/macosx/native/sun/awt/CDesktopPeer.m +++ b/jdk/src/macosx/native/sun/awt/CDesktopPeer.m @@ -36,8 +36,6 @@ JNIEXPORT jint JNICALL Java_sun_lwawt_macosx_CDesktopPeer__1lsOpenURI (JNIEnv *env, jclass clz, jstring uri) { - // AWT_ASSERT_ANY_THREAD - OSStatus status = noErr; JNF_COCOA_ENTER(env); @@ -63,8 +61,6 @@ JNF_COCOA_EXIT(env); JNIEXPORT jint JNICALL Java_sun_lwawt_macosx_CDesktopPeer__1lsOpenFile (JNIEnv *env, jclass clz, jstring jpath, jboolean print) { - // AWT_ASSERT_ANY_THREAD - OSStatus status = noErr; JNF_COCOA_ENTER(env); diff --git a/jdk/src/macosx/native/sun/awt/CDragSourceContextPeer.m b/jdk/src/macosx/native/sun/awt/CDragSourceContextPeer.m index e2fedfe35df..46788795f45 100644 --- a/jdk/src/macosx/native/sun/awt/CDragSourceContextPeer.m +++ b/jdk/src/macosx/native/sun/awt/CDragSourceContextPeer.m @@ -46,7 +46,7 @@ JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CDragSourceContextPeer_createNativ __block CDragSource* dragSource = nil; JNF_COCOA_ENTER(env); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ dragSource = [[CDragSource alloc] init:jthis component:jcomponent peer:jpeer control:controlObj transferable:jtransferable triggerEvent:jtrigger dragPosX:jdragposx dragPosY:jdragposy modifiers:jextmodifiers clickCount:jclickcount timeStamp:jtimestamp @@ -103,7 +103,7 @@ JNF_COCOA_EXIT(env); JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CDragSourceContextPeer_setNativeCursor (JNIEnv *env, jobject jthis, jlong nativeDragSourceVal, jobject jcursor, jint jcursortype) { - AWT_ASSERT_NOT_APPKIT_THREAD; + //AWT_ASSERT_NOT_APPKIT_THREAD; //JNF_COCOA_ENTER(env); // jobject gCursor = JNFNewGlobalRef(env, jcursor); diff --git a/jdk/src/macosx/native/sun/awt/CImage.m b/jdk/src/macosx/native/sun/awt/CImage.m index 330e9943f62..9bd56da3db6 100644 --- a/jdk/src/macosx/native/sun/awt/CImage.m +++ b/jdk/src/macosx/native/sun/awt/CImage.m @@ -108,7 +108,6 @@ JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CImage_nativeCreateNSImageFromArra jlong result = 0L; JNF_COCOA_ENTER(env); -AWT_ASSERT_ANY_THREAD; NSBitmapImageRep* imageRep = CImage_CreateImageRep(env, buffer, width, height); if (imageRep) { @@ -139,7 +138,6 @@ JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CImage_nativeCreateNSImageFromArra jlong result = 0L; JNF_COCOA_ENTER(env); -AWT_ASSERT_ANY_THREAD; jsize num = (*env)->GetArrayLength(env, buffers); NSMutableArray * reps = [NSMutableArray arrayWithCapacity: num]; @@ -187,7 +185,6 @@ JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CImage_nativeCreateNSImageFromIcon NSImage *image = nil; JNF_COCOA_ENTER(env); -AWT_ASSERT_ANY_THREAD; IconRef iconRef; if (noErr == GetIconRef(kOnSystemDisk, kSystemIconsCreator, selector, &iconRef)) { @@ -212,7 +209,6 @@ JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CImage_nativeCreateNSImageFromFile NSImage *image = nil; JNF_COCOA_ENTER(env); -AWT_ASSERT_ANY_THREAD; NSString *path = JNFNormalizedNSStringForPath(env, file); image = [[NSImage alloc] initByReferencingFile:path]; @@ -234,10 +230,9 @@ JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CImage_nativeCreateNSImageOfFileFr __block NSImage *image = nil; JNF_COCOA_ENTER(env); -AWT_ASSERT_ANY_THREAD; NSString *path = JNFNormalizedNSStringForPath(env, file); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ image = [[NSWorkspace sharedWorkspace] iconForFile:path]; [image setScalesWhenResized:TRUE]; if (image) CFRetain(image); // GC @@ -259,7 +254,6 @@ JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CImage_nativeCreateNSImageFromImag NSImage *image = nil; JNF_COCOA_ENTER(env); -AWT_ASSERT_ANY_THREAD; image = [NSImage imageNamed:JNFJavaToNSString(env, name)]; if (image) CFRetain(image); // GC @@ -278,7 +272,6 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CImage_nativeCopyNSImageIntoArray (JNIEnv *env, jclass klass, jlong nsImgPtr, jintArray buffer, jint w, jint h) { JNF_COCOA_ENTER(env); -AWT_ASSERT_ANY_THREAD; NSImage *img = (NSImage *)jlong_to_ptr(nsImgPtr); jint *dst = (*env)->GetPrimitiveArrayCritical(env, buffer, NULL); @@ -301,7 +294,6 @@ JNIEXPORT jobject JNICALL Java_sun_lwawt_macosx_CImage_nativeGetNSImageSize jobject size = NULL; JNF_COCOA_ENTER(env); -AWT_ASSERT_ANY_THREAD; size = NSToJavaSize(env, [(NSImage *)jlong_to_ptr(nsImgPtr) size]); diff --git a/jdk/src/macosx/native/sun/awt/CInputMethod.m b/jdk/src/macosx/native/sun/awt/CInputMethod.m index 764e2db534d..39a66c7242d 100644 --- a/jdk/src/macosx/native/sun/awt/CInputMethod.m +++ b/jdk/src/macosx/native/sun/awt/CInputMethod.m @@ -153,7 +153,7 @@ JNIEXPORT jobject JNICALL Java_sun_lwawt_macosx_CInputMethod_nativeGetCurrentInp __block NSString *keyboardInfo = NULL; JNF_COCOA_ENTER(env); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ keyboardInfo = [inputMethodController performSelector:@selector(currentInputMethodName)]; [keyboardInfo retain]; }]; @@ -177,7 +177,7 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CInputMethod_nativeNotifyPeer JNF_COCOA_ENTER(env); AWTView *view = (AWTView *)jlong_to_ptr(nativePeer); JNFJObjectWrapper *inputMethodWrapper = [[JNFJObjectWrapper alloc] initWithJObject:inputMethod withEnv:env]; - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [CInputMethod _nativeNotifyPeerWithView:view inputMethod:inputMethodWrapper]; }]; @@ -196,7 +196,7 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CInputMethod_nativeEndComposition JNF_COCOA_ENTER(env); AWTView *view = (AWTView *)jlong_to_ptr(nativePeer); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [CInputMethod _nativeEndComposition:view]; }]; @@ -216,7 +216,7 @@ JNIEXPORT jobject JNICALL Java_sun_lwawt_macosx_CInputMethod_getNativeLocale __block NSString *isoAbbreviation; JNF_COCOA_ENTER(env); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ isoAbbreviation = (NSString *) [inputMethodController performSelector:@selector(currentInputMethodLocale)]; [isoAbbreviation retain]; }]; @@ -259,7 +259,7 @@ JNF_COCOA_ENTER(env); NSString *localeStr = JNFJavaToNSString(env, locale); [localeStr retain]; - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ [CInputMethod setKeyboardLayout:localeStr]; }]; @@ -293,7 +293,7 @@ JNIEXPORT jobject JNICALL Java_sun_lwawt_macosx_CInputMethodDescriptor_nativeGet __block NSArray *selectableArray = nil; JNF_COCOA_ENTER(env); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ selectableArray = (NSArray *)[inputMethodController performSelector:@selector(availableInputMethodLocales)]; [selectableArray retain]; }]; diff --git a/jdk/src/macosx/native/sun/awt/CMenu.m b/jdk/src/macosx/native/sun/awt/CMenu.m index 32a9d5b7e12..e6325c2c3db 100644 --- a/jdk/src/macosx/native/sun/awt/CMenu.m +++ b/jdk/src/macosx/native/sun/awt/CMenu.m @@ -55,12 +55,10 @@ AWT_ASSERT_APPKIT_THREAD; //- (void)finalize { [super finalize]; } - (void)addJavaSubmenu:(CMenu *)submenu { -AWT_ASSERT_NOT_APPKIT_THREAD; [ThreadUtilities performOnMainThread:@selector(addNativeItem_OnAppKitThread:) onObject:self withObject:submenu waitUntilDone:YES awtMode:YES]; } - (void)addJavaMenuItem:(CMenuItem *)theMenuItem { -AWT_ASSERT_NOT_APPKIT_THREAD; [ThreadUtilities performOnMainThread:@selector(addNativeItem_OnAppKitThread:) onObject:self withObject:theMenuItem waitUntilDone:YES awtMode:YES]; } @@ -70,7 +68,6 @@ AWT_ASSERT_APPKIT_THREAD; } - (void)setJavaMenuTitle:(NSString *)title { -AWT_ASSERT_NOT_APPKIT_THREAD; if (title) { [ThreadUtilities performOnMainThread:@selector(setNativeMenuTitle_OnAppKitThread:) onObject:self withObject:title waitUntilDone:YES awtMode:YES]; @@ -95,7 +92,6 @@ AWT_ASSERT_APPKIT_THREAD; } - (void)deleteJavaItem:(jint)index { -AWT_ASSERT_NOT_APPKIT_THREAD; [ThreadUtilities performOnMainThread:@selector(deleteNativeJavaItem_OnAppKitThread:) onObject:self withObject:[NSNumber numberWithInt:index] waitUntilDone:YES awtMode:YES]; } diff --git a/jdk/src/macosx/native/sun/awt/CMenuComponent.m b/jdk/src/macosx/native/sun/awt/CMenuComponent.m index 9787625a751..4f9fc593e11 100644 --- a/jdk/src/macosx/native/sun/awt/CMenuComponent.m +++ b/jdk/src/macosx/native/sun/awt/CMenuComponent.m @@ -80,10 +80,10 @@ Java_sun_lwawt_macosx_CMenuComponent_nativeDispose { JNF_COCOA_ENTER(env); - [JNFRunLoop performOnMainThread:@selector(disposer) - on:((id)jlong_to_ptr(menuItemObj)) - withObject:nil - waitUntilDone:NO]; + [ThreadUtilities performOnMainThread:@selector(disposer) + on:((id)jlong_to_ptr(menuItemObj)) + withObject:nil + waitUntilDone:NO]; JNF_COCOA_EXIT(env); } diff --git a/jdk/src/macosx/native/sun/awt/CMenuItem.m b/jdk/src/macosx/native/sun/awt/CMenuItem.m index 663860d7ff6..2281d8bcd95 100644 --- a/jdk/src/macosx/native/sun/awt/CMenuItem.m +++ b/jdk/src/macosx/native/sun/awt/CMenuItem.m @@ -104,7 +104,6 @@ JNF_COCOA_EXIT(env); } - (void) setJavaLabel:(NSString *)theLabel shortcut:(NSString *)theKeyEquivalent modifierMask:(jint)modifiers { -AWT_ASSERT_NOT_APPKIT_THREAD; NSUInteger modifierMask = 0; @@ -126,8 +125,7 @@ AWT_ASSERT_NOT_APPKIT_THREAD; modifierMask = JavaModifiersToNsKeyModifiers(modifiers, NO); } - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ [fMenuItem setKeyEquivalent:theKeyEquivalent]; [fMenuItem setKeyEquivalentModifierMask:modifierMask]; [fMenuItem setTitle:theLabel]; @@ -135,32 +133,23 @@ AWT_ASSERT_NOT_APPKIT_THREAD; } - (void) setJavaImage:(NSImage *)theImage { -AWT_ASSERT_NOT_APPKIT_THREAD; - - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [fMenuItem setImage:theImage]; }]; } - (void) setJavaToolTipText:(NSString *)theText { -AWT_ASSERT_NOT_APPKIT_THREAD; - - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [fMenuItem setToolTip:theText]; }]; } - (void)setJavaEnabled:(BOOL) enabled { -AWT_ASSERT_NOT_APPKIT_THREAD; - - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ @synchronized(self) { fIsEnabled = enabled; @@ -173,7 +162,6 @@ AWT_ASSERT_NOT_APPKIT_THREAD; } - (BOOL)isEnabled { - // AWT_ASSERT_ANY_THREAD; BOOL enabled = NO; @synchronized(self) { @@ -184,11 +172,8 @@ AWT_ASSERT_NOT_APPKIT_THREAD; - (void)setJavaState:(BOOL)newState { -AWT_ASSERT_NOT_APPKIT_THREAD; - - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ -AWT_ASSERT_APPKIT_THREAD; + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [fMenuItem setState:(newState ? NSOnState : NSOffState)]; }]; } diff --git a/jdk/src/macosx/native/sun/awt/CPopupMenu.m b/jdk/src/macosx/native/sun/awt/CPopupMenu.m index 49f4ceb9384..746bf44f785 100644 --- a/jdk/src/macosx/native/sun/awt/CPopupMenu.m +++ b/jdk/src/macosx/native/sun/awt/CPopupMenu.m @@ -64,7 +64,7 @@ JNF_COCOA_ENTER(env); jobject cPeerObjGlobal = JNFNewGlobalRef(env, peer); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ aCPopupMenu = [[CPopupMenu alloc] initWithPeer:cPeerObjGlobal]; CFRetain(aCPopupMenu); [aCPopupMenu release]; @@ -82,7 +82,7 @@ JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPopupMenu_nativeShowPopupMenu CPopupMenu* cPopupMenu = (CPopupMenu*)jlong_to_ptr(menuPtr); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ NSPoint loc = ConvertNSScreenPoint(env, NSMakePoint(x, y)); [[cPopupMenu menu] popUpMenuPositioningItem: nil diff --git a/jdk/src/macosx/native/sun/awt/CTrayIcon.m b/jdk/src/macosx/native/sun/awt/CTrayIcon.m index a69995f84aa..fbc711534e9 100644 --- a/jdk/src/macosx/native/sun/awt/CTrayIcon.m +++ b/jdk/src/macosx/native/sun/awt/CTrayIcon.m @@ -303,10 +303,9 @@ JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CTrayIcon_nativeCreate __block AWTTrayIcon *trayIcon = nil; JNF_COCOA_ENTER(env); -AWT_ASSERT_NOT_APPKIT_THREAD; jobject thePeer = JNFNewGlobalRef(env, peer); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ trayIcon = [[AWTTrayIcon alloc] initWithPeer:thePeer]; }]; @@ -334,11 +333,10 @@ JNIEXPORT void JNICALL Java_java_awt_TrayIcon_initIDs JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTrayIcon_nativeSetToolTip (JNIEnv *env, jobject self, jlong model, jstring jtooltip) { JNF_COCOA_ENTER(env); -AWT_ASSERT_NOT_APPKIT_THREAD; AWTTrayIcon *icon = jlong_to_ptr(model); NSString *tooltip = JNFJavaToNSString(env, jtooltip); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [icon setTooltip:tooltip]; }]; @@ -353,10 +351,9 @@ JNF_COCOA_EXIT(env); JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTrayIcon_setNativeImage (JNIEnv *env, jobject self, jlong model, jlong imagePtr, jboolean autosize) { JNF_COCOA_ENTER(env); -AWT_ASSERT_NOT_APPKIT_THREAD; AWTTrayIcon *icon = jlong_to_ptr(model); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ [icon setImage:jlong_to_ptr(imagePtr) sizing:autosize]; }]; @@ -369,13 +366,10 @@ Java_sun_lwawt_macosx_CTrayIcon_nativeGetIconLocation jobject jpt = NULL; JNF_COCOA_ENTER(env); -AWT_ASSERT_NOT_APPKIT_THREAD; __block NSPoint pt = NSZeroPoint; AWTTrayIcon *icon = jlong_to_ptr(model); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ - AWT_ASSERT_APPKIT_THREAD; - + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ NSPoint loc = [icon getLocationOnScreen]; pt = ConvertNSScreenPoint(env, loc); }]; diff --git a/jdk/src/macosx/native/sun/awt/CWrapper.m b/jdk/src/macosx/native/sun/awt/CWrapper.m index eb047c5bc82..538a0c3f1ff 100644 --- a/jdk/src/macosx/native/sun/awt/CWrapper.m +++ b/jdk/src/macosx/native/sun/awt/CWrapper.m @@ -46,7 +46,7 @@ Java_sun_lwawt_macosx_CWrapper_00024NSObject_release JNF_COCOA_ENTER(env); id obj = (id)jlong_to_ptr(objectPtr); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ CFRelease(obj); }]; @@ -66,10 +66,10 @@ Java_sun_lwawt_macosx_CWrapper_00024NSWindow_makeKeyAndOrderFront JNF_COCOA_ENTER(env); NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr); - [JNFRunLoop performOnMainThread:@selector(makeKeyAndOrderFront:) - on:window - withObject:nil - waitUntilDone:NO]; + [ThreadUtilities performOnMainThread:@selector(makeKeyAndOrderFront:) + on:window + withObject:nil + waitUntilDone:NO]; JNF_COCOA_EXIT(env); } @@ -86,10 +86,10 @@ Java_sun_lwawt_macosx_CWrapper_00024NSWindow_makeKeyWindow JNF_COCOA_ENTER(env); NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr); - [JNFRunLoop performOnMainThread:@selector(makeKeyWindow) - on:window - withObject:nil - waitUntilDone:NO]; + [ThreadUtilities performOnMainThread:@selector(makeKeyWindow) + on:window + withObject:nil + waitUntilDone:NO]; JNF_COCOA_EXIT(env); } @@ -106,10 +106,10 @@ Java_sun_lwawt_macosx_CWrapper_00024NSWindow_makeMainWindow JNF_COCOA_ENTER(env); NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr); - [JNFRunLoop performOnMainThread:@selector(makeMainWindow) - on:window - withObject:nil - waitUntilDone:NO]; + [ThreadUtilities performOnMainThread:@selector(makeMainWindow) + on:window + withObject:nil + waitUntilDone:NO]; JNF_COCOA_EXIT(env); } @@ -128,7 +128,7 @@ Java_sun_lwawt_macosx_CWrapper_00024NSWindow_canBecomeMainWindow JNF_COCOA_ENTER(env); NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ canBecomeMainWindow = [window canBecomeMainWindow]; }]; @@ -151,7 +151,7 @@ Java_sun_lwawt_macosx_CWrapper_00024NSWindow_isKeyWindow JNF_COCOA_ENTER(env); NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ isKeyWindow = [window isKeyWindow]; }]; @@ -172,10 +172,10 @@ Java_sun_lwawt_macosx_CWrapper_00024NSWindow_orderFront JNF_COCOA_ENTER(env); NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr); - [JNFRunLoop performOnMainThread:@selector(orderFront:) - on:window - withObject:window - waitUntilDone:NO]; + [ThreadUtilities performOnMainThread:@selector(orderFront:) + on:window + withObject:window + waitUntilDone:NO]; JNF_COCOA_EXIT(env); } @@ -192,10 +192,10 @@ Java_sun_lwawt_macosx_CWrapper_00024NSWindow_orderOut JNF_COCOA_ENTER(env); NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr); - [JNFRunLoop performOnMainThread:@selector(orderOut:) - on:window - withObject:window - waitUntilDone:NO]; + [ThreadUtilities performOnMainThread:@selector(orderOut:) + on:window + withObject:window + waitUntilDone:NO]; JNF_COCOA_EXIT(env); } @@ -212,10 +212,10 @@ Java_sun_lwawt_macosx_CWrapper_00024NSWindow_orderFrontRegardless JNF_COCOA_ENTER(env); NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr); - [JNFRunLoop performOnMainThread:@selector(orderFrontRegardless) - on:window - withObject:nil - waitUntilDone:NO]; + [ThreadUtilities performOnMainThread:@selector(orderFrontRegardless) + on:window + withObject:nil + waitUntilDone:NO]; JNF_COCOA_EXIT(env); } @@ -233,7 +233,7 @@ JNF_COCOA_ENTER(env); NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr); NSWindow *relativeTo = (NSWindow *)jlong_to_ptr(relativeToPtr); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [window orderWindow:(NSWindowOrderingMode)order relativeTo:[relativeTo windowNumber]]; }]; @@ -267,7 +267,7 @@ JNF_COCOA_ENTER(env); initLevels(); NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [window setLevel: LEVELS[level]]; }]; } else { @@ -290,7 +290,7 @@ JNF_COCOA_ENTER(env); NSWindow *parent = (NSWindow *)jlong_to_ptr(parentPtr); NSWindow *child = (NSWindow *)jlong_to_ptr(childPtr); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [parent addChildWindow:child ordered:order]; }]; @@ -310,10 +310,10 @@ JNF_COCOA_ENTER(env); AWTWindow *parent = (AWTWindow *)jlong_to_ptr(parentPtr); AWTWindow *child = (AWTWindow *)jlong_to_ptr(childPtr); - [JNFRunLoop performOnMainThread:@selector(removeChildWindow:) - on:parent - withObject:child - waitUntilDone:NO]; + [ThreadUtilities performOnMainThread:@selector(removeChildWindow:) + on:parent + withObject:child + waitUntilDone:NO]; JNF_COCOA_EXIT(env); } @@ -331,7 +331,7 @@ JNF_COCOA_ENTER(env); AWTWindow *window = (AWTWindow *)jlong_to_ptr(windowPtr); NSRect frame = NSMakeRect(x, y, w, h); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [window setFrame:frame display:display]; }]; @@ -350,7 +350,7 @@ Java_sun_lwawt_macosx_CWrapper_00024NSWindow_setAlphaValue JNF_COCOA_ENTER(env); AWTWindow *window = (AWTWindow *)jlong_to_ptr(windowPtr); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [window setAlphaValue:(CGFloat)alpha]; }]; @@ -369,7 +369,7 @@ Java_sun_lwawt_macosx_CWrapper_00024NSWindow_setOpaque JNF_COCOA_ENTER(env); AWTWindow *window = (AWTWindow *)jlong_to_ptr(windowPtr); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [window setOpaque:(BOOL)opaque]; }]; @@ -389,7 +389,7 @@ JNF_COCOA_ENTER(env); AWTWindow *window = (AWTWindow *)jlong_to_ptr(windowPtr); NSColor *color = (NSColor *)jlong_to_ptr(colorPtr); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [window setBackgroundColor:color]; }]; @@ -410,7 +410,7 @@ Java_sun_lwawt_macosx_CWrapper_00024NSWindow_screen JNF_COCOA_ENTER(env); AWTWindow *window = (AWTWindow *)jlong_to_ptr(windowPtr); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ const NSScreen *screen = [window screen]; CFRetain(screen); // GC screenPtr = ptr_to_jlong(screen); @@ -432,10 +432,10 @@ Java_sun_lwawt_macosx_CWrapper_00024NSWindow_miniaturize JNF_COCOA_ENTER(env); NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr); - [JNFRunLoop performOnMainThread:@selector(miniaturize:) - on:window - withObject:nil - waitUntilDone:NO]; + [ThreadUtilities performOnMainThread:@selector(miniaturize:) + on:window + withObject:nil + waitUntilDone:NO]; JNF_COCOA_EXIT(env); } @@ -452,10 +452,10 @@ Java_sun_lwawt_macosx_CWrapper_00024NSWindow_deminiaturize JNF_COCOA_ENTER(env); NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr); - [JNFRunLoop performOnMainThread:@selector(deminiaturize:) - on:window - withObject:nil - waitUntilDone:NO]; + [ThreadUtilities performOnMainThread:@selector(deminiaturize:) + on:window + withObject:nil + waitUntilDone:NO]; JNF_COCOA_EXIT(env); } @@ -472,10 +472,10 @@ Java_sun_lwawt_macosx_CWrapper_00024NSWindow_zoom JNF_COCOA_ENTER(env); NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr); - [JNFRunLoop performOnMainThread:@selector(zoom:) - on:window - withObject:nil - waitUntilDone:NO]; + [ThreadUtilities performOnMainThread:@selector(zoom:) + on:window + withObject:nil + waitUntilDone:NO]; JNF_COCOA_EXIT(env); } @@ -493,10 +493,10 @@ JNF_COCOA_ENTER(env); NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr); NSResponder *responder = (NSResponder *)jlong_to_ptr(responderPtr); - [JNFRunLoop performOnMainThread:@selector(makeFirstResponder:) - on:window - withObject:responder - waitUntilDone:NO]; + [ThreadUtilities performOnMainThread:@selector(makeFirstResponder:) + on:window + withObject:responder + waitUntilDone:NO]; JNF_COCOA_EXIT(env); } @@ -514,7 +514,7 @@ JNF_COCOA_ENTER(env); NSView *view = (NSView *)jlong_to_ptr(viewPtr); NSView *subview = (NSView *)jlong_to_ptr(subviewPtr); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ [view addSubview:subview]; }]; @@ -533,10 +533,10 @@ Java_sun_lwawt_macosx_CWrapper_00024NSView_removeFromSuperview JNF_COCOA_ENTER(env); NSView *view = (NSView *)jlong_to_ptr(viewPtr); - [JNFRunLoop performOnMainThread:@selector(removeFromSuperview) - on:view - withObject:nil - waitUntilDone:NO]; + [ThreadUtilities performOnMainThread:@selector(removeFromSuperview) + on:view + withObject:nil + waitUntilDone:NO]; JNF_COCOA_EXIT(env); } @@ -553,7 +553,7 @@ Java_sun_lwawt_macosx_CWrapper_00024NSView_setFrame JNF_COCOA_ENTER(env); NSView *view = (NSView *)jlong_to_ptr(viewPtr); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [view setFrame:NSMakeRect(x, y, w, h)]; }]; @@ -576,7 +576,7 @@ JNF_COCOA_ENTER(env); __block NSRect rect = NSZeroRect; NSView *view = (NSView *)jlong_to_ptr(viewPtr); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ rect = [view frame]; }]; @@ -599,7 +599,7 @@ Java_sun_lwawt_macosx_CWrapper_00024NSView_enterFullScreenMode JNF_COCOA_ENTER(env); NSView *view = (NSView *)jlong_to_ptr(viewPtr); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ NSScreen *screen = [[view window] screen]; NSDictionary *opts = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO], NSFullScreenModeAllScreens, nil]; [view enterFullScreenMode:screen withOptions:opts]; @@ -620,7 +620,7 @@ Java_sun_lwawt_macosx_CWrapper_00024NSView_exitFullScreenMode JNF_COCOA_ENTER(env); NSView *view = (NSView *)jlong_to_ptr(viewPtr); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [view exitFullScreenModeWithOptions:nil]; }]; @@ -641,7 +641,7 @@ Java_sun_lwawt_macosx_CWrapper_00024NSView_window JNF_COCOA_ENTER(env); NSView *view = (NSView *)jlong_to_ptr(viewPtr); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ windowPtr = ptr_to_jlong([view window]); }]; @@ -655,14 +655,14 @@ JNF_COCOA_EXIT(env); * Method: setHidden * Signature: (JZ)V */ -JNIEXPORT jlong JNICALL +JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CWrapper_00024NSView_setHidden (JNIEnv *env, jclass cls, jlong viewPtr, jboolean toHide) { JNF_COCOA_ENTER(env); NSView *view = (NSView *)jlong_to_ptr(viewPtr); - [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:NO block:^(){ [view setHidden:(BOOL)toHide]; }]; @@ -686,7 +686,7 @@ JNF_COCOA_ENTER(env); __block NSRect rect = NSZeroRect; NSScreen *screen = (NSScreen *)jlong_to_ptr(screenPtr); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ rect = [screen frame]; }]; @@ -713,7 +713,7 @@ JNF_COCOA_ENTER(env); __block NSRect rect = NSZeroRect; NSScreen *screen = (NSScreen *)jlong_to_ptr(screenPtr); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ rect = [screen visibleFrame]; }]; @@ -737,7 +737,7 @@ Java_sun_lwawt_macosx_CWrapper_00024NSScreen_screenByDisplayId JNF_COCOA_ENTER(env); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ NSArray *screens = [NSScreen screens]; for (NSScreen *screen in screens) { NSDictionary *screenInfo = [screen deviceDescription]; @@ -768,7 +768,7 @@ Java_sun_lwawt_macosx_CWrapper_00024NSColor_clearColor JNF_COCOA_ENTER(env); - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^(){ + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ clearColorPtr = ptr_to_jlong([NSColor clearColor]); }]; diff --git a/jdk/src/macosx/native/sun/awt/JavaComponentAccessibility.m b/jdk/src/macosx/native/sun/awt/JavaComponentAccessibility.m index 61ba619af40..f886c4afbe1 100644 --- a/jdk/src/macosx/native/sun/awt/JavaComponentAccessibility.m +++ b/jdk/src/macosx/native/sun/awt/JavaComponentAccessibility.m @@ -1147,7 +1147,6 @@ static NSObject *sAttributeNamesLOCK = nil; JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessibility_focusChanged (JNIEnv *env, jobject jthis) { - AWT_ASSERT_NOT_APPKIT_THREAD; JNF_COCOA_ENTER(env); [ThreadUtilities performOnMainThread:@selector(postFocusChanged:) onObject:[JavaComponentAccessibility class] withObject:nil waitUntilDone:NO awtMode:NO]; @@ -1164,7 +1163,6 @@ JNF_COCOA_EXIT(env); JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_valueChanged (JNIEnv *env, jclass jklass, jlong element) { - AWT_ASSERT_NOT_APPKIT_THREAD; JNF_COCOA_ENTER(env); [ThreadUtilities performOnMainThread:@selector(postValueChanged) onObject:(JavaComponentAccessibility *)jlong_to_ptr(element) withObject:nil waitUntilDone:NO awtMode:NO]; JNF_COCOA_EXIT(env); @@ -1178,7 +1176,6 @@ JNF_COCOA_EXIT(env); JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_selectionChanged (JNIEnv *env, jclass jklass, jlong element) { - AWT_ASSERT_NOT_APPKIT_THREAD; JNF_COCOA_ENTER(env); [ThreadUtilities performOnMainThread:@selector(postSelectionChanged) onObject:(JavaComponentAccessibility *)jlong_to_ptr(element) withObject:nil waitUntilDone:NO awtMode:NO]; JNF_COCOA_EXIT(env); @@ -1193,7 +1190,6 @@ JNF_COCOA_EXIT(env); JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_unregisterFromCocoaAXSystem (JNIEnv *env, jclass jklass, jlong element) { - AWT_ASSERT_NOT_APPKIT_THREAD; JNF_COCOA_ENTER(env); [ThreadUtilities performOnMainThread:@selector(unregisterFromCocoaAXSystem) onObject:(JavaComponentAccessibility *)jlong_to_ptr(element) withObject:nil waitUntilDone:NO awtMode:NO]; JNF_COCOA_EXIT(env); diff --git a/jdk/src/macosx/native/sun/awt/LWCToolkit.m b/jdk/src/macosx/native/sun/awt/LWCToolkit.m index b58cfc0627d..45c546181f0 100644 --- a/jdk/src/macosx/native/sun/awt/LWCToolkit.m +++ b/jdk/src/macosx/native/sun/awt/LWCToolkit.m @@ -415,13 +415,9 @@ JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_isApplicationActive JNF_COCOA_ENTER(env); - if ([NSThread isMainThread]) { + [ThreadUtilities performOnMainThreadWaiting:YES block:^() { active = (jboolean)[NSRunningApplication currentApplication].active; - } else { - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:^() { - active = (jboolean)[NSRunningApplication currentApplication].active; - }]; - } + }]; JNF_COCOA_EXIT(env); diff --git a/jdk/src/macosx/native/sun/awt/awt.m b/jdk/src/macosx/native/sun/awt/awt.m index bf8705f039e..ea684ed0808 100644 --- a/jdk/src/macosx/native/sun/awt/awt.m +++ b/jdk/src/macosx/native/sun/awt/awt.m @@ -315,14 +315,9 @@ AWT_ASSERT_APPKIT_THREAD; // Don't set the delegate until the NSApplication has been created and // its finishLaunching has initialized it. // ApplicationDelegate is the support code for com.apple.eawt. - void (^setDelegateBlock)() = ^(){ + [ThreadUtilities performOnMainThreadWaiting:YES block:^(){ OSXAPP_SetApplicationDelegate([ApplicationDelegate sharedDelegate]); - }; - if (onMainThread) { - setDelegateBlock(); - } else { - [JNFRunLoop performOnMainThreadWaiting:YES withBlock:setDelegateBlock]; - } + }]; } - (void)starter:(NSArray*)args { diff --git a/jdk/src/macosx/native/sun/osxapp/ThreadUtilities.h b/jdk/src/macosx/native/sun/osxapp/ThreadUtilities.h index 0acde2f9aa6..c0d2054f6e3 100644 --- a/jdk/src/macosx/native/sun/osxapp/ThreadUtilities.h +++ b/jdk/src/macosx/native/sun/osxapp/ThreadUtilities.h @@ -98,8 +98,6 @@ do { \ } \ } while (0) -#define AWT_ASSERT_ANY_THREAD - #endif /* AWT_THREAD_ASSERTS_MESSAGES */ #ifdef AWT_THREAD_ASSERTS_WAIT @@ -114,15 +112,12 @@ do { \ while (pthread_main_np() != 0) {} \ } while (0) -#define AWT_ASSERT_ANY_THREAD - #endif /* AWT_THREAD_ASSERTS_WAIT */ #else /* AWT_THREAD_ASSERTS */ #define AWT_ASSERT_APPKIT_THREAD do {} while (0) #define AWT_ASSERT_NOT_APPKIT_THREAD do {} while (0) -#define AWT_ASSERT_ANY_THREAD #endif /* AWT_THREAD_ASSERTS */ // -------------------------------------------------------------------------- @@ -139,7 +134,10 @@ __attribute__((visibility("default"))) + (JNIEnv*)getJNIEnvUncached; + (void)performOnMainThread:(SEL)aSelector onObject:(id)target withObject:(id)arg waitUntilDone:(BOOL)wait awtMode:(BOOL)inAWT; + +//Wrappers for the corresponding JNFRunLoop methods with a check for main thread + (void)performOnMainThreadWaiting:(BOOL)wait block:(void (^)())block; ++ (void)performOnMainThread:(SEL)aSelector on:(id)target withObject:(id)arg waitUntilDone:(BOOL)wait; @end void OSXAPP_SetJavaVM(JavaVM *vm); diff --git a/jdk/src/macosx/native/sun/osxapp/ThreadUtilities.m b/jdk/src/macosx/native/sun/osxapp/ThreadUtilities.m index 0e40fc1a52b..0b42f1b5896 100644 --- a/jdk/src/macosx/native/sun/osxapp/ThreadUtilities.m +++ b/jdk/src/macosx/native/sun/osxapp/ThreadUtilities.m @@ -37,27 +37,13 @@ static JNIEnv *appKitEnv = NULL; static NSArray *sPerformModes = nil; static NSArray *sAWTPerformModes = nil; -static BOOL sCocoaComponentCompatibility = NO; -static NSTimeInterval sCocoaComponentCompatibilityTimeout = 0.5; static BOOL sLoggingEnabled = YES; #ifdef AWT_THREAD_ASSERTS_ENV_ASSERT int sAWTThreadAsserts = 0; #endif /* AWT_THREAD_ASSERTS_ENV_ASSERT */ - -// This is for backward compatibility for those people using CocoaComponent -// Since we've flipped the AWT threading model for Tiger (10.4), all the rules -// for CocoaComponent are wrong. -// So for existing CocoaComponent users, we can't be synchronous. -// Making things totally asynchronous breaks a _lot_, so we try to be -// synchronous and time out after a little bit. -#define NOT_READY 0 -#define READY 1 -#define IN_PROGRESS 2 - BOOL sInPerformFromJava = NO; -NSUInteger sPerformCount = 0; // This class is used so that performSelectorOnMainThread can be // controlled a little more easily by us. It has 2 roles. @@ -73,8 +59,6 @@ NSUInteger sPerformCount = 0; - (id) initWithTarget:(id)target selector:(SEL)selector arg:(id)arg wait:(BOOL)wait; - (void) perform; -- (void) performCompatible; -- (void) _performCompatible:(NSConditionLock *)resultLock; @end @@ -113,8 +97,6 @@ NSUInteger sPerformCount = 0; sInPerformFromJava = YES; } - sPerformCount++; - // Actually do the work (cheat to avoid a method call) @try { objc_msgSend(fTarget, fSelector, fArg); @@ -128,69 +110,6 @@ NSUInteger sPerformCount = 0; } } } - -- (void) performCompatible { - // We check if we are on the AppKit thread because frequently, apps - // using CocoaComponent are doing things on the wrong thread! - if (pthread_main_np()) { - [fTarget performSelector:fSelector withObject:fArg]; - } else { - // Setup the lock - NSConditionLock *resultLock = - [[NSConditionLock alloc] initWithCondition:NOT_READY]; - - // Make sure that if we return early, nothing gets released out - // from under us - [resultLock retain]; - [fTarget retain]; - [fArg retain]; - [self retain]; - // Do an asynchronous perform to the main thread. - [self performSelectorOnMainThread:@selector(_performCompatible:) - withObject:resultLock waitUntilDone:NO modes:sAWTPerformModes]; - - // Wait for a little bit for it to finish - [resultLock lockWhenCondition:READY beforeDate:[NSDate dateWithTimeIntervalSinceNow:sCocoaComponentCompatibilityTimeout]]; - - // If the _performCompatible is actually in progress, - // we should let it finish - if ([resultLock condition] == IN_PROGRESS) { - [resultLock lockWhenCondition:READY]; - } - - if ([resultLock condition] == NOT_READY && sLoggingEnabled) { - NSLog(@"[Java CocoaComponent compatibility mode]: Operation timed out due to possible deadlock: selector '%@' on target '%@' with args '%@'", NSStringFromSelector(fSelector), fTarget, fArg); - } - - [resultLock unlock]; - [resultLock autorelease]; - } -} - -- (void) _performCompatible:(NSConditionLock *)resultLock { - // notify that the perform is in progress! - [resultLock lock]; - [resultLock unlockWithCondition:IN_PROGRESS]; - - sPerformCount++; - - // Actually do the work. - @try { - [fTarget performSelector:fSelector withObject:fArg]; - } @catch (NSException *e) { - NSLog(@"*** CPerformer: ignoring exception '%@' raised during performCompatible of selector '%@' on target '%@' with args '%@'", e, NSStringFromSelector(fSelector), fTarget, fArg); - } @finally { - // notify done! - [resultLock lock]; - [resultLock unlockWithCondition:READY]; - - // Clean up after ourselves - [resultLock autorelease]; - [fTarget autorelease]; - [fArg autorelease]; - [self autorelease]; - } -} @end @@ -236,13 +155,8 @@ AWT_ASSERT_APPKIT_THREAD; // java event thread without deadlocking. See CToolkit.invokeAndWait. + (void)performOnMainThread:(SEL)aSelector onObject:(id)target withObject:(id)arg waitUntilDone:(BOOL)wait awtMode:(BOOL)inAWT { CPerformer *performer = [[CPerformer alloc] initWithTarget:target selector:aSelector arg:arg wait:wait]; - if (sCocoaComponentCompatibility && wait && inAWT) { - [performer performCompatible]; - [performer autorelease]; - } else { - [performer performSelectorOnMainThread:@selector(perform) withObject:nil waitUntilDone:wait modes:((inAWT) ? sAWTPerformModes : sPerformModes)]; // AWT_THREADING Safe (cover method) - [performer release]; - } + [performer performSelectorOnMainThread:@selector(perform) withObject:nil waitUntilDone:wait modes:((inAWT) ? sAWTPerformModes : sPerformModes)]; // AWT_THREADING Safe (cover method) + [performer release]; } + (void)performOnMainThreadWaiting:(BOOL)wait block:(void (^)())block { @@ -253,6 +167,14 @@ AWT_ASSERT_APPKIT_THREAD; } } ++ (void)performOnMainThread:(SEL)aSelector on:(id)target withObject:(id)arg waitUntilDone:(BOOL)wait { + if ([NSThread isMainThread] && wait == YES) { + [target performSelector:aSelector withObject:arg]; + } else { + [JNFRunLoop performOnMainThread:aSelector on:target withObject:arg waitUntilDone:wait]; + } +} + @end From ca94a86847db79d4ddfe3585a2ac0786cd6027f7 Mon Sep 17 00:00:00 2001 From: Anton Litvinov Date: Fri, 18 Jan 2013 18:34:46 +0400 Subject: [PATCH 069/138] 8006417: JComboBox.showPopup(), hidePopup() fails in JRE 1.7 on OS X Reviewed-by: art, serb --- .../macosx/classes/sun/lwawt/LWToolkit.java | 4 +- .../classes/sun/lwawt/LWWindowPeer.java | 12 ++- .../ShowPopupAfterHidePopupTest.java | 78 +++++++++++++++++++ 3 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 jdk/test/javax/swing/JComboBox/ShowPopupAfterHidePopupTest/ShowPopupAfterHidePopupTest.java diff --git a/jdk/src/macosx/classes/sun/lwawt/LWToolkit.java b/jdk/src/macosx/classes/sun/lwawt/LWToolkit.java index 57a8642b6d0..89f39fccbf8 100644 --- a/jdk/src/macosx/classes/sun/lwawt/LWToolkit.java +++ b/jdk/src/macosx/classes/sun/lwawt/LWToolkit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -539,7 +539,7 @@ public abstract class LWToolkit extends SunToolkit implements Runnable { @Override public void ungrab(Window w) { if (w.getPeer() != null) { - ((LWWindowPeer)w.getPeer()).ungrab(); + ((LWWindowPeer)w.getPeer()).ungrab(false); } } } diff --git a/jdk/src/macosx/classes/sun/lwawt/LWWindowPeer.java b/jdk/src/macosx/classes/sun/lwawt/LWWindowPeer.java index 9d583e65749..c11b626a764 100644 --- a/jdk/src/macosx/classes/sun/lwawt/LWWindowPeer.java +++ b/jdk/src/macosx/classes/sun/lwawt/LWWindowPeer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1208,13 +1208,19 @@ public class LWWindowPeer grabbingWindow = this; } - void ungrab() { + final void ungrab(boolean doPost) { if (isGrabbing()) { grabbingWindow = null; - postEvent(new UngrabEvent(getTarget())); + if (doPost) { + postEvent(new UngrabEvent(getTarget())); + } } } + void ungrab() { + ungrab(true); + } + private boolean isGrabbing() { return this == grabbingWindow; } diff --git a/jdk/test/javax/swing/JComboBox/ShowPopupAfterHidePopupTest/ShowPopupAfterHidePopupTest.java b/jdk/test/javax/swing/JComboBox/ShowPopupAfterHidePopupTest/ShowPopupAfterHidePopupTest.java new file mode 100644 index 00000000000..e015f5ed587 --- /dev/null +++ b/jdk/test/javax/swing/JComboBox/ShowPopupAfterHidePopupTest/ShowPopupAfterHidePopupTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + @bug 8006417 + @summary JComboBox.showPopup(), hidePopup() fails in JRE 1.7 on OS X + @author Anton Litvinov +*/ + +import java.awt.*; + +import javax.swing.*; +import javax.swing.plaf.metal.*; + +import sun.awt.SunToolkit; + +public class ShowPopupAfterHidePopupTest { + private static JFrame frame = null; + private static JComboBox comboBox = null; + private static boolean popupIsVisible = false; + + public static void main(String[] args) throws Exception { + UIManager.setLookAndFeel(new MetalLookAndFeel()); + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + frame = new JFrame("Popup Menu of JComboBox"); + comboBox = new JComboBox(new String[]{"Item1", "Item2", "Item3"}); + frame.getContentPane().add(comboBox); + frame.pack(); + frame.setVisible(true); + } + }); + final SunToolkit toolkit = (SunToolkit)Toolkit.getDefaultToolkit(); + toolkit.realSync(); + + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + comboBox.showPopup(); + comboBox.hidePopup(); + comboBox.showPopup(); + } + }); + toolkit.realSync(); + + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + popupIsVisible = comboBox.isPopupVisible(); + frame.dispose(); + } + }); + if (!popupIsVisible) { + throw new RuntimeException("Calling hidePopup() affected the next call to showPopup()."); + } + } +} From 9bad85cb031937355fe2fa9cbc29d17a717f9c34 Mon Sep 17 00:00:00 2001 From: John Zavgren Date: Fri, 18 Jan 2013 17:34:40 +0000 Subject: [PATCH 070/138] 8005120: Compiler warnings in socket transport native code Reviewed-by: chegar, dsamersoff --- .../share/transport/socket/socketTransport.c | 7 ++-- jdk/src/share/transport/socket/sysSocket.h | 23 ++++++------- jdk/src/solaris/transport/socket/socket_md.c | 22 ++++++------- jdk/src/windows/transport/socket/socket_md.c | 32 +++++++++---------- 4 files changed, 43 insertions(+), 41 deletions(-) diff --git a/jdk/src/share/transport/socket/socketTransport.c b/jdk/src/share/transport/socket/socketTransport.c index e3a50d93ecd..666ce2d6d6b 100644 --- a/jdk/src/share/transport/socket/socketTransport.c +++ b/jdk/src/share/transport/socket/socketTransport.c @@ -304,7 +304,7 @@ socketTransport_startListening(jdwpTransportEnv* env, const char* address, { char buf[20]; - int len = sizeof(sa); + socklen_t len = sizeof(sa); jint portNum; err = dbgsysGetSocketName(serverSocketFD, (struct sockaddr *)&sa, &len); @@ -324,7 +324,8 @@ socketTransport_startListening(jdwpTransportEnv* env, const char* address, static jdwpTransportError JNICALL socketTransport_accept(jdwpTransportEnv* env, jlong acceptTimeout, jlong handshakeTimeout) { - int socketLen, err; + socklen_t socketLen; + int err; struct sockaddr_in socket; jlong startTime = (jlong)0; @@ -508,7 +509,7 @@ socketTransport_close(jdwpTransportEnv* env) if (dbgsysSocketClose(fd) < 0) { /* * close failed - it's pointless to restore socketFD here because - * any subsequent close will likely fail aswell. + * any subsequent close will likely fail as well. */ RETURN_IO_ERROR("close failed"); } diff --git a/jdk/src/share/transport/socket/sysSocket.h b/jdk/src/share/transport/socket/sysSocket.h index b40267ab432..f80cc67a1de 100644 --- a/jdk/src/share/transport/socket/sysSocket.h +++ b/jdk/src/share/transport/socket/sysSocket.h @@ -34,28 +34,29 @@ #define DBG_EINPROGRESS -150 #define DBG_ETIMEOUT -200 +#ifdef WIN32 +typedef int socklen_t; +#endif int dbgsysSocketClose(int fd); -int dbgsysConnect(int fd, struct sockaddr *him, int len); -int dbgsysFinishConnect(int fd, long timeout); -int dbgsysAccept(int fd, struct sockaddr *him, int *len); -int dbgsysSendTo(int fd, char *buf, int len, int flags, struct sockaddr *to, - int tolen); -int dbgsysRecvFrom(int fd, char *buf, int nbytes, int flags, - struct sockaddr *from, int *fromlen); +int dbgsysConnect(int fd, struct sockaddr *him, socklen_t len); +int dbgsysFinishConnect(int fd, int timeout); +int dbgsysAccept(int fd, struct sockaddr *him, socklen_t *len); +int dbgsysSendTo(int fd, char *buf, size_t len, int flags, struct sockaddr *to, socklen_t tolen); +int dbgsysRecvFrom(int fd, char *buf, size_t nBytes, int flags, struct sockaddr *from, socklen_t *fromlen); int dbgsysListen(int fd, int backlog); -int dbgsysRecv(int fd, char *buf, int nBytes, int flags); -int dbgsysSend(int fd, char *buf, int nBytes, int flags); +int dbgsysRecv(int fd, char *buf, size_t nBytes, int flags); +int dbgsysSend(int fd, char *buf, size_t nBytes, int flags); struct hostent *dbgsysGetHostByName(char *hostname); int dbgsysSocket(int domain, int type, int protocol); -int dbgsysBind(int fd, struct sockaddr *name, int namelen); +int dbgsysBind(int fd, struct sockaddr *name, socklen_t namelen); int dbgsysSetSocketOption(int fd, jint cmd, jboolean on, jvalue value); uint32_t dbgsysInetAddr(const char* cp); uint32_t dbgsysHostToNetworkLong(uint32_t hostlong); unsigned short dbgsysHostToNetworkShort(unsigned short hostshort); uint32_t dbgsysNetworkToHostLong(uint32_t netlong); unsigned short dbgsysNetworkToHostShort(unsigned short netshort); -int dbgsysGetSocketName(int fd, struct sockaddr *him, int *len); +int dbgsysGetSocketName(int fd, struct sockaddr *him, socklen_t *len); int dbgsysConfigureBlocking(int fd, jboolean blocking); int dbgsysPoll(int fd, jboolean rd, jboolean wr, long timeout); int dbgsysGetLastIOError(char *buf, jint size); diff --git a/jdk/src/solaris/transport/socket/socket_md.c b/jdk/src/solaris/transport/socket/socket_md.c index 74e8d846c7b..715b7ac066a 100644 --- a/jdk/src/solaris/transport/socket/socket_md.c +++ b/jdk/src/solaris/transport/socket/socket_md.c @@ -49,7 +49,7 @@ dbgsysListen(int fd, int backlog) { } int -dbgsysConnect(int fd, struct sockaddr *name, int namelen) { +dbgsysConnect(int fd, struct sockaddr *name, socklen_t namelen) { int rv = connect(fd, name, namelen); if (rv < 0 && (errno == EINPROGRESS || errno == EINTR)) { return DBG_EINPROGRESS; @@ -59,7 +59,7 @@ dbgsysConnect(int fd, struct sockaddr *name, int namelen) { } int -dbgsysFinishConnect(int fd, long timeout) { +dbgsysFinishConnect(int fd, int timeout) { int rv = dbgsysPoll(fd, 0, 1, timeout); if (rv == 0) { return DBG_ETIMEOUT; @@ -71,7 +71,7 @@ dbgsysFinishConnect(int fd, long timeout) { } int -dbgsysAccept(int fd, struct sockaddr *name, int *namelen) { +dbgsysAccept(int fd, struct sockaddr *name, socklen_t *namelen) { int rv; for (;;) { rv = accept(fd, name, namelen); @@ -85,8 +85,8 @@ dbgsysAccept(int fd, struct sockaddr *name, int *namelen) { } int -dbgsysRecvFrom(int fd, char *buf, int nBytes, - int flags, struct sockaddr *from, int *fromlen) { +dbgsysRecvFrom(int fd, char *buf, size_t nBytes, + int flags, struct sockaddr *from, socklen_t *fromlen) { int rv; do { rv = recvfrom(fd, buf, nBytes, flags, from, fromlen); @@ -96,8 +96,8 @@ dbgsysRecvFrom(int fd, char *buf, int nBytes, } int -dbgsysSendTo(int fd, char *buf, int len, - int flags, struct sockaddr *to, int tolen) { +dbgsysSendTo(int fd, char *buf, size_t len, + int flags, struct sockaddr *to, socklen_t tolen) { int rv; do { rv = sendto(fd, buf, len, flags, to, tolen); @@ -107,7 +107,7 @@ dbgsysSendTo(int fd, char *buf, int len, } int -dbgsysRecv(int fd, char *buf, int nBytes, int flags) { +dbgsysRecv(int fd, char *buf, size_t nBytes, int flags) { int rv; do { rv = recv(fd, buf, nBytes, flags); @@ -117,7 +117,7 @@ dbgsysRecv(int fd, char *buf, int nBytes, int flags) { } int -dbgsysSend(int fd, char *buf, int nBytes, int flags) { +dbgsysSend(int fd, char *buf, size_t nBytes, int flags) { int rv; do { rv = send(fd, buf, nBytes, flags); @@ -151,7 +151,7 @@ int dbgsysSocketClose(int fd) { } int -dbgsysBind(int fd, struct sockaddr *name, int namelen) { +dbgsysBind(int fd, struct sockaddr *name, socklen_t namelen) { return bind(fd, name, namelen); } @@ -171,7 +171,7 @@ dbgsysNetworkToHostShort(unsigned short netshort) { } int -dbgsysGetSocketName(int fd, struct sockaddr *name, int *namelen) { +dbgsysGetSocketName(int fd, struct sockaddr *name, socklen_t *namelen) { return getsockname(fd, name, namelen); } diff --git a/jdk/src/windows/transport/socket/socket_md.c b/jdk/src/windows/transport/socket/socket_md.c index 5b025cf2f4e..8ac98c96aae 100644 --- a/jdk/src/windows/transport/socket/socket_md.c +++ b/jdk/src/windows/transport/socket/socket_md.c @@ -125,7 +125,7 @@ dbgsysListen(int fd, int backlog) { } int -dbgsysConnect(int fd, struct sockaddr *name, int namelen) { +dbgsysConnect(int fd, struct sockaddr *name, socklen_t namelen) { int rv = connect(fd, name, namelen); if (rv == SOCKET_ERROR) { if (WSAGetLastError() == WSAEINPROGRESS || WSAGetLastError() == WSAEWOULDBLOCK) { @@ -135,7 +135,7 @@ dbgsysConnect(int fd, struct sockaddr *name, int namelen) { return rv; } -int dbgsysFinishConnect(int fd, long timeout) { +int dbgsysFinishConnect(int fd, int timeout) { int rv; struct timeval t; fd_set wr, ex; @@ -171,30 +171,30 @@ int dbgsysFinishConnect(int fd, long timeout) { int -dbgsysAccept(int fd, struct sockaddr *name, int *namelen) { +dbgsysAccept(int fd, struct sockaddr *name, socklen_t *namelen) { return (int)accept(fd, name, namelen); } int -dbgsysRecvFrom(int fd, char *buf, int nBytes, - int flags, struct sockaddr *from, int *fromlen) { - return recvfrom(fd, buf, nBytes, flags, from, fromlen); +dbgsysRecvFrom(int fd, char *buf, size_t nBytes, + int flags, struct sockaddr *from, socklen_t *fromlen) { + return recvfrom(fd, buf, (int)nBytes, flags, from, fromlen); } int -dbgsysSendTo(int fd, char *buf, int len, - int flags, struct sockaddr *to, int tolen) { - return sendto(fd, buf, len, flags, to, tolen); +dbgsysSendTo(int fd, char *buf, size_t len, + int flags, struct sockaddr *to, socklen_t tolen) { + return sendto(fd, buf, (int)len, flags, to, tolen); } int -dbgsysRecv(int fd, char *buf, int nBytes, int flags) { - return recv(fd, buf, nBytes, flags); +dbgsysRecv(int fd, char *buf, size_t nBytes, int flags) { + return recv(fd, buf, (int) nBytes, flags); } int -dbgsysSend(int fd, char *buf, int nBytes, int flags) { - return send(fd, buf, nBytes, flags); +dbgsysSend(int fd, char *buf, size_t nBytes, int flags) { + return send(fd, buf, (int)nBytes, flags); } struct hostent * @@ -232,7 +232,7 @@ dbgsysSocketClose(int fd) { /* Additions to original follow */ int -dbgsysBind(int fd, struct sockaddr *name, int namelen) { +dbgsysBind(int fd, struct sockaddr *name, socklen_t namelen) { return bind(fd, name, namelen); } @@ -253,7 +253,7 @@ dbgsysNetworkToHostShort(unsigned short netshort) { } int -dbgsysGetSocketName(int fd, struct sockaddr *name, int *namelen) { +dbgsysGetSocketName(int fd, struct sockaddr *name, socklen_t *namelen) { return getsockname(fd, name, namelen); } @@ -426,7 +426,7 @@ dbgsysTlsGet(int index) { } #define FT2INT64(ft) \ - ((long)(ft).dwHighDateTime << 32 | (long)(ft).dwLowDateTime) + ((INT64)(ft).dwHighDateTime << 32 | (INT64)(ft).dwLowDateTime) long dbgsysCurrentTimeMillis() { From 69b0c6aad4b42c38615e81542d49acb15ce48781 Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Fri, 18 Jan 2013 18:48:44 +0000 Subject: [PATCH 071/138] 6939260: (fs) BasicFileAttributes.lastModifiedTime() should return last modified time with higher precision Reviewed-by: chegar --- .../sun/nio/fs/UnixFileAttributes.java | 27 +++++++++++++---- .../native/sun/nio/fs/UnixNativeDispatcher.c | 30 +++++++++++++------ .../BasicFileAttributeView/Basic.java | 8 ++--- 3 files changed, 46 insertions(+), 19 deletions(-) diff --git a/jdk/src/solaris/classes/sun/nio/fs/UnixFileAttributes.java b/jdk/src/solaris/classes/sun/nio/fs/UnixFileAttributes.java index a6b79953add..b61b5d6e4a0 100644 --- a/jdk/src/solaris/classes/sun/nio/fs/UnixFileAttributes.java +++ b/jdk/src/solaris/classes/sun/nio/fs/UnixFileAttributes.java @@ -45,9 +45,12 @@ class UnixFileAttributes private int st_uid; private int st_gid; private long st_size; - private long st_atime; - private long st_mtime; - private long st_ctime; + private long st_atime_sec; + private long st_atime_nsec; + private long st_mtime_sec; + private long st_mtime_nsec; + private long st_ctime_sec; + private long st_ctime_nsec; // created lazily private volatile UserPrincipal owner; @@ -101,8 +104,20 @@ class UnixFileAttributes int uid() { return st_uid; } int gid() { return st_gid; } + private static FileTime toFileTime(long sec, long nsec) { + if (nsec == 0) { + return FileTime.from(sec, TimeUnit.SECONDS); + } else { + // truncate to microseconds to avoid overflow with timestamps + // way out into the future. We can re-visit this if FileTime + // is updated to define a from(secs,nsecs) method. + long micro = sec*1000000L + nsec/1000L; + return FileTime.from(micro, TimeUnit.MICROSECONDS); + } + } + FileTime ctime() { - return FileTime.from(st_ctime, TimeUnit.SECONDS); + return toFileTime(st_ctime_sec, st_ctime_nsec); } boolean isDevice() { @@ -114,12 +129,12 @@ class UnixFileAttributes @Override public FileTime lastModifiedTime() { - return FileTime.from(st_mtime, TimeUnit.SECONDS); + return toFileTime(st_mtime_sec, st_mtime_nsec); } @Override public FileTime lastAccessTime() { - return FileTime.from(st_atime, TimeUnit.SECONDS); + return toFileTime(st_atime_sec, st_atime_nsec); } @Override diff --git a/jdk/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c b/jdk/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c index 45d1191c43a..eb4698183df 100644 --- a/jdk/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c +++ b/jdk/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c @@ -90,9 +90,12 @@ static jfieldID attrs_st_nlink; static jfieldID attrs_st_uid; static jfieldID attrs_st_gid; static jfieldID attrs_st_size; -static jfieldID attrs_st_atime; -static jfieldID attrs_st_mtime; -static jfieldID attrs_st_ctime; +static jfieldID attrs_st_atime_sec; +static jfieldID attrs_st_atime_nsec; +static jfieldID attrs_st_mtime_sec; +static jfieldID attrs_st_mtime_nsec; +static jfieldID attrs_st_ctime_sec; +static jfieldID attrs_st_ctime_nsec; static jfieldID attrs_f_frsize; static jfieldID attrs_f_blocks; @@ -183,9 +186,12 @@ Java_sun_nio_fs_UnixNativeDispatcher_init(JNIEnv* env, jclass this) attrs_st_uid = (*env)->GetFieldID(env, clazz, "st_uid", "I"); attrs_st_gid = (*env)->GetFieldID(env, clazz, "st_gid", "I"); attrs_st_size = (*env)->GetFieldID(env, clazz, "st_size", "J"); - attrs_st_atime = (*env)->GetFieldID(env, clazz, "st_atime", "J"); - attrs_st_mtime = (*env)->GetFieldID(env, clazz, "st_mtime", "J"); - attrs_st_ctime = (*env)->GetFieldID(env, clazz, "st_ctime", "J"); + attrs_st_atime_sec = (*env)->GetFieldID(env, clazz, "st_atime_sec", "J"); + attrs_st_atime_nsec = (*env)->GetFieldID(env, clazz, "st_atime_nsec", "J"); + attrs_st_mtime_sec = (*env)->GetFieldID(env, clazz, "st_mtime_sec", "J"); + attrs_st_mtime_nsec = (*env)->GetFieldID(env, clazz, "st_mtime_nsec", "J"); + attrs_st_ctime_sec = (*env)->GetFieldID(env, clazz, "st_ctime_sec", "J"); + attrs_st_ctime_nsec = (*env)->GetFieldID(env, clazz, "st_ctime_nsec", "J"); clazz = (*env)->FindClass(env, "sun/nio/fs/UnixFileStoreAttributes"); if (clazz == NULL) { @@ -395,9 +401,15 @@ static void prepAttributes(JNIEnv* env, struct stat64* buf, jobject attrs) { (*env)->SetIntField(env, attrs, attrs_st_uid, (jint)buf->st_uid); (*env)->SetIntField(env, attrs, attrs_st_gid, (jint)buf->st_gid); (*env)->SetLongField(env, attrs, attrs_st_size, (jlong)buf->st_size); - (*env)->SetLongField(env, attrs, attrs_st_atime, (jlong)buf->st_atime); - (*env)->SetLongField(env, attrs, attrs_st_mtime, (jlong)buf->st_mtime); - (*env)->SetLongField(env, attrs, attrs_st_ctime, (jlong)buf->st_ctime); + (*env)->SetLongField(env, attrs, attrs_st_atime_sec, (jlong)buf->st_atime); + (*env)->SetLongField(env, attrs, attrs_st_mtime_sec, (jlong)buf->st_mtime); + (*env)->SetLongField(env, attrs, attrs_st_ctime_sec, (jlong)buf->st_ctime); + +#if (_POSIX_C_SOURCE >= 200809L) || defined(__solaris__) + (*env)->SetLongField(env, attrs, attrs_st_atime_nsec, (jlong)buf->st_atim.tv_nsec); + (*env)->SetLongField(env, attrs, attrs_st_mtime_nsec, (jlong)buf->st_mtim.tv_nsec); + (*env)->SetLongField(env, attrs, attrs_st_ctime_nsec, (jlong)buf->st_ctim.tv_nsec); +#endif } JNIEXPORT void JNICALL diff --git a/jdk/test/java/nio/file/attribute/BasicFileAttributeView/Basic.java b/jdk/test/java/nio/file/attribute/BasicFileAttributeView/Basic.java index dd005c2c326..40792dfde46 100644 --- a/jdk/test/java/nio/file/attribute/BasicFileAttributeView/Basic.java +++ b/jdk/test/java/nio/file/attribute/BasicFileAttributeView/Basic.java @@ -49,9 +49,9 @@ public class Basic { check(!attrs.isSymbolicLink(), "is not a link"); check(!attrs.isOther(), "is not other"); - // last-modified-time should match java.io.File + // last-modified-time should match java.io.File in seconds File f = new File(dir.toString()); - check(f.lastModified() == attrs.lastModifiedTime().toMillis(), + check(f.lastModified()/1000 == attrs.lastModifiedTime().to(TimeUnit.SECONDS), "last-modified time should be the same"); } @@ -64,10 +64,10 @@ public class Basic { check(!attrs.isSymbolicLink(), "is not a link"); check(!attrs.isOther(), "is not other"); - // size and last-modified-time should match java.io.File + // size and last-modified-time should match java.io.File in seconds File f = new File(file.toString()); check(f.length() == attrs.size(), "size should be the same"); - check(f.lastModified() == attrs.lastModifiedTime().toMillis(), + check(f.lastModified()/1000 == attrs.lastModifiedTime().to(TimeUnit.SECONDS), "last-modified time should be the same"); // copy last-modified time and file create time from directory to file, From a9080d9ab2c2a4b467a2b3528b034e561c095b02 Mon Sep 17 00:00:00 2001 From: Ragini Prasad Date: Fri, 18 Jan 2013 11:31:33 -0800 Subject: [PATCH 072/138] 8000839: Integrate the Java Access Bridge with Java Runtime Reviewed-by: ptbrunet, erikj --- common/bin/compare_exceptions.sh.incl | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/common/bin/compare_exceptions.sh.incl b/common/bin/compare_exceptions.sh.incl index 607e5fd4563..7aefaeecd90 100644 --- a/common/bin/compare_exceptions.sh.incl +++ b/common/bin/compare_exceptions.sh.incl @@ -882,6 +882,17 @@ ACCEPTED_SMALL_SIZE_DIFF=" ./jre/bin/unpack200.exe " +# jabswitch.exe is compiled and linked with incremental turned on in the old +# build. This makes no sense, so it's turned off in the new build. +ACCEPTED_SIZE_DIFF=" +./bin/jabswitch.exe +./jre/bin/jabswitch.exe +" +ACCEPTED_DIS_DIFF=" +./bin/jabswitch.exe +./jre/bin/jabswitch.exe +" + # On windows, there are unavoidable allignment issues making # a perfect disasm diff impossible. Filter out the following: # * Random parts of C++ symbols (this is a bit greedy, but does the trick) From 435bcab6c4dac6881f913048ca0c6dee3d073a11 Mon Sep 17 00:00:00 2001 From: Ragini Prasad Date: Fri, 18 Jan 2013 11:33:31 -0800 Subject: [PATCH 073/138] 8000839: Integrate the Java Access Bridge with Java Runtime Reviewed-by: ptbrunet, erikj --- jdk/make/Makefile | 3 + jdk/make/bridge/AccessBridgeJava/Makefile | 93 ++++++++++++++++ .../bridge/JAWTAccessBridge/Files_cpp.gmk | 29 +++++ jdk/make/bridge/JAWTAccessBridge/Makefile | 69 ++++++++++++ jdk/make/bridge/Jabswitch/Makefile | 63 +++++++++++ jdk/make/bridge/Jaccess/Makefile | 85 ++++++++++++++ .../bridge/JavaAccessBridge/Files_cpp.gmk | 33 ++++++ jdk/make/bridge/JavaAccessBridge/Makefile | 90 +++++++++++++++ jdk/make/bridge/Makefile | 65 +++++++++++ .../bridge/WindowsAccessBridge/Files_cpp.gmk | 35 ++++++ jdk/make/bridge/WindowsAccessBridge/Makefile | 71 ++++++++++++ jdk/makefiles/CompileJavaClasses.gmk | 104 +++++++++++++++--- jdk/makefiles/CompileLaunchers.gmk | 28 +++++ jdk/makefiles/CompileNativeLibraries.gmk | 91 +++++++++++++++ jdk/makefiles/CopyFiles.gmk | 23 +++- jdk/makefiles/CreateJars.gmk | 42 +++++++ jdk/makefiles/GensrcMisc.gmk | 33 ++++++ 17 files changed, 940 insertions(+), 17 deletions(-) create mode 100644 jdk/make/bridge/AccessBridgeJava/Makefile create mode 100644 jdk/make/bridge/JAWTAccessBridge/Files_cpp.gmk create mode 100644 jdk/make/bridge/JAWTAccessBridge/Makefile create mode 100644 jdk/make/bridge/Jabswitch/Makefile create mode 100644 jdk/make/bridge/Jaccess/Makefile create mode 100644 jdk/make/bridge/JavaAccessBridge/Files_cpp.gmk create mode 100644 jdk/make/bridge/JavaAccessBridge/Makefile create mode 100644 jdk/make/bridge/Makefile create mode 100644 jdk/make/bridge/WindowsAccessBridge/Files_cpp.gmk create mode 100644 jdk/make/bridge/WindowsAccessBridge/Makefile diff --git a/jdk/make/Makefile b/jdk/make/Makefile index ceb1492bb4e..c2db5a816a2 100644 --- a/jdk/make/Makefile +++ b/jdk/make/Makefile @@ -237,6 +237,9 @@ SUBDIRS = tools java javax sun com jdk ifeq ($(PLATFORM), macosx) SUBDIRS += apple endif +ifeq ($(PLATFORM), windows) + SUBDIRS += bridge +endif SUBDIRS_tools = launchers SUBDIRS_misc = org jpda diff --git a/jdk/make/bridge/AccessBridgeJava/Makefile b/jdk/make/bridge/AccessBridgeJava/Makefile new file mode 100644 index 00000000000..c5bebd02cea --- /dev/null +++ b/jdk/make/bridge/AccessBridgeJava/Makefile @@ -0,0 +1,93 @@ +# +# Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# +# Makefile for building AccessBridge +# +BUILDDIR = ../.. +PRODUCT = java +PACKAGE = com.sun.java.accessibility + +include $(BUILDDIR)/common/Defs.gmk +JARFILE = $(EXTDIR)/access-bridge$(ABSUFFIX).jar + +ifeq ($(ARCH_DATA_MODEL), 64) + ABPLATFORM = 64bit + ABSUFFIX = -64 +else +ifeq ($(ARCH_DATA_MODEL), 32) +ifdef ABLEGACY + ABSUFFIX = + ABPLATFORM = legacy +else + ABPLATFORM = 32bit + ABSUFFIX = -32 +endif +endif +endif + +# +# Java files to compile. +# +FILES_java = com/sun/java/accessibility/AccessBridge.java + +# +# Location for the newly built classfiles. +# +CLASSDESTDIR = $(TEMPDIR)/classes + +# +# Rules +# +CLASSDESTDIR = $(TEMPDIR)/classes + +FILES_class = $(FILES_java:%.java=$(CLASSDESTDIR)/%.class) + +build: prebuild + +prebuild: + $(CP) $(CLOSED_PLATFORM_SRC)/classes/com/sun/java/accessibility/$(ABPLATFORM)/AccessBridge.java \ + $(CLOSED_PLATFORM_SRC)/classes/com/sun/java/accessibility + +all : build $(JARFILE) + +# +# JAR file +# +$(JARFILE): \ + $(FILES_class) + $(BOOT_JAR_CMD) -cf $(JARFILE) \ + -C $(CLASSDESTDIR) com \ + $(BOOT_JAR_JFLAGS) + @$(java-vm-cleanup) + +# +# Rules +# +include $(BUILDDIR)/common/Classes.gmk + +clean clobber:: + $(RM) -r $(CLASSDESTDIR) \ + $(EXTDIR)/$(JARFILE) diff --git a/jdk/make/bridge/JAWTAccessBridge/Files_cpp.gmk b/jdk/make/bridge/JAWTAccessBridge/Files_cpp.gmk new file mode 100644 index 00000000000..5527f825b9f --- /dev/null +++ b/jdk/make/bridge/JAWTAccessBridge/Files_cpp.gmk @@ -0,0 +1,29 @@ +# +# Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# +# Native files to compile. +FILES_cpp = \ + JAWTAccessBridge.cpp diff --git a/jdk/make/bridge/JAWTAccessBridge/Makefile b/jdk/make/bridge/JAWTAccessBridge/Makefile new file mode 100644 index 00000000000..7ca80afdbf7 --- /dev/null +++ b/jdk/make/bridge/JAWTAccessBridge/Makefile @@ -0,0 +1,69 @@ +# +# Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# +# Makefile for building JAWTAccessBridge +# + +BUILDDIR = ../.. +LIBRARY = JAWTAccessBridge$(ABSUFFIX) +include $(BUILDDIR)/common/Defs.gmk + +# Indicate we want the C++ compiler to do the linking. +CPLUSPLUSLIBRARY=true + +ifeq ($(ARCH_DATA_MODEL), 64) + ABSUFFIX = -64 + ACCESSBRIDGE_ARCH = ACCESSBRIDGE_ARCH_64 + ABRIDGE_MACHINE=X64 +else +ifeq ($(ARCH_DATA_MODEL), 32) + ABRIDGE_MACHINE=I386 +ifdef ABLEGACY + ABSUFFIX = + ACCESSBRIDGE_ARCH = ACCESSBRIDGE_ARCH_LEGACY +else + ABSUFFIX = -32 + ACCESSBRIDGE_ARCH = ACCESSBRIDGE_ARCH_32 +endif +endif +endif + +include FILES_cpp.gmk + +VERSIONINFO_RESOURCE = $(CLOSED_PLATFORM_SRC)/native/sun/bridge/AccessBridgeStatusWindow.rc + +OTHER_CPPFLAGS += -D$(ACCESSBRIDGE_ARCH) -I "$(INCLUDEDIR)" -I "$(PLATFORM_INCLUDE)" +LDLIBS += kernel32.lib user32.lib gdi32.lib winspool.lib jawt.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib \ + uuid.lib odbc32.lib odbccp32.lib /subsystem:windows /dll /incremental:no /machine:$(ABRIDGE_MACHINE) \ + /def:$(CLOSED_PLATFORM_SRC)/native/sun/bridge/JAWTAccessBridge.DEF /libpath:"$(LIBDIR)" + +# +# Rules +# +include $(BUILDDIR)/common/Library.gmk + +vpath %.cpp $(CLOSED_PLATFORM_SRC)/native/sun/bridge +vpath %.RC $(CLOSED_PLATFORM_SRC)/native/sun/bridge diff --git a/jdk/make/bridge/Jabswitch/Makefile b/jdk/make/bridge/Jabswitch/Makefile new file mode 100644 index 00000000000..efd65e07a42 --- /dev/null +++ b/jdk/make/bridge/Jabswitch/Makefile @@ -0,0 +1,63 @@ +# +# Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# +# Makefile for building jabswitch.exe +# + +BUILDDIR = ../.. +PROGRAM = jabswitch +include $(BUILDDIR)/common/Defs.gmk + +# Indicate we want the C++ compiler to do the linking. +CPLUSPLUSLIBRARY=true + +VERSIONINFO_RESOURCE = $(CLOSED_PLATFORM_SRC)/native/sun/bridge/AccessBridgeStatusWindow.rc +VERSIONRES = $(TEMPDIR)/AccessBridgeStatusWindow.res + +JAB_EXE= $(TEMPDIR)/jabswitch.exe + +JAB_SRC = $(CLOSED_PLATFORM_SRC)/native/sun/bridge/jabswitch.cpp + +JAB_MANIFEST_INP = $(CLOSED_PLATFORM_SRC)/native/sun/bridge/jabswitch.manifest +JAB_MANIFEST_OUT = $(TEMPDIR)/jabswitch.exe.intermediate.manifest + +RC_FLAGS += /fo "$(VERSIONRES)" +OTHER_CPPFLAGS += /MD /Fo"$(TEMPDIR)/" /Fd"$(TEMPDIR)/" /analyze- /Od /Gd /nologo /D "WIN32" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /RTC1 /W3 /ZI /Zc:wchar_t /EHsc +LDDFLAGS += Advapi32.lib Version.lib User32.lib + +all: buildexe copyfilejab + +buildexe : + $(CD) $(TEMPDIR) + $(RC) $(RC_FLAGS) $(VERSIONINFO_RESOURCE) + $(CC) $(CPPFLAGS) $(JAB_SRC) $(LDDFLAGS) $(VERSIONRES) -o $(JAB_EXE) + $(MT) /nologo /verbose /manifest $(JAB_MANIFEST_INP) /outputresource:$(JAB_EXE) + +copyfilejab : + $(CP) $(JAB_EXE) $(BINDIR) + +vpath %.cpp $(CLOSED_PLATFORM_SRC)/native/sun/bridge +vpath %.rc $(CLOSED_PLATFORM_SRC)/native/sun/bridge diff --git a/jdk/make/bridge/Jaccess/Makefile b/jdk/make/bridge/Jaccess/Makefile new file mode 100644 index 00000000000..df57536943e --- /dev/null +++ b/jdk/make/bridge/Jaccess/Makefile @@ -0,0 +1,85 @@ +# +# Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# +# Makefile for building jaccess +# +BUILDDIR = ../.. +PRODUCT = java +PACKAGE = com.sun.java.accessibility.jaccess + +include $(BUILDDIR)/common/Defs.gmk +JARFILE = $(EXTDIR)/jaccess.jar + +# +# Java files to compile. +# +#AUTO_FILES_JAVA_DIRS = $(CLOSED_PLATFORM_SRC)/bridge +FILES_java = \ + com/sun/java/accessibility/util/AccessibilityEventMonitor.java \ + com/sun/java/accessibility/util/AccessibilityListenerList.java \ + com/sun/java/accessibility/util/AWTEventMonitor.java \ + com/sun/java/accessibility/util/EventID.java \ + com/sun/java/accessibility/util/EventQueueMonitor.java \ + com/sun/java/accessibility/util/GUIInitializedListener.java \ + com/sun/java/accessibility/util/GUIInitializedMulticaster.java \ + com/sun/java/accessibility/util/SwingEventMonitor.java \ + com/sun/java/accessibility/util/TopLevelWindowListener.java \ + com/sun/java/accessibility/util/TopLevelWindowMulticaster.java \ + com/sun/java/accessibility/util/Translator.java \ + com/sun/java/accessibility/util/java/awt/ButtonTranslator.java \ + com/sun/java/accessibility/util/java/awt/CheckboxTranslator.java \ + com/sun/java/accessibility/util/java/awt/LabelTranslator.java \ + com/sun/java/accessibility/util/java/awt/ListTranslator.java \ + com/sun/java/accessibility/util/java/awt/TextComponentTranslator.java + +# +# Rules +# +CLASSDESTDIR = $(TEMPDIR)/classes + +FILES_class = $(FILES_java:%.java=$(CLASSDESTDIR)/%.class) + +all : build $(JARFILE) + +# +# JAR file +# +$(JARFILE): \ + $(FILES_class) + $(BOOT_JAR_CMD) -cf $(JARFILE) \ + -C $(CLASSDESTDIR) com \ + $(BOOT_JAR_JFLAGS) + @$(java-vm-cleanup) + + +# +# Rules +# +include $(BUILDDIR)/common/Classes.gmk + +clean clobber:: + $(RM) -r $(CLASSDESTDIR) \ + $(EXTDIR)/$(JARFILE) diff --git a/jdk/make/bridge/JavaAccessBridge/Files_cpp.gmk b/jdk/make/bridge/JavaAccessBridge/Files_cpp.gmk new file mode 100644 index 00000000000..3d197be7d4b --- /dev/null +++ b/jdk/make/bridge/JavaAccessBridge/Files_cpp.gmk @@ -0,0 +1,33 @@ +# +# Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# +# Native files to compile. +FILES_cpp = \ + AccessBridgeATInstance.cpp \ + AccessBridgeDebug.cpp \ + AccessBridgeJavaEntryPoints.cpp \ + AccessBridgeMessages.cpp \ + JavaAccessBridge.cpp diff --git a/jdk/make/bridge/JavaAccessBridge/Makefile b/jdk/make/bridge/JavaAccessBridge/Makefile new file mode 100644 index 00000000000..6637a777326 --- /dev/null +++ b/jdk/make/bridge/JavaAccessBridge/Makefile @@ -0,0 +1,90 @@ +# +# Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# +# Makefile for building JavaAccessBridge.DLL +# + +BUILDDIR = ../.. +LIBRARY = JavaAccessBridge$(ABSUFFIX) +include $(BUILDDIR)/common/Defs.gmk + +# Indicate we want the C++ compiler to do the linking. +CPLUSPLUSLIBRARY=true + +ifeq ($(ARCH_DATA_MODEL), 64) + ABSUFFIX = -64 + ACCESSBRIDGE_ARCH = ACCESSBRIDGE_ARCH_64 + ABRIDGE_MACHINE=X64 +else +ifeq ($(ARCH_DATA_MODEL), 32) + ABRIDGE_MACHINE=I386 +ifdef ABLEGACY + ABSUFFIX = + ACCESSBRIDGE_ARCH = ACCESSBRIDGE_ARCH_LEGACY +else + ABSUFFIX = -32 + ACCESSBRIDGE_ARCH = ACCESSBRIDGE_ARCH_32 +endif +endif +endif + +include FILES_cpp.gmk + +PLATFORM_INCLUDE_BRIDGE = $(PLATFORM_INCLUDE)/bridge + +VERSIONINFO_RESOURCE = $(CLOSED_PLATFORM_SRC)/native/sun/bridge/AccessBridgeStatusWindow.rc + +OTHER_CPPFLAGS += -D$(ACCESSBRIDGE_ARCH) -I "$(INCLUDEDIR)" -I "$(PLATFORM_INCLUDE)" +LDLIBS += kernel32.lib user32.lib gdi32.lib winspool.lib jawt.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib \ + odbc32.lib odbccp32.lib /subsystem:windows /dll /incremental:no /machine:$(ABRIDGE_MACHINE) \ + /def:$(CLOSED_PLATFORM_SRC)/native/sun/bridge/JavaAccessBridge.DEF /libpath:"$(LIBDIR)" + +all : build postbuild + +postbuild : + $(MKDIR) -p $(PLATFORM_INCLUDE_BRIDGE) + $(CP) $(CLOSED_PLATFORM_SRC)/native/sun/bridge/accessibility.properties $(LIBDIR) + $(CP) $(CLOSED_PLATFORM_SRC)/native/sun/bridge/AccessBridgeCallbacks.h $(PLATFORM_INCLUDE_BRIDGE) + $(CP) $(CLOSED_PLATFORM_SRC)/native/sun/bridge/AccessBridgeCalls.h $(PLATFORM_INCLUDE_BRIDGE) + $(CP) $(CLOSED_PLATFORM_SRC)/native/sun/bridge/AccessBridgePackages.h $(PLATFORM_INCLUDE_BRIDGE) + $(CP) $(CLOSED_PLATFORM_SRC)/native/sun/bridge/AccessBridgeCalls.c $(PLATFORM_INCLUDE_BRIDGE) + +# +# Rules +# +include $(BUILDDIR)/common/Library.gmk + +vpath %.cpp $(CLOSED_PLATFORM_SRC)/native/sun/bridge +vpath %.DEF $(CLOSED_PLATFORM_SRC)/native/sun/bridge +vpath %.rc $(CLOSED_PLATFORM_SRC)/native/sun/bridge +vpath %.c $(CLOSED_PLATFORM_SRC)/native/sun/bridge +vpath %.h $(CLOSED_PLATFORM_SRC)/native/sun/bridge + +# +# Extra clean rule. +# +clean clobber:: + $(RM) $(FILES_h) diff --git a/jdk/make/bridge/Makefile b/jdk/make/bridge/Makefile new file mode 100644 index 00000000000..6703a21d753 --- /dev/null +++ b/jdk/make/bridge/Makefile @@ -0,0 +1,65 @@ +# +# Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# +# Makefile for building Java Access Bridge +# + +BUILDDIR = .. +include $(BUILDDIR)/common/Defs.gmk + +# +# +ifndef OPENJDK +ifeq ($(PLATFORM), windows) +include $(BUILDDIR)/common/Subdirs.gmk + +# +# build for 32 and 64 bit (new api) +# +SUBDIRS = Jaccess JavaAccessBridge WindowsAccessBridge JAWTAccessBridge AccessBridgeJava Jabswitch +# +# build for legacy +# +ifeq ($(ARCH_DATA_MODEL), 32) +OTHERSUBDIRS_MAKEFLAGS += ABLEGACY=true +OTHERSUBDIRS = JavaAccessBridge WindowsAccessBridge JAWTAccessBridge AccessBridgeJava +endif + +ifeq ($(ARCH_DATA_MODEL), 32) +all build clean clobber :: + $(SUBDIRS-loop) + $(OTHERSUBDIRS-loop) +else +all build clean clobber :: + $(SUBDIRS-loop) +endif + +clean:: + $(RM) -r $(CLASSBINDIR) $(CLASSBINDIR) + +endif # PLATFORM +endif #OPENJDK + diff --git a/jdk/make/bridge/WindowsAccessBridge/Files_cpp.gmk b/jdk/make/bridge/WindowsAccessBridge/Files_cpp.gmk new file mode 100644 index 00000000000..411e2830153 --- /dev/null +++ b/jdk/make/bridge/WindowsAccessBridge/Files_cpp.gmk @@ -0,0 +1,35 @@ +# +# Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# +# Native files to compile. +FILES_cpp = \ + AccessBridgeJavaVMInstance.cpp \ + AccessBridgeMessageQueue.cpp \ + AccessBridgeMessages.cpp \ + AccessBridgeWindowsEntryPoints.cpp \ + WinAccessBridge.cpp \ + AccessBridgeDebug.cpp \ + AccessBridgeEventHandler.cpp diff --git a/jdk/make/bridge/WindowsAccessBridge/Makefile b/jdk/make/bridge/WindowsAccessBridge/Makefile new file mode 100644 index 00000000000..f65f704e3ea --- /dev/null +++ b/jdk/make/bridge/WindowsAccessBridge/Makefile @@ -0,0 +1,71 @@ +# +# Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +# +# Makefile for building WindowsAccessBridge.dll +# + +BUILDDIR = ../.. +LIBRARY = WindowsAccessBridge$(ABSUFFIX) +include $(BUILDDIR)/common/Defs.gmk + +# Indicate we want the C++ compiler to do the linking. +CPLUSPLUSLIBRARY=true + +ifeq ($(ARCH_DATA_MODEL), 64) + ABSUFFIX = -64 + ACCESSBRIDGE_ARCH = ACCESSBRIDGE_ARCH_64 + ABRIDGE_MACHINE=X64 +else +ifeq ($(ARCH_DATA_MODEL), 32) + ABRIDGE_MACHINE=I386 +ifdef ABLEGACY + ABSUFFIX = + ACCESSBRIDGE_ARCH = ACCESSBRIDGE_ARCH_LEGACY +else + ABSUFFIX = -32 + ACCESSBRIDGE_ARCH = ACCESSBRIDGE_ARCH_32 +endif +endif +endif + +include FILES_cpp.gmk + +VERSIONINFO_RESOURCE = $(CLOSED_PLATFORM_SRC)/native/sun/bridge/AccessBridgeStatusWindow.rc + +OTHER_CPPFLAGS += -MT -D$(ACCESSBRIDGE_ARCH) -I "$(INCLUDEDIR)" -I "$(PLATFORM_INCLUDE)" +LDLIBS += kernel32.lib user32.lib gdi32.lib winspool.lib jawt.lib comdlg32.lib advapi32.lib shell32.lib \ + ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /subsystem:windows /dll /incremental:no /machine:$(ABRIDGE_MACHINE) \ + /def:$(CLOSED_PLATFORM_SRC)/native/sun/bridge/WinAccessBridge.DEF /libpath:"$(LIBDIR)" + + +# +# Rules +# +include $(BUILDDIR)/common/Library.gmk + +vpath %.cpp $(CLOSED_PLATFORM_SRC)/native/sun/bridge +vpath %.DEF $(CLOSED_PLATFORM_SRC)/native/sun/bridge +vpath %.rc $(CLOSED_PLATFORM_SRC)/native/sun/bridge diff --git a/jdk/makefiles/CompileJavaClasses.gmk b/jdk/makefiles/CompileJavaClasses.gmk index 2a7f4def426..5557c026bba 100644 --- a/jdk/makefiles/CompileJavaClasses.gmk +++ b/jdk/makefiles/CompileJavaClasses.gmk @@ -60,6 +60,12 @@ ifndef OPENJDK # This gets built on unix platforms implicitly in the old build even though # it's excluded in the closed build. EXCLUDES+=sun/java2d/pisces + + # AccessBridge is compiled separately below. + EXFILES += AccessBridge.java \ + com/sun/java/accessibility/util/java/awt/ChoiceTranslator.java + # This seems to never be built + EXCLUDES += com/sun/java/accessibility/extensions endif endif @@ -121,7 +127,6 @@ ifneq ($(OPENJDK_TARGET_OS),linux) sun/nio/fs/LinuxFileStore.java \ sun/nio/fs/LinuxFileSystem.java \ sun/nio/fs/LinuxFileSystemProvider.java \ - sun/nio/fs/MagicFileTypeDetector.java \ sun/nio/fs/LinuxNativeDispatcher.java \ sun/nio/fs/LinuxUserDefinedFileAttributeView.java \ sun/nio/fs/LinuxWatchService.java @@ -221,10 +226,24 @@ endif EXFILES+=-linux-arm.java \ -linux-ppc.java +# TODO: Is this necessary? ifeq ($(OPENJDK_TARGET_OS), windows) EXFILES+=sun/nio/ch/AbstractPollSelectorImpl.java \ + sun/nio/ch/DevPollArrayWrapper.java \ + sun/nio/ch/DevPollSelectorImpl.java \ + sun/nio/ch/DevPollSelectorProvider.java \ + sun/nio/ch/InheritedChannel.java \ sun/nio/ch/PollSelectorProvider.java \ - sun/nio/ch/SimpleAsynchronousFileChannelImpl.java + sun/nio/ch/PollSelectorImpl.java \ + sun/nio/ch/Port.java \ + sun/nio/ch/SimpleAsynchronousFileChannelImpl.java \ + sun/nio/ch/SolarisAsynchronousChannelProvider.java \ + sun/nio/ch/SolarisEventPort.java \ + sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.java \ + sun/nio/ch/UnixAsynchronousSocketChannelImpl.java + EXFILES+=sun/net/sdp/SdpProvider.java +else + EXFILES+=sun/net/www/protocol/http/ntlm/NTLMAuthSequence.java endif # Exclude nimbus files from rt.jar @@ -241,10 +260,8 @@ include CopyIntoClasses.gmk # Now we have COPY_PATTERNS, COPY_FILES and COPY_EXTRA ifndef OPENJDK - CLOSED_SRC_DIRS:=$(JDK_TOPDIR)/src/closed/share/classes - ifneq ($(OPENJDK_TARGET_OS_API_DIR),windows) - CLOSED_SRC_DIRS += $(JDK_TOPDIR)/src/closed/$(OPENJDK_TARGET_OS_API_DIR)/classes - endif + CLOSED_SRC_DIRS:=$(JDK_TOPDIR)/src/closed/share/classes \ + $(JDK_TOPDIR)/src/closed/$(OPENJDK_TARGET_OS_API_DIR)/classes endif MACOSX_SRC_DIRS := @@ -325,14 +342,29 @@ $(JDK_OUTPUTDIR)/gensrc_headers/_the.jdk.base.headers: $(JDK_BASE_HEADER_JAVA_FI ########################################################################################## +# +# This is an empty jar (only contains manifest) and fits poorly into framework... +# create simple rule instead +# +MANAGEMENT_AGENT_JAR_DEPS := $(JDK_TOPDIR)/src/share/classes/sun/management/manifest + +$(JDK_OUTPUTDIR)/lib/management-agent.jar : $(JDK_TOPDIR)/src/share/classes/sun/management/manifest + $(JAR) cfm $@ $(JDK_TOPDIR)/src/share/classes/sun/management/manifest + +JARS += $(JDK_OUTPUTDIR)/lib/management-agent.jar + +########################################################################################## + ifndef OPENJDK - $(eval $(call SetupJavaCompilation,BUILD_ALTCLASSES,\ +$(eval $(call SetupJavaCompilation,BUILD_ALTCLASSES_JAR,\ SETUP:=GENERATE_JDKBYTECODE,\ SRC:=$(JDK_TOPDIR)/src/closed/share/altclasses, \ - BIN:=$(JDK_OUTPUTDIR)/altclasses_classes)) + BIN:=$(JDK_OUTPUTDIR)/altclasses_classes,\ + JAR:=$(JDK_OUTPUTDIR)/lib/alt-rt.jar)) - $(BUILD_ALTCLASSES): $(BUILD_JDK) +$(BUILD_ALTCLASSES_JAR): $(BUILD_JDK) +JARS += $(JDK_OUTPUTDIR)/lib/alt-rt.jar endif @@ -355,7 +387,7 @@ $(eval $(call SetupJavaCompiler,GENERATE_15BYTECODE,\ SERVER_DIR:=$(SJAVAC_SERVER_DIR),\ SERVER_JVM:=$(SJAVAC_SERVER_JAVA))) -$(eval $(call SetupJavaCompilation,BUILD_JOBJC,\ +$(eval $(call SetupJavaCompilation,BUILD_JOBJC_JAR,\ SETUP:=GENERATE_15BYTECODE,\ DISABLE_SJAVAC:=true,\ SRC:=$(JDK_TOPDIR)/src/macosx/native/jobjc/src/core/java \ @@ -367,9 +399,11 @@ $(eval $(call SetupJavaCompilation,BUILD_JOBJC,\ JAR:=$(JDK_OUTPUTDIR)/lib/JObjC.jar, \ JARINDEX := true)) -$(BUILD_JOBJC) : $(BUILD_JDK) +$(BUILD_JOBJC_JAR) : $(BUILD_JDK) -$(eval $(call SetupJavaCompilation,BUILD_JOBJC_HEADERS,\ +JARS += $(JDK_OUTPUTDIR)/lib/JObjC.jar + +$(eval $(call SetupJavaCompilation,BUILD_JOBJC_HEADERS_JAR,\ SETUP:=GENERATE_JDKBYTECODE,\ SRC:=$(JDK_TOPDIR)/src/macosx/native/jobjc/src/core/java \ $(JDK_TOPDIR)/src/macosx/native/jobjc/src/runtime-additions/java \ @@ -379,15 +413,53 @@ $(eval $(call SetupJavaCompilation,BUILD_JOBJC_HEADERS,\ BIN:=$(JDK_OUTPUTDIR)/jobjc_classes_headers,\ HEADERS:=$(JDK_OUTPUTDIR)/gensrc_headers_jobjc)) -$(BUILD_JOBJC_HEADERS) : $(BUILD_JDK) +$(BUILD_JOBJC_HEADERS_JAR) : $(BUILD_JDK) + +JARS += $(BUILD_JOBJC_HEADERS_JAR) endif ########################################################################################## +ifndef OPENJDK +ifeq ($(OPENJDK_TARGET_OS), windows) +ifeq ($(OPENJDK_TARGET_CPU_BITS), 32) + $(eval $(call SetupJavaCompilation,BUILD_ACCESSBRIDGE_32,\ + SETUP:=GENERATE_JDKBYTECODE,\ + JAVAC_FLAGS:=-cp $(JDK_OUTPUTDIR)/classes,\ + SRC:=$(JDK_OUTPUTDIR)/gensrc_ab/32bit,\ + BIN:=$(JDK_OUTPUTDIR)/classes_ab/32bit)) + + $(BUILD_ACCESSBRIDGE_32): $(BUILD_JDK) + + $(eval $(call SetupJavaCompilation,BUILD_ACCESSBRIDGE_LEGACY,\ + SETUP:=GENERATE_JDKBYTECODE,\ + JAVAC_FLAGS:=-cp $(JDK_OUTPUTDIR)/classes,\ + SRC:=$(JDK_OUTPUTDIR)/gensrc_ab/legacy,\ + BIN:=$(JDK_OUTPUTDIR)/classes_ab/legacy)) + + $(BUILD_ACCESSBRIDGE_LEGACY): $(BUILD_JDK) + +else + + $(eval $(call SetupJavaCompilation,BUILD_ACCESSBRIDGE_64,\ + SETUP:=GENERATE_JDKBYTECODE,\ + JAVAC_FLAGS:=-cp $(JDK_OUTPUTDIR)/classes,\ + SRC:=$(JDK_OUTPUTDIR)/gensrc_ab/64bit,\ + BIN:=$(JDK_OUTPUTDIR)/classes_ab/64bit)) + + $(BUILD_ACCESSBRIDGE_64): $(BUILD_JDK) + +endif +endif +endif + +########################################################################################## + # copy with -a to preserve timestamps so dependencies down the line aren't messed up -all: $(BUILD_JDK) $(BUILD_ALTCLASSES) $(BUILD_JOBJC) $(BUILD_JOBJC_HEADERS) $(COPY_EXTRA) \ - $(JDK_OUTPUTDIR)/classes/META-INF/services/com.sun.tools.xjc.Plugin \ - $(JDK_OUTPUTDIR)/gensrc_headers/_the.jdk.base.headers +all: $(BUILD_JDK) $(JARS) $(COPY_EXTRA) $(JDK_OUTPUTDIR)/classes/META-INF/services/com.sun.tools.xjc.Plugin \ + $(JDK_OUTPUTDIR)/gensrc_headers/_the.jdk.base.headers \ + $(BUILD_ACCESSBRIDGE_32) $(BUILD_ACCESSBRIDGE_64) \ + $(BUILD_ACCESSBRIDGE_LEGACY) .PHONY: all diff --git a/jdk/makefiles/CompileLaunchers.gmk b/jdk/makefiles/CompileLaunchers.gmk index f97578aa087..ba3844ef0e4 100644 --- a/jdk/makefiles/CompileLaunchers.gmk +++ b/jdk/makefiles/CompileLaunchers.gmk @@ -587,6 +587,34 @@ else $(CHMOD) a+x $@ endif +########################################################################################## +# jabswitch + +ifndef OPENJDK +ifeq ($(OPENJDK_TARGET_OS),windows) + + $(eval $(call SetupNativeCompilation,BUILD_JABSWITCH,\ + SRC:=$(JDK_TOPDIR)/src/closed/windows/native/sun/bridge,\ + INCLUDE_FILES:=jabswitch.cpp,\ + LANG:=C++,\ + CFLAGS:=$(filter-out -Zc:wchar_t-,$(CFLAGS_JDKEXE)) -Zc:wchar_t \ + -analyze- -Od -Gd -D_WINDOWS \ + -D_UNICODE -DUNICODE -RTC1 -EHsc,\ + LDFLAGS:=$(LDFLAGS_JDKEXE) \ + Advapi32.lib Version.lib User32.lib,\ + OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/jabswitch,\ + OUTPUT_DIR:=$(JDK_OUTPUTDIR)/bin,\ + PROGRAM:=jabswitch,\ + DEBUG_SYMBOLS:=true,\ + VERSIONINFO_RESOURCE:=$(JDK_TOPDIR)/src/closed/windows/native/sun/bridge/AccessBridgeStatusWindow.rc,\ + RC_FLAGS:=$(RC_FLAGS),\ + MANIFEST:=$(JDK_TOPDIR)/src/closed/windows/native/sun/bridge/jabswitch.manifest)) + + BUILD_LAUNCHERS += $(BUILD_JABSWITCH) + +endif +endif + ########################################################################################## $(BUILD_LAUNCHERS) : $(JDK_TOPDIR)/makefiles/CompileLaunchers.gmk diff --git a/jdk/makefiles/CompileNativeLibraries.gmk b/jdk/makefiles/CompileNativeLibraries.gmk index cb2aae5a8f0..2ab8f8b1eea 100644 --- a/jdk/makefiles/CompileNativeLibraries.gmk +++ b/jdk/makefiles/CompileNativeLibraries.gmk @@ -3292,6 +3292,97 @@ BUILD_LIBRARIES += $(INSTALL_LIBRARIES_HERE)/$(LIBRARY_PREFIX)JObjC$(SHARED_LIBR endif +########################################################################################## + +ifndef OPENJDK +ifeq ($(OPENJDK_TARGET_OS), windows) + + ACCESSBRIDGE_SRCDIR:=$(JDK_TOPDIR)/src/closed/windows/native/sun/bridge + + define SetupAccessBridge + # Parameter 1 Suffix + # Parameter 2 Machine + # Parameter 3 ACCESSBRIDGE_ARCH_ suffix + + $(call SetupNativeCompilation,BUILD_JAWTACCESSBRIDGE$1,\ + LIBRARY=JAWTAccessBridge$1,\ + OUTPUT_DIR:=$(INSTALL_LIBRARIES_HERE),\ + SRC:=$(ACCESSBRIDGE_SRCDIR),\ + INCLUDE_FILES:=JAWTAccessBridge.cpp,\ + LANG:=C++,\ + OPTIMIZATION:=LOW,\ + CFLAGS:=$(CFLAGS_JDKLIB) \ + -DACCESSBRIDGE_ARCH_$3,\ + LDFLAGS:=$(LDFLAGS_JDKLIB) kernel32.lib user32.lib gdi32.lib \ + winspool.lib jawt.lib comdlg32.lib advapi32.lib shell32.lib \ + ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib \ + -subsystem:windows -machine:$2 \ + -def:$(ACCESSBRIDGE_SRCDIR)/JAWTAccessBridge.DEF,\ + VERSIONINFO_RESOURCE:=$(ACCESSBRIDGE_SRCDIR)/AccessBridgeStatusWindow.rc,\ + RC_FLAGS:=$(RC_FLAGS),\ + OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libjawtaccessbridge$1,\ + DEBUG_SYMBOLS:=true) + + $$(BUILD_JAWTACCESSBRIDGE$1): $(JDK_OUTPUTDIR)/lib/$(LIBRARY_PREFIX)jawt$(STATIC_LIBRARY_SUFFIX) + + $(call SetupNativeCompilation,BUILD_JAVAACCESSBRIDGE$1,\ + LIBRARY=JavaAccessBridge$1,\ + OUTPUT_DIR:=$(INSTALL_LIBRARIES_HERE),\ + SRC:=$(ACCESSBRIDGE_SRCDIR),\ + INCLUDE_FILES:=AccessBridgeATInstance.cpp AccessBridgeDebug.cpp \ + AccessBridgeJavaEntryPoints.cpp \ + AccessBridgeMessages.cpp JavaAccessBridge.cpp,\ + LANG:=C++,\ + OPTIMIZATION:=LOW,\ + CFLAGS:=$(CFLAGS_JDKLIB) \ + -DACCESSBRIDGE_ARCH_$3,\ + LDFLAGS:=$(LDFLAGS_JDKLIB) kernel32.lib user32.lib gdi32.lib \ + winspool.lib comdlg32.lib advapi32.lib shell32.lib \ + ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib \ + -subsystem:windows -machine:$2 \ + -def:$(ACCESSBRIDGE_SRCDIR)/JavaAccessBridge.DEF,\ + VERSIONINFO_RESOURCE:=$(ACCESSBRIDGE_SRCDIR)/AccessBridgeStatusWindow.rc,\ + RC_FLAGS:=$(RC_FLAGS),\ + OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libjavaaccessbridge$1,\ + DEBUG_SYMBOLS:=true) + + $(call SetupNativeCompilation,BUILD_WINDOWSACCESSBRIDGE$1,\ + LIBRARY=WindowsAccessBridge$1,\ + OUTPUT_DIR:=$(INSTALL_LIBRARIES_HERE),\ + SRC:=$(ACCESSBRIDGE_SRCDIR),\ + INCLUDE_FILES:=AccessBridgeJavaVMInstance.cpp AccessBridgeMessageQueue.cpp \ + AccessBridgeMessages.cpp AccessBridgeWindowsEntryPoints.cpp \ + WinAccessBridge.cpp AccessBridgeDebug.cpp \ + AccessBridgeEventHandler.cpp,\ + LANG:=C++,\ + OPTIMIZATION:=LOW,\ + CFLAGS:=$(filter-out -MD,$(CFLAGS_JDKLIB)) -MT \ + -DACCESSBRIDGE_ARCH_$3,\ + LDFLAGS:=$(LDFLAGS_JDKLIB) kernel32.lib user32.lib gdi32.lib \ + winspool.lib comdlg32.lib advapi32.lib shell32.lib \ + ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib \ + -subsystem:windows -machine:$2 \ + -def:$(ACCESSBRIDGE_SRCDIR)/WinAccessBridge.DEF,\ + VERSIONINFO_RESOURCE:=$(ACCESSBRIDGE_SRCDIR)/AccessBridgeStatusWindow.rc,\ + RC_FLAGS:=$(RC_FLAGS),\ + OBJECT_DIR:=$(JDK_OUTPUTDIR)/objs/libwindowsaccessbridge$1,\ + DEBUG_SYMBOLS:=true) + + BUILD_LIBRARIES += $$(BUILD_JAWTACCESSBRIDGE$1) $$(BUILD_JAVAACCESSBRIDGE$1) \ + $$(BUILD_WINDOWSACCESSBRIDGE$1) + + endef + + ifeq ($(OPENJDK_TARGET_CPU_BITS),32) + $(eval $(call SetupAccessBridge,-32,I386,32)) + $(eval $(call SetupAccessBridge,,I386,LEGACY)) + else + $(eval $(call SetupAccessBridge,-64,X64,64)) + endif +endif +endif + + ########################################################################################## all: $(COPY_FILES) $(BUILD_LIBRARIES) diff --git a/jdk/makefiles/CopyFiles.gmk b/jdk/makefiles/CopyFiles.gmk index be24ab22819..89ff6d5a1c0 100644 --- a/jdk/makefiles/CopyFiles.gmk +++ b/jdk/makefiles/CopyFiles.gmk @@ -42,7 +42,7 @@ H_TARGET_FILES =$(INCLUDEDIR)/jdwpTransport.h \ $(INCLUDEDIR)/jvmticmlr.h \ $(INCLUDEDIR)/classfile_constants.h \ $(INCLUDEDIR)/jawt.h \ - $(OPENJDK_TARGET_OS_INCLUDE)/jni_md.h \ + $(OPENJDK_TARGET_OS_INCLUDE)/jni_md.h \ $(OPENJDK_TARGET_OS_INCLUDE)/jawt_md.h $(INCLUDEDIR)/%.h: $(JDK_TOPDIR)/src/share/javavm/export/%.h @@ -59,6 +59,27 @@ COPY_FILES = $(H_TARGET_FILES) ########################################################################################## +ifndef OPENJDK +ifeq ($(OPENJDK_TARGET_OS), windows) + COPY_FILES += $(OPENJDK_TARGET_OS_INCLUDE)/bridge/AccessBridgeCallbacks.h \ + $(OPENJDK_TARGET_OS_INCLUDE)/bridge/AccessBridgeCalls.h \ + $(OPENJDK_TARGET_OS_INCLUDE)/bridge/AccessBridgePackages.h \ + $(OPENJDK_TARGET_OS_INCLUDE)/bridge/AccessBridgeCalls.c \ + $(JDK_OUTPUTDIR)/lib/accessibility.properties + + $(OPENJDK_TARGET_OS_INCLUDE)/bridge/%: \ + $(JDK_TOPDIR)/src/closed/windows/native/sun/bridge/% + $(install-file) + + $(JDK_OUTPUTDIR)/lib/accessibility.properties: \ + $(JDK_TOPDIR)/src/closed/windows/native/sun/bridge/accessibility.properties + $(install-file) + +endif +endif + +########################################################################################## + LIBDIR = $(JDK_OUTPUTDIR)/lib SERVICETAG_LIBDIR = $(LIBDIR)/servicetag diff --git a/jdk/makefiles/CreateJars.gmk b/jdk/makefiles/CreateJars.gmk index 81f1fdf6601..a51d0b3c644 100644 --- a/jdk/makefiles/CreateJars.gmk +++ b/jdk/makefiles/CreateJars.gmk @@ -126,6 +126,7 @@ JARS+=$(IMAGES_OUTPUTDIR)/lib/ext/localedata.jar # Exclude list for rt.jar and resources.jar RT_JAR_EXCLUDES := \ + com/sun/java/accessibility \ com/sun/javadoc \ com/sun/jdi \ com/sun/jarsigner \ @@ -918,6 +919,47 @@ JARS+=$(IMAGES_OUTPUTDIR)/src.zip ########################################################################################## +ifndef OPENJDK +ifeq ($(OPENJDK_TARGET_OS), windows) + + $(eval $(call SetupArchive,BUILD_JACCESS_JAR,,\ + SRCS:=$(JDK_OUTPUTDIR)/classes,\ + INCLUDES:=com/sun/java/accessibility/util,\ + JAR:=$(IMAGES_OUTPUTDIR)/lib/ext/jaccess.jar,\ + SKIP_METAINF:=true)) + + JARS += $(IMAGES_OUTPUTDIR)/lib/ext/jaccess.jar + + ifeq ($(OPENJDK_TARGET_CPU_BITS), 32) + $(eval $(call SetupArchive,BUILD_ACCESSBRIDGE_32_JAR,,\ + SRCS:=$(JDK_OUTPUTDIR)/classes_ab/32bit $(JDK_OUTPUTDIR)/classes,\ + INCLUDES:=com/sun/java/accessibility,\ + JAR:=$(IMAGES_OUTPUTDIR)/lib/ext/access-bridge-32.jar,\ + SKIP_METAINF:=true)) + + $(eval $(call SetupArchive,BUILD_ACCESSBRIDGE_LEGACY_JAR,,\ + SRCS:=$(JDK_OUTPUTDIR)/classes_ab/legacy $(JDK_OUTPUTDIR)/classes,\ + INCLUDES:=com/sun/java/accessibility,\ + JAR:=$(IMAGES_OUTPUTDIR)/lib/ext/access-bridge.jar,\ + SKIP_METAINF:=true)) + + JARS += $(IMAGES_OUTPUTDIR)/lib/ext/access-bridge-32.jar \ + $(IMAGES_OUTPUTDIR)/lib/ext/access-bridge.jar + else + $(eval $(call SetupArchive,BUILD_ACCESSBRIDGE_64_JAR,,\ + SRCS:=$(JDK_OUTPUTDIR)/classes_ab/64bit $(JDK_OUTPUTDIR)/classes,\ + INCLUDES:=com/sun/java/accessibility,\ + EXCLUDES:=com/sun/java/accessibility/util/java,\ + JAR:=$(IMAGES_OUTPUTDIR)/lib/ext/access-bridge-64.jar,\ + SKIP_METAINF:=true)) + + JARS += $(IMAGES_OUTPUTDIR)/lib/ext/access-bridge-64.jar + endif +endif +endif + +########################################################################################## + # # This is an empty jar (only contains manifest) and fits poorly into framework... # create simple rule instead diff --git a/jdk/makefiles/GensrcMisc.gmk b/jdk/makefiles/GensrcMisc.gmk index 7fdfa3c846c..5b9c06e5abf 100644 --- a/jdk/makefiles/GensrcMisc.gmk +++ b/jdk/makefiles/GensrcMisc.gmk @@ -190,3 +190,36 @@ $(JDK_OUTPUTDIR)/gensrc/sun/nio/fs/SolarisConstants.java : $(BUILD_GENSRC_SOL_EX endif + +########################################################################################## + +ifndef OPENJDK +ifeq ($(OPENJDK_TARGET_OS), windows) + + AB_GENSRC_DIR := $(JDK_OUTPUTDIR)/gensrc_ab + AB_SRC_DIR := $(JDK_TOPDIR)/src/closed/windows/classes/com/sun/java/accessibility + + ifeq ($(OPENJDK_TARGET_CPU_BITS), 32) + $(AB_GENSRC_DIR)/32bit/com/sun/java/accessibility/AccessBridge.java: \ + $(AB_SRC_DIR)/32bit/AccessBridge.java + $(install-file) + + $(AB_GENSRC_DIR)/legacy/com/sun/java/accessibility/AccessBridge.java: \ + $(AB_SRC_DIR)/legacy/AccessBridge.java + $(install-file) + + GENSRC_MISC += $(AB_GENSRC_DIR)/32bit/com/sun/java/accessibility/AccessBridge.java \ + $(AB_GENSRC_DIR)/legacy/com/sun/java/accessibility/AccessBridge.java + + else + $(AB_GENSRC_DIR)/64bit/com/sun/java/accessibility/AccessBridge.java: \ + $(AB_SRC_DIR)/64bit/AccessBridge.java + $(install-file) + + GENSRC_MISC += $(AB_GENSRC_DIR)/64bit/com/sun/java/accessibility/AccessBridge.java + + endif +endif +endif + +########################################################################################## \ No newline at end of file From 4e11f499f4e963a616d65f30772202c2b9ef3ff5 Mon Sep 17 00:00:00 2001 From: Mark Sheppard Date: Sat, 19 Jan 2013 08:39:20 +0000 Subject: [PATCH 074/138] 8006568: HTTP protocol handler NLTM Authentication should use Base64 API Reviewed-by: chegar, alanb --- .../www/protocol/http/ntlm/NTLMAuthentication.java | 13 ++++--------- .../www/protocol/http/ntlm/NTLMAuthSequence.java | 12 +++--------- .../net/www/protocol/http/ProxyTunnelServer.java | 7 +++---- 3 files changed, 10 insertions(+), 22 deletions(-) diff --git a/jdk/src/solaris/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java b/jdk/src/solaris/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java index 0d898336e24..693833198b3 100644 --- a/jdk/src/solaris/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java +++ b/jdk/src/solaris/classes/sun/net/www/protocol/http/ntlm/NTLMAuthentication.java @@ -33,6 +33,7 @@ import java.net.PasswordAuthentication; import java.net.UnknownHostException; import java.net.URL; import java.security.GeneralSecurityException; +import java.util.Base64; import sun.net.www.HeaderParser; import sun.net.www.protocol.http.AuthenticationInfo; @@ -230,7 +231,7 @@ public class NTLMAuthentication extends AuthenticationInfo { private String buildType1Msg () { byte[] msg = client.type1(); - String result = "NTLM " + (new B64Encoder()).encode (msg); + String result = "NTLM " + Base64.getEncoder().encodeToString(msg); return result; } @@ -239,18 +240,12 @@ public class NTLMAuthentication extends AuthenticationInfo { /* First decode the type2 message to get the server nonce */ /* nonce is located at type2[24] for 8 bytes */ - byte[] type2 = (new sun.misc.BASE64Decoder()).decodeBuffer (challenge); + byte[] type2 = Base64.getDecoder().decode(challenge); byte[] nonce = new byte[8]; new java.util.Random().nextBytes(nonce); byte[] msg = client.type3(type2, nonce); - String result = "NTLM " + (new B64Encoder()).encode (msg); + String result = "NTLM " + Base64.getEncoder().encodeToString(msg); return result; } } -class B64Encoder extends sun.misc.BASE64Encoder { - /* to force it to to the entire encoding in one line */ - protected int bytesPerLine () { - return 1024; - } -} diff --git a/jdk/src/windows/classes/sun/net/www/protocol/http/ntlm/NTLMAuthSequence.java b/jdk/src/windows/classes/sun/net/www/protocol/http/ntlm/NTLMAuthSequence.java index 5e6cbb02f31..a3ac941a7a2 100644 --- a/jdk/src/windows/classes/sun/net/www/protocol/http/ntlm/NTLMAuthSequence.java +++ b/jdk/src/windows/classes/sun/net/www/protocol/http/ntlm/NTLMAuthSequence.java @@ -26,8 +26,7 @@ package sun.net.www.protocol.http.ntlm; import java.io.IOException; -import sun.misc.BASE64Encoder; -import sun.misc.BASE64Decoder; +import java.util.Base64; /* * Hooks into Windows implementation of NTLM. @@ -77,11 +76,11 @@ public class NTLMAuthSequence { assert !status.sequenceComplete; if (token != null) - input = (new BASE64Decoder()).decodeBuffer(token); + input = Base64.getDecoder().decode(token); byte[] b = getNextToken (crdHandle, input, status); if (b == null) throw new IOException ("Internal authentication error"); - return (new B64Encoder()).encode (b); + return Base64.getEncoder().encodeToString(b); } public boolean isComplete() { @@ -95,8 +94,3 @@ public class NTLMAuthSequence { private native byte[] getNextToken (long crdHandle, byte[] lastToken, Status returned); } -class B64Encoder extends BASE64Encoder { - protected int bytesPerLine () { - return 1024; - } -} diff --git a/jdk/test/sun/net/www/protocol/http/ProxyTunnelServer.java b/jdk/test/sun/net/www/protocol/http/ProxyTunnelServer.java index 96d9e9ef91a..a19d89ca101 100644 --- a/jdk/test/sun/net/www/protocol/http/ProxyTunnelServer.java +++ b/jdk/test/sun/net/www/protocol/http/ProxyTunnelServer.java @@ -31,6 +31,7 @@ import java.io.*; import java.net.*; +import java.util.Base64; import javax.net.ssl.*; import javax.net.ServerSocketFactory; import sun.net.www.*; @@ -295,10 +296,8 @@ public class ProxyTunnelServer extends Thread { String recvdUserPlusPass = authInfo.substring(ind + 1).trim(); // extract encoded (username:passwd if (userPlusPass.equals( - new String( - (new sun.misc.BASE64Decoder()). - decodeBuffer(recvdUserPlusPass) - ))) { + new String(Base64.getDecoder().decode(recvdUserPlusPass)) + )) { matched = true; } } catch (Exception e) { From c98a554aaf8f1109dfdd361ddd19a524d5f7d1af Mon Sep 17 00:00:00 2001 From: Lance Andersen Date: Sat, 19 Jan 2013 10:11:19 -0500 Subject: [PATCH 075/138] 8006139: add missing methods to javax.sql.rowset.serial.SQLInputImpl, SQLOutputImpl Reviewed-by: naoto, ulfzibis, alanb --- .../javax/sql/rowset/serial/SQLInputImpl.java | 270 ++++-------------- .../sql/rowset/serial/SQLOutputImpl.java | 18 +- 2 files changed, 66 insertions(+), 222 deletions(-) diff --git a/jdk/src/share/classes/javax/sql/rowset/serial/SQLInputImpl.java b/jdk/src/share/classes/javax/sql/rowset/serial/SQLInputImpl.java index f05be51c14f..95a9938a007 100644 --- a/jdk/src/share/classes/javax/sql/rowset/serial/SQLInputImpl.java +++ b/jdk/src/share/classes/javax/sql/rowset/serial/SQLInputImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,6 +55,7 @@ import java.util.Map; * stream to the method SQLData.readSQL, which in turn * calls the SQLInputImpl reader methods * to read the attributes from the input stream. + * @since 1.5 * @see java.sql.SQLData */ public class SQLInputImpl implements SQLInput { @@ -142,6 +143,7 @@ public class SQLInputImpl implements SQLInput { throw new SQLException("SQLInputImpl exception: Invalid read " + "position"); } else { + lastValueWasNull = attrib[idx] == null; return attrib[idx]; } } @@ -168,16 +170,7 @@ public class SQLInputImpl implements SQLInput { * position or if there are no further values in the stream. */ public String readString() throws SQLException { - - String attrib = (String)getNextAttribute(); - - if (attrib == null) { - lastValueWasNull = true; - return null; - } else { - lastValueWasNull = false; - return attrib; - } + return (String)getNextAttribute(); } /** @@ -195,16 +188,8 @@ public class SQLInputImpl implements SQLInput { * position or if there are no further values in the stream. */ public boolean readBoolean() throws SQLException { - Boolean attrib = (Boolean)getNextAttribute(); - - if (attrib == null) { - lastValueWasNull = true; - return false; - } else { - lastValueWasNull = false; - return attrib.booleanValue(); - } + return (attrib == null) ? false : attrib.booleanValue(); } /** @@ -223,14 +208,7 @@ public class SQLInputImpl implements SQLInput { */ public byte readByte() throws SQLException { Byte attrib = (Byte)getNextAttribute(); - - if (attrib == null) { - lastValueWasNull = true; - return (byte)0; - } else { - lastValueWasNull = false; - return attrib.byteValue(); - } + return (attrib == null) ? 0 : attrib.byteValue(); } /** @@ -248,14 +226,7 @@ public class SQLInputImpl implements SQLInput { */ public short readShort() throws SQLException { Short attrib = (Short)getNextAttribute(); - - if (attrib == null) { - lastValueWasNull = true; - return (short)0; - } else { - lastValueWasNull = false; - return attrib.shortValue(); - } + return (attrib == null) ? 0 : attrib.shortValue(); } /** @@ -273,14 +244,7 @@ public class SQLInputImpl implements SQLInput { */ public int readInt() throws SQLException { Integer attrib = (Integer)getNextAttribute(); - - if (attrib == null) { - lastValueWasNull = true; - return 0; - } else { - lastValueWasNull = false; - return attrib.intValue(); - } + return (attrib == null) ? 0 : attrib.intValue(); } /** @@ -298,14 +262,7 @@ public class SQLInputImpl implements SQLInput { */ public long readLong() throws SQLException { Long attrib = (Long)getNextAttribute(); - - if (attrib == null) { - lastValueWasNull = true; - return (long)0; - } else { - lastValueWasNull = false; - return attrib.longValue(); - } + return (attrib == null) ? 0 : attrib.longValue(); } /** @@ -323,14 +280,7 @@ public class SQLInputImpl implements SQLInput { */ public float readFloat() throws SQLException { Float attrib = (Float)getNextAttribute(); - - if (attrib == null) { - lastValueWasNull = true; - return (float)0; - } else { - lastValueWasNull = false; - return attrib.floatValue(); - } + return (attrib == null) ? 0 : attrib.floatValue(); } /** @@ -348,14 +298,7 @@ public class SQLInputImpl implements SQLInput { */ public double readDouble() throws SQLException { Double attrib = (Double)getNextAttribute(); - - if (attrib == null) { - lastValueWasNull = true; - return (double)0; - } else { - lastValueWasNull = false; - return attrib.doubleValue(); - } + return (attrib == null) ? 0 : attrib.doubleValue(); } /** @@ -372,15 +315,7 @@ public class SQLInputImpl implements SQLInput { * position or if there are no more values in the stream */ public java.math.BigDecimal readBigDecimal() throws SQLException { - java.math.BigDecimal attrib = (java.math.BigDecimal)getNextAttribute(); - - if (attrib == null) { - lastValueWasNull = true; - return null; - } else { - lastValueWasNull = false; - return attrib; - } + return (java.math.BigDecimal)getNextAttribute(); } /** @@ -397,15 +332,7 @@ public class SQLInputImpl implements SQLInput { * position or if there are no more values in the stream */ public byte[] readBytes() throws SQLException { - byte[] attrib = (byte[])getNextAttribute(); - - if (attrib == null) { - lastValueWasNull = true; - return null; - } else { - lastValueWasNull = false; - return attrib; - } + return (byte[])getNextAttribute(); } /** @@ -422,15 +349,7 @@ public class SQLInputImpl implements SQLInput { * position or if there are no more values in the stream */ public java.sql.Date readDate() throws SQLException { - java.sql.Date attrib = (java.sql.Date)getNextAttribute(); - - if (attrib == null) { - lastValueWasNull = true; - return null; - } else { - lastValueWasNull = false; - return attrib; - } + return (java.sql.Date)getNextAttribute(); } /** @@ -448,15 +367,7 @@ public class SQLInputImpl implements SQLInput { * position; or if there are no further values in the stream. */ public java.sql.Time readTime() throws SQLException { - java.sql.Time attrib = (java.sql.Time)getNextAttribute(); - - if (attrib == null) { - lastValueWasNull = true; - return null; - } else { - lastValueWasNull = false; - return attrib; - } + return (java.sql.Time)getNextAttribute(); } /** @@ -469,15 +380,7 @@ public class SQLInputImpl implements SQLInput { * position; or if there are no further values in the stream. */ public java.sql.Timestamp readTimestamp() throws SQLException { - java.sql.Timestamp attrib = (java.sql.Timestamp)getNextAttribute(); - - if (attrib == null) { - lastValueWasNull = true; - return null; - } else { - lastValueWasNull = false; - return attrib; - } + return (java.sql.Timestamp)getNextAttribute(); } /** @@ -494,15 +397,7 @@ public class SQLInputImpl implements SQLInput { * position; or if there are no further values in the stream. */ public java.io.Reader readCharacterStream() throws SQLException { - java.io.Reader attrib = (java.io.Reader)getNextAttribute(); - - if (attrib == null) { - lastValueWasNull = true; - return null; - } else { - lastValueWasNull = false; - return attrib; - } + return (java.io.Reader)getNextAttribute(); } /** @@ -520,15 +415,7 @@ public class SQLInputImpl implements SQLInput { * position; or if there are no further values in the stream. */ public java.io.InputStream readAsciiStream() throws SQLException { - java.io.InputStream attrib = (java.io.InputStream)getNextAttribute(); - - if (attrib == null) { - lastValueWasNull = true; - return null; - } else { - lastValueWasNull = false; - return attrib; - } + return (java.io.InputStream)getNextAttribute(); } /** @@ -546,15 +433,7 @@ public class SQLInputImpl implements SQLInput { * position; or if there are no further values in the stream. */ public java.io.InputStream readBinaryStream() throws SQLException { - java.io.InputStream attrib = (java.io.InputStream)getNextAttribute(); - - if (attrib == null) { - lastValueWasNull = true; - return null; - } else { - lastValueWasNull = false; - return attrib; - } + return (java.io.InputStream)getNextAttribute(); } //================================================================ @@ -589,39 +468,32 @@ public class SQLInputImpl implements SQLInput { */ public Object readObject() throws SQLException { Object attrib = getNextAttribute(); - - if (attrib == null) { - lastValueWasNull = true; - return null; - } else { - lastValueWasNull = false; - if (attrib instanceof Struct) { - Struct s = (Struct)attrib; - // look up the class in the map - Class c = map.get(s.getSQLTypeName()); - if (c != null) { - // create new instance of the class - SQLData obj = null; - try { - obj = (SQLData)c.newInstance(); - } catch (java.lang.InstantiationException ex) { - throw new SQLException("Unable to instantiate: " + - ex.getMessage()); - } catch (java.lang.IllegalAccessException ex) { - throw new SQLException("Unable to instantiate: " + - ex.getMessage()); - } - // get the attributes from the struct - Object attribs[] = s.getAttributes(map); - // create the SQLInput "stream" - SQLInputImpl sqlInput = new SQLInputImpl(attribs, map); - // read the values... - obj.readSQL(sqlInput, s.getSQLTypeName()); - return obj; + if (attrib instanceof Struct) { + Struct s = (Struct)attrib; + // look up the class in the map + Class c = map.get(s.getSQLTypeName()); + if (c != null) { + // create new instance of the class + SQLData obj = null; + try { + obj = (SQLData)c.newInstance(); + } catch (java.lang.InstantiationException ex) { + throw new SQLException("Unable to instantiate: " + + ex.getMessage()); + } catch (java.lang.IllegalAccessException ex) { + throw new SQLException("Unable to instantiate: " + + ex.getMessage()); } + // get the attributes from the struct + Object attribs[] = s.getAttributes(map); + // create the SQLInput "stream" + SQLInputImpl sqlInput = new SQLInputImpl(attribs, map); + // read the values... + obj.readSQL(sqlInput, s.getSQLTypeName()); + return obj; } - return attrib; } + return attrib; } /** @@ -635,15 +507,7 @@ public class SQLInputImpl implements SQLInput { * position; or if there are no further values in the stream. */ public Ref readRef() throws SQLException { - Ref attrib = (Ref)getNextAttribute(); - - if (attrib == null) { - lastValueWasNull = true; - return null; - } else { - lastValueWasNull = false; - return attrib; - } + return (Ref)getNextAttribute(); } /** @@ -664,15 +528,7 @@ public class SQLInputImpl implements SQLInput { * position; or if there are no further values in the stream. */ public Blob readBlob() throws SQLException { - Blob attrib = (Blob)getNextAttribute(); - - if (attrib == null) { - lastValueWasNull = true; - return null; - } else { - lastValueWasNull = false; - return attrib; - } + return (Blob)getNextAttribute(); } /** @@ -693,15 +549,7 @@ public class SQLInputImpl implements SQLInput { * position; or if there are no further values in the stream. */ public Clob readClob() throws SQLException { - - Clob attrib = (Clob)getNextAttribute(); - if (attrib == null) { - lastValueWasNull = true; - return null; - } else { - lastValueWasNull = false; - return attrib; - } + return (Clob)getNextAttribute(); } /** @@ -723,15 +571,7 @@ public class SQLInputImpl implements SQLInput { */ public Array readArray() throws SQLException { - Array attrib = (Array)getNextAttribute(); - - if (attrib == null) { - lastValueWasNull = true; - return null; - } else { - lastValueWasNull = false; - return attrib; - } + return (Array)getNextAttribute(); } /** @@ -766,7 +606,7 @@ public class SQLInputImpl implements SQLInput { * position; or if there are no further values in the stream. */ public java.net.URL readURL() throws SQLException { - throw new SQLException("Operation not supported"); + return (java.net.URL)getNextAttribute(); } //---------------------------- JDBC 4.0 ------------------------- @@ -779,10 +619,11 @@ public class SQLInputImpl implements SQLInput { * at the head of the stream; null if the value read is * SQL NULL * @exception SQLException if a database access error occurs + * @since 1.6 */ public NClob readNClob() throws SQLException { - throw new UnsupportedOperationException("Operation not supported"); - } + return (NClob)getNextAttribute(); + } /** * Reads the next attribute in the stream and returns it as a String @@ -792,9 +633,10 @@ public class SQLInputImpl implements SQLInput { * * @return the attribute; if the value is SQL NULL, returns null * @exception SQLException if a database access error occurs + * @since 1.6 */ public String readNString() throws SQLException { - throw new UnsupportedOperationException("Operation not supported"); + return (String)getNextAttribute(); } /** @@ -805,12 +647,13 @@ public class SQLInputImpl implements SQLInput { * at the head of the stream; null if the value read is * SQL NULL * @exception SQLException if a database access error occurs + * @since 1.6 */ public SQLXML readSQLXML() throws SQLException { - throw new UnsupportedOperationException("Operation not supported"); + return (SQLXML)getNextAttribute(); } - /** + /** * Reads an SQL ROWID value from the stream and returns it as a * RowId object in the Java programming language. * @@ -818,9 +661,10 @@ public class SQLInputImpl implements SQLInput { * at the head of the stream; null if the value read is * SQL NULL * @exception SQLException if a database access error occurs + * @since 1.6 */ public RowId readRowId() throws SQLException { - throw new UnsupportedOperationException("Operation not supported"); + return (RowId)getNextAttribute(); } diff --git a/jdk/src/share/classes/javax/sql/rowset/serial/SQLOutputImpl.java b/jdk/src/share/classes/javax/sql/rowset/serial/SQLOutputImpl.java index fbe4a741726..cf2824cfda9 100644 --- a/jdk/src/share/classes/javax/sql/rowset/serial/SQLOutputImpl.java +++ b/jdk/src/share/classes/javax/sql/rowset/serial/SQLOutputImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -579,7 +579,7 @@ public class SQLOutputImpl implements SQLOutput { } - /** + /** * Writes the next attribute to the stream as a String * in the Java programming language. The driver converts this to a * SQL NCHAR or @@ -594,8 +594,8 @@ public class SQLOutputImpl implements SQLOutput { */ @SuppressWarnings("unchecked") public void writeNString(String x) throws SQLException { - throw new UnsupportedOperationException("Operation not supported"); - } + attribs.add(x); + } /** * Writes an SQL NCLOB value to the stream. @@ -608,8 +608,8 @@ public class SQLOutputImpl implements SQLOutput { */ @SuppressWarnings("unchecked") public void writeNClob(NClob x) throws SQLException { - throw new UnsupportedOperationException("Operation not supported"); - } + attribs.add(x); + } /** @@ -623,8 +623,8 @@ public class SQLOutputImpl implements SQLOutput { */ @SuppressWarnings("unchecked") public void writeRowId(RowId x) throws SQLException { - throw new UnsupportedOperationException("Operation not supported"); - } + attribs.add(x); + } /** @@ -638,7 +638,7 @@ public class SQLOutputImpl implements SQLOutput { */ @SuppressWarnings("unchecked") public void writeSQLXML(SQLXML x) throws SQLException { - throw new UnsupportedOperationException("Operation not supported"); + attribs.add(x); } } From 69757a1c928f4fd0fca97ccc239cacb5dd2f7dba Mon Sep 17 00:00:00 2001 From: Lance Andersen Date: Sat, 19 Jan 2013 10:53:14 -0500 Subject: [PATCH 076/138] 8005080: JDBC 4.2 Core changes Reviewed-by: naoto --- .../java/sql/BatchUpdateException.java | 414 ++++++++++++++---- .../classes/java/sql/CallableStatement.java | 366 +++++++++++++++- .../classes/java/sql/DatabaseMetaData.java | 41 +- jdk/src/share/classes/java/sql/Driver.java | 19 +- .../share/classes/java/sql/DriverManager.java | 38 +- jdk/src/share/classes/java/sql/JDBCType.java | 251 +++++++++++ .../classes/java/sql/PreparedStatement.java | 145 +++++- jdk/src/share/classes/java/sql/ResultSet.java | 147 ++++++- .../classes/java/sql/SQLTimeoutException.java | 8 +- jdk/src/share/classes/java/sql/SQLType.java | 56 +++ jdk/src/share/classes/java/sql/Statement.java | 312 ++++++++++++- jdk/src/share/classes/java/sql/Types.java | 12 +- jdk/src/share/classes/java/sql/package.html | 44 +- .../share/classes/javax/sql/DataSource.java | 48 +- .../share/classes/javax/sql/XADataSource.java | 20 +- .../classes/javax/sql/rowset/BaseRowSet.java | 364 +++++++-------- 16 files changed, 1912 insertions(+), 373 deletions(-) create mode 100644 jdk/src/share/classes/java/sql/JDBCType.java create mode 100644 jdk/src/share/classes/java/sql/SQLType.java diff --git a/jdk/src/share/classes/java/sql/BatchUpdateException.java b/jdk/src/share/classes/java/sql/BatchUpdateException.java index 19742c82fa7..9f5fb2e33b5 100644 --- a/jdk/src/share/classes/java/sql/BatchUpdateException.java +++ b/jdk/src/share/classes/java/sql/BatchUpdateException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,10 @@ package java.sql; +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.util.Arrays; /** @@ -49,6 +53,15 @@ import java.util.Arrays; * commands, the array element for any command * that failed is Statement.EXECUTE_FAILED. *

    + * A JDBC driver implementation should use + * the constructor {@code BatchUpdateException(String reason, String SQLState, + * int vendorCode, long []updateCounts,Throwable cause) } instead of + * constructors that take {@code int[]} for the update counts to avoid the + * possibility of overflow. + *

    + * If {@code Statement.executeLargeBatch} method is invoked it is recommended that + * {@code getLargeUpdateCounts} be called instead of {@code getUpdateCounts} + * in order to avoid a possible overflow of the integer update count. * @since 1.2 */ @@ -62,7 +75,11 @@ public class BatchUpdateException extends SQLException { * initialized by a call to the * {@link Throwable#initCause(java.lang.Throwable)} method. *

    - * + * Note: There is no validation of {@code updateCounts} for + * overflow and because of this it is recommended that you use the constructor + * {@code BatchUpdateException(String reason, String SQLState, + * int vendorCode, long []updateCounts,Throwable cause) }. + *

    * @param reason a description of the error * @param SQLState an XOPEN or SQL:2003 code identifying the exception * @param vendorCode an exception code used by a particular @@ -76,11 +93,14 @@ public class BatchUpdateException extends SQLException { * prior to the failure for JDBC drivers that stop processing after a command * failure * @since 1.2 + * @see #BatchUpdateException(java.lang.String, java.lang.String, int, long[], + * java.lang.Throwable) */ public BatchUpdateException( String reason, String SQLState, int vendorCode, int[] updateCounts ) { super(reason, SQLState, vendorCode); this.updateCounts = (updateCounts == null) ? null : Arrays.copyOf(updateCounts, updateCounts.length); + this.longUpdateCounts = (updateCounts == null) ? null : copyUpdateCount(updateCounts); } /** @@ -92,7 +112,11 @@ public class BatchUpdateException extends SQLException { * {@link Throwable#initCause(java.lang.Throwable)} method. The vendor code * is initialized to 0. *

    - * + * Note: There is no validation of {@code updateCounts} for + * overflow and because of this it is recommended that you use the constructor + * {@code BatchUpdateException(String reason, String SQLState, + * int vendorCode, long []updateCounts,Throwable cause) }. + *

    * @param reason a description of the exception * @param SQLState an XOPEN or SQL:2003 code identifying the exception * @param updateCounts an array of int, with each element @@ -104,6 +128,8 @@ public class BatchUpdateException extends SQLException { * prior to the failure for JDBC drivers that stop processing after a command * failure * @since 1.2 + * @see #BatchUpdateException(java.lang.String, java.lang.String, int, long[], + * java.lang.Throwable) */ public BatchUpdateException(String reason, String SQLState, int[] updateCounts) { @@ -119,8 +145,11 @@ public class BatchUpdateException extends SQLException { * SQLState is initialized to null * and the vender code is initialized to 0. *

    - * - * + * Note: There is no validation of {@code updateCounts} for + * overflow and because of this it is recommended that you use the constructor + * {@code BatchUpdateException(String reason, String SQLState, + * int vendorCode, long []updateCounts,Throwable cause) }. + *

    * @param reason a description of the exception * @param updateCounts an array of int, with each element * indicating the update count, Statement.SUCCESS_NO_INFO or @@ -131,6 +160,8 @@ public class BatchUpdateException extends SQLException { * prior to the failure for JDBC drivers that stop processing after a command * failure * @since 1.2 + * @see #BatchUpdateException(java.lang.String, java.lang.String, int, long[], + * java.lang.Throwable) */ public BatchUpdateException(String reason, int[] updateCounts) { this(reason, null, 0, updateCounts); @@ -144,7 +175,11 @@ public class BatchUpdateException extends SQLException { * and SQLState are initialized to null and the vendor code * is initialized to 0. *

    - * + * Note: There is no validation of {@code updateCounts} for + * overflow and because of this it is recommended that you use the constructor + * {@code BatchUpdateException(String reason, String SQLState, + * int vendorCode, long []updateCounts,Throwable cause) }. + *

    * @param updateCounts an array of int, with each element * indicating the update count, Statement.SUCCESS_NO_INFO or * Statement.EXECUTE_FAILED for each SQL command in @@ -154,6 +189,8 @@ public class BatchUpdateException extends SQLException { * prior to the failure for JDBC drivers that stop processing after a command * failure * @since 1.2 + * @see #BatchUpdateException(java.lang.String, java.lang.String, int, long[], + * java.lang.Throwable) */ public BatchUpdateException(int[] updateCounts) { this(null, null, 0, updateCounts); @@ -169,131 +206,167 @@ public class BatchUpdateException extends SQLException { *

    * * @since 1.2 + * @see #BatchUpdateException(java.lang.String, java.lang.String, int, long[], + * java.lang.Throwable) */ public BatchUpdateException() { this(null, null, 0, null); } - /** - * Constructs a BatchUpdateException object initialized with - * a given cause. - * The SQLState and updateCounts - * are initialized - * to null and the vendor code is initialized to 0. - * The reason is initialized to null if - * cause==null or to cause.toString() if - * cause!=null. - * @param cause the underlying reason for this SQLException - * (which is saved for later retrieval by the getCause() method); - * may be null indicating the cause is non-existent or unknown. - * @since 1.6 - */ - public BatchUpdateException(Throwable cause) { - this((cause == null ? null : cause.toString()), null, 0, null, cause); - } + /** + * Constructs a BatchUpdateException object initialized with + * a given cause. + * The SQLState and updateCounts + * are initialized + * to null and the vendor code is initialized to 0. + * The reason is initialized to null if + * cause==null or to cause.toString() if + * cause!=null. + * @param cause the underlying reason for this SQLException + * (which is saved for later retrieval by the getCause() method); + * may be null indicating the cause is non-existent or unknown. + * @since 1.6 + * @see #BatchUpdateException(java.lang.String, java.lang.String, int, long[], + * java.lang.Throwable) + */ + public BatchUpdateException(Throwable cause) { + this((cause == null ? null : cause.toString()), null, 0, (int[])null, cause); + } - /** - * Constructs a BatchUpdateException object initialized with a - * given cause and updateCounts. - * The SQLState is initialized - * to null and the vendor code is initialized to 0. - * The reason is initialized to null if - * cause==null or to cause.toString() if - * cause!=null. - * - * @param updateCounts an array of int, with each element - * indicating the update count, Statement.SUCCESS_NO_INFO or + /** + * Constructs a BatchUpdateException object initialized with a + * given cause and updateCounts. + * The SQLState is initialized + * to null and the vendor code is initialized to 0. + * The reason is initialized to null if + * cause==null or to cause.toString() if + * cause!=null. + *

    + * Note: There is no validation of {@code updateCounts} for + * overflow and because of this it is recommended that you use the constructor + * {@code BatchUpdateException(String reason, String SQLState, + * int vendorCode, long []updateCounts,Throwable cause) }. + *

    + * @param updateCounts an array of int, with each element + * indicating the update count, Statement.SUCCESS_NO_INFO or * Statement.EXECUTE_FAILED for each SQL command in * the batch for JDBC drivers that continue processing * after a command failure; an update count or * Statement.SUCCESS_NO_INFO for each SQL command in the batch * prior to the failure for JDBC drivers that stop processing after a command * failure - * @param cause the underlying reason for this SQLException - * (which is saved for later retrieval by the getCause() method); may be null indicating - * the cause is non-existent or unknown. - * @since 1.6 - */ - public BatchUpdateException(int []updateCounts , Throwable cause) { - this((cause == null ? null : cause.toString()), null, 0, updateCounts, cause); - } + * @param cause the underlying reason for this SQLException + * (which is saved for later retrieval by the getCause() method); may be null indicating + * the cause is non-existent or unknown. + * @since 1.6 + * @see #BatchUpdateException(java.lang.String, java.lang.String, int, long[], + * java.lang.Throwable) + */ + public BatchUpdateException(int []updateCounts , Throwable cause) { + this((cause == null ? null : cause.toString()), null, 0, updateCounts, cause); + } - /** - * Constructs a BatchUpdateException object initialized with - * a given reason, cause - * and updateCounts. The SQLState is initialized - * to null and the vendor code is initialized to 0. - * - * @param reason a description of the exception - * @param updateCounts an array of int, with each element - *indicating the update count, Statement.SUCCESS_NO_INFO or + /** + * Constructs a BatchUpdateException object initialized with + * a given reason, cause + * and updateCounts. The SQLState is initialized + * to null and the vendor code is initialized to 0. + *

    + * Note: There is no validation of {@code updateCounts} for + * overflow and because of this it is recommended that you use the constructor + * {@code BatchUpdateException(String reason, String SQLState, + * int vendorCode, long []updateCounts,Throwable cause) }. + *

    + * @param reason a description of the exception + * @param updateCounts an array of int, with each element + *indicating the update count, Statement.SUCCESS_NO_INFO or * Statement.EXECUTE_FAILED for each SQL command in * the batch for JDBC drivers that continue processing * after a command failure; an update count or * Statement.SUCCESS_NO_INFO for each SQL command in the batch * prior to the failure for JDBC drivers that stop processing after a command * failure - * @param cause the underlying reason for this SQLException (which is saved for later retrieval by the getCause() method); - * may be null indicating - * the cause is non-existent or unknown. - * @since 1.6 - */ - public BatchUpdateException(String reason, int []updateCounts, Throwable cause) { - this(reason, null, 0, updateCounts, cause); - } + * @param cause the underlying reason for this SQLException (which is saved for later retrieval by the getCause() method); + * may be null indicating + * the cause is non-existent or unknown. + * @since 1.6 + * @see #BatchUpdateException(java.lang.String, java.lang.String, int, long[], + * java.lang.Throwable) + */ + public BatchUpdateException(String reason, int []updateCounts, Throwable cause) { + this(reason, null, 0, updateCounts, cause); + } - /** - * Constructs a BatchUpdateException object initialized with - * a given reason, SQLState,cause, and + /** + * Constructs a BatchUpdateException object initialized with + * a given reason, SQLState,cause, and * updateCounts. The vendor code is initialized to 0. - * - * @param reason a description of the exception - * @param SQLState an XOPEN or SQL:2003 code identifying the exception - * @param updateCounts an array of int, with each element - * indicating the update count, Statement.SUCCESS_NO_INFO or + * + * @param reason a description of the exception + * @param SQLState an XOPEN or SQL:2003 code identifying the exception + * @param updateCounts an array of int, with each element + * indicating the update count, Statement.SUCCESS_NO_INFO or * Statement.EXECUTE_FAILED for each SQL command in * the batch for JDBC drivers that continue processing * after a command failure; an update count or * Statement.SUCCESS_NO_INFO for each SQL command in the batch * prior to the failure for JDBC drivers that stop processing after a command * failure - * @param cause the underlying reason for this SQLException (which is saved for later retrieval by the getCause() method); - * may be null indicating - * the cause is non-existent or unknown. - * @since 1.6 - */ - public BatchUpdateException(String reason, String SQLState, - int []updateCounts, Throwable cause) { - this(reason, SQLState, 0, updateCounts, cause); - } + *

    + * Note: There is no validation of {@code updateCounts} for + * overflow and because of this it is recommended that you use the constructor + * {@code BatchUpdateException(String reason, String SQLState, + * int vendorCode, long []updateCounts,Throwable cause) }. + *

    + * @param cause the underlying reason for this SQLException + * (which is saved for later retrieval by the getCause() method); + * may be null indicating + * the cause is non-existent or unknown. + * @since 1.6 + * @see #BatchUpdateException(java.lang.String, java.lang.String, int, long[], + * java.lang.Throwable) + */ + public BatchUpdateException(String reason, String SQLState, + int []updateCounts, Throwable cause) { + this(reason, SQLState, 0, updateCounts, cause); + } - /** - * Constructs a BatchUpdateException object initialized with - * a given reason, SQLState, vendorCode - * cause and updateCounts. - * - * @param reason a description of the error - * @param SQLState an XOPEN or SQL:2003 code identifying the exception - * @param vendorCode an exception code used by a particular - * database vendor - * @param updateCounts an array of int, with each element - *indicating the update count, Statement.SUCCESS_NO_INFO or + /** + * Constructs a BatchUpdateException object initialized with + * a given reason, SQLState, vendorCode + * cause and updateCounts. + * + * @param reason a description of the error + * @param SQLState an XOPEN or SQL:2003 code identifying the exception + * @param vendorCode an exception code used by a particular + * database vendor + * @param updateCounts an array of int, with each element + *indicating the update count, Statement.SUCCESS_NO_INFO or * Statement.EXECUTE_FAILED for each SQL command in * the batch for JDBC drivers that continue processing * after a command failure; an update count or * Statement.SUCCESS_NO_INFO for each SQL command in the batch * prior to the failure for JDBC drivers that stop processing after a command * failure - * @param cause the underlying reason for this SQLException (which is saved for later retrieval by the getCause() method); - * may be null indicating - * the cause is non-existent or unknown. - * @since 1.6 - */ - public BatchUpdateException(String reason, String SQLState, int vendorCode, + *

    + * Note: There is no validation of {@code updateCounts} for + * overflow and because of this it is recommended that you use the constructor + * {@code BatchUpdateException(String reason, String SQLState, + * int vendorCode, long []updateCounts,Throwable cause) }. + *

    + * @param cause the underlying reason for this SQLException (which is saved for later retrieval by the getCause() method); + * may be null indicating + * the cause is non-existent or unknown. + * @since 1.6 + * @see #BatchUpdateException(java.lang.String, java.lang.String, int, long[], + * java.lang.Throwable) + */ + public BatchUpdateException(String reason, String SQLState, int vendorCode, int []updateCounts,Throwable cause) { super(reason, SQLState, vendorCode, cause); this.updateCounts = (updateCounts == null) ? null : Arrays.copyOf(updateCounts, updateCounts.length); - } + this.longUpdateCounts = (updateCounts == null) ? null : copyUpdateCount(updateCounts); + } /** * Retrieves the update count for each update statement in the batch @@ -324,17 +397,168 @@ public class BatchUpdateException extends SQLException { * failed to execute successfully * * @since 1.3 + * @see #getLargeUpdateCounts() */ public int[] getUpdateCounts() { return (updateCounts == null) ? null : Arrays.copyOf(updateCounts, updateCounts.length); } + /** + * Constructs a BatchUpdateException object initialized with + * a given reason, SQLState, vendorCode + * cause and updateCounts. + *

    + * This constructor should be used when the returned update count may exceed + * {@link Integer.MAX_VALUE}. + *

    + * @param reason a description of the error + * @param SQLState an XOPEN or SQL:2003 code identifying the exception + * @param vendorCode an exception code used by a particular + * database vendor + * @param updateCounts an array of long, with each element + *indicating the update count, Statement.SUCCESS_NO_INFO or + * Statement.EXECUTE_FAILED for each SQL command in + * the batch for JDBC drivers that continue processing + * after a command failure; an update count or + * Statement.SUCCESS_NO_INFO for each SQL command in the batch + * prior to the failure for JDBC drivers that stop processing after a command + * failure + * @param cause the underlying reason for this SQLException + * (which is saved for later retrieval by the getCause() method); + * may be null indicating the cause is non-existent or unknown. + * @since 1.8 + */ + public BatchUpdateException(String reason, String SQLState, int vendorCode, + long []updateCounts,Throwable cause) { + super(reason, SQLState, vendorCode, cause); + this.longUpdateCounts = (updateCounts == null) ? null : Arrays.copyOf(updateCounts, updateCounts.length); + this.updateCounts = (longUpdateCounts == null) ? null : copyUpdateCount(longUpdateCounts); + } + + /** + * Retrieves the update count for each update statement in the batch + * update that executed successfully before this exception occurred. + * A driver that implements batch updates may or may not continue to + * process the remaining commands in a batch when one of the commands + * fails to execute properly. If the driver continues processing commands, + * the array returned by this method will have as many elements as + * there are commands in the batch; otherwise, it will contain an + * update count for each command that executed successfully before + * the BatchUpdateException was thrown. + *

    + * This method should be used when {@code Statement.executeLargeBatch} is + * invoked and the returned update count may exceed {@link Integer.MAX_VALUE}. + *

    + * @return an array of long containing the update counts + * for the updates that were executed successfully before this error + * occurred. Or, if the driver continues to process commands after an + * error, one of the following for every command in the batch: + *

      + *
    1. an update count + *
    2. Statement.SUCCESS_NO_INFO to indicate that the command + * executed successfully but the number of rows affected is unknown + *
    3. Statement.EXECUTE_FAILED to indicate that the command + * failed to execute successfully + *
    + * @since 1.8 + */ + public long[] getLargeUpdateCounts() { + return (longUpdateCounts == null) ? null : + Arrays.copyOf(longUpdateCounts, longUpdateCounts.length); + } + /** * The array that describes the outcome of a batch execution. * @serial * @since 1.2 */ - private final int[] updateCounts; + private int[] updateCounts; + + /* + * Starting with Java SE 8, JDBC has added support for returning an update + * count > Integer.MAX_VALUE. Because of this the following changes were made + * to BatchUpdateException: + *
      + *
    • Add field longUpdateCounts
    • + *
    • Add Constructorr which takes long[] for update counts
    • + *
    • Add getLargeUpdateCounts method
    • + *
    + * When any of the constructors are called, the int[] and long[] updateCount + * fields are populated by copying the one array to each other. + * + * As the JDBC driver passes in the updateCounts, there has always been the + * possiblity for overflow and BatchUpdateException does not need to account + * for that, it simply copies the arrays. + * + * JDBC drivers should always use the constructor that specifies long[] and + * JDBC application developers should call getLargeUpdateCounts. + */ + + /** + * The array that describes the outcome of a batch execution. + * @serial + * @since 1.8 + */ + private long[] longUpdateCounts; private static final long serialVersionUID = 5977529877145521757L; + + /* + * Utility method to copy int[] updateCount to long[] updateCount + */ + private static long[] copyUpdateCount(int[] uc) { + long[] copy = new long[uc.length]; + for(int i= 0; i< uc.length; i++) { + copy[i] = uc[i]; + } + return copy; + } + + /* + * Utility method to copy long[] updateCount to int[] updateCount. + * No checks for overflow will be done as it is expected a user will call + * getLargeUpdateCounts. + */ + private static int[] copyUpdateCount(long[] uc) { + int[] copy = new int[uc.length]; + for(int i= 0; i< uc.length; i++) { + copy[i] = (int) uc[i]; + } + return copy; + } + /** + * readObject is called to restore the state of the + * {@code BatchUpdateException} from a stream. + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException { + + ObjectInputStream.GetField fields = s.readFields(); + int[] tmp = (int[])fields.get("updateCounts", null); + long[] tmp2 = (long[])fields.get("longUpdateCounts", null); + if(tmp != null && tmp2 != null && tmp.length != tmp2.length) + throw new InvalidObjectException("update counts are not the expected size"); + if (tmp != null) + updateCounts = tmp.clone(); + if (tmp2 != null) + longUpdateCounts = tmp2.clone(); + if(updateCounts == null && longUpdateCounts != null) + updateCounts = copyUpdateCount(longUpdateCounts); + if(longUpdateCounts == null && updateCounts != null) + longUpdateCounts = copyUpdateCount(updateCounts); + + } + + /** + * writeObject is called to save the state of the {@code BatchUpdateException} + * to a stream. + */ + private void writeObject(ObjectOutputStream s) + throws IOException, ClassNotFoundException { + + ObjectOutputStream.PutField fields = s.putFields(); + fields.put("updateCounts", updateCounts); + fields.put("longUpdateCounts", longUpdateCounts); + s.writeFields(); + } } diff --git a/jdk/src/share/classes/java/sql/CallableStatement.java b/jdk/src/share/classes/java/sql/CallableStatement.java index a86caa69ebb..1200f771dad 100644 --- a/jdk/src/share/classes/java/sql/CallableStatement.java +++ b/jdk/src/share/classes/java/sql/CallableStatement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1079,9 +1079,7 @@ public interface CallableStatement extends PreparedStatement { int length) throws SQLException; /** - * Sets the value of the designated parameter with the given object. The second - * argument must be an object type; for integral values, the - * java.lang equivalent objects should be used. + * Sets the value of the designated parameter with the given object. * *

    The given Java object will be converted to the given targetSqlType * before being sent to the database. @@ -1109,13 +1107,8 @@ public interface CallableStatement extends PreparedStatement { * @exception SQLException if parameterName does not correspond to a named * parameter; if a database access error occurs or * this method is called on a closed CallableStatement - * @exception SQLFeatureNotSupportedException if targetSqlType is - * a ARRAY, BLOB, CLOB, - * DATALINK, JAVA_OBJECT, NCHAR, - * NCLOB, NVARCHAR, LONGNVARCHAR, - * REF, ROWID, SQLXML - * or STRUCT data type and the JDBC driver does not support - * this data type + * @exception SQLFeatureNotSupportedException if + * the JDBC driver does not support this data type * @see Types * @see #getObject * @since 1.4 @@ -1125,8 +1118,10 @@ public interface CallableStatement extends PreparedStatement { /** * Sets the value of the designated parameter with the given object. - * This method is like the method setObject - * above, except that it assumes a scale of zero. + * + * This method is similar to {@link #setObject(String parameterName, + * Object x, int targetSqlType, int scaleOrLength)}, + * except that it assumes a scale of zero. * * @param parameterName the name of the parameter * @param x the object containing the input parameter value @@ -1135,13 +1130,8 @@ public interface CallableStatement extends PreparedStatement { * @exception SQLException if parameterName does not correspond to a named * parameter; if a database access error occurs or * this method is called on a closed CallableStatement - * @exception SQLFeatureNotSupportedException if targetSqlType is - * a ARRAY, BLOB, CLOB, - * DATALINK, JAVA_OBJECT, NCHAR, - * NCLOB, NVARCHAR, LONGNVARCHAR, - * REF, ROWID, SQLXML - * or STRUCT data type and the JDBC driver does not support - * this data type + * @exception SQLFeatureNotSupportedException if + * the JDBC driver does not support this data type * @see #getObject * @since 1.4 */ @@ -1150,8 +1140,6 @@ public interface CallableStatement extends PreparedStatement { /** * Sets the value of the designated parameter with the given object. - * The second parameter must be of type Object; therefore, the - * java.lang equivalent objects should be used for built-in types. * *

    The JDBC specification specifies a standard mapping from * Java Object types to SQL types. The given argument @@ -2497,4 +2485,338 @@ public interface CallableStatement extends PreparedStatement { */ public T getObject(String parameterName, Class type) throws SQLException; + //------------------------- JDBC 4.2 ----------------------------------- + + /** + *

    Sets the value of the designated parameter with the given object. + * + * If the second argument is an {@code InputStream} then the stream + * must contain the number of bytes specified by scaleOrLength. + * If the second argument is a {@code Reader} then the reader must + * contain the number of characters specified + * by scaleOrLength. If these conditions are not true the driver + * will generate a + * {@code SQLException} when the prepared statement is executed. + * + *

    The given Java object will be converted to the given targetSqlType + * before being sent to the database. + * + * If the object has a custom mapping (is of a class implementing the + * interface {@code SQLData}), + * the JDBC driver should call the method {@code SQLData.writeSQL} to + * write it to the SQL data stream. + * If, on the other hand, the object is of a class implementing + * {@code Ref}, {@code Blob}, {@code Clob}, {@code NClob}, + * {@code Struct}, {@code java.net.URL}, + * or {@code Array}, the driver should pass it to the database as a + * value of the corresponding SQL type. + * + *

    Note that this method may be used to pass database-specific + * abstract data types. + *

    + * The default implementation will throw {@code SQLFeatureNotSupportedException} + * + * @param parameterName the name of the parameter + * @param x the object containing the input parameter value + * @param targetSqlType the SQL type to be + * sent to the database. The scale argument may further qualify this type. + * @param scaleOrLength for {@code java.sql.JDBCType.DECIMAL} + * or {@code java.sql.JDBCType.NUMERIC types}, + * this is the number of digits after the decimal point. For + * Java Object types {@code InputStream} and {@code Reader}, + * this is the length + * of the data in the stream or reader. For all other types, + * this value will be ignored. + * @exception SQLException if parameterName does not correspond to a named + * parameter; if a database access error occurs + * or this method is called on a closed {@code CallableStatement} or + * if the Java Object specified by x is an InputStream + * or Reader object and the value of the scale parameter is less + * than zero + * @exception SQLFeatureNotSupportedException if + * the JDBC driver does not support this data type + * @see JDBCType + * @see SQLType + * + * @since 1.8 + */ + default void setObject(String parameterName, Object x, SQLType targetSqlType, + int scaleOrLength) throws SQLException { + throw new SQLFeatureNotSupportedException("setObject not implemented"); + } + /** + * Sets the value of the designated parameter with the given object. + * + * This method is similar to {@link #setObject(String parameterName, + * Object x, SQLType targetSqlType, int scaleOrLength)}, + * except that it assumes a scale of zero. + *

    + * The default implementation will throw {@code SQLFeatureNotSupportedException} + * + * @param parameterName the name of the parameter + * @param x the object containing the input parameter value + * @param targetSqlType the SQL type to be sent to the database + * @exception SQLException if parameterName does not correspond to a named + * parameter; if a database access error occurs + * or this method is called on a closed {@code CallableStatement} + * @exception SQLFeatureNotSupportedException if + * the JDBC driver does not support this data type + * @see JDBCType + * @see SQLType + * @since 1.8 + */ + default void setObject(String parameterName, Object x, SQLType targetSqlType) + throws SQLException { + throw new SQLFeatureNotSupportedException("setObject not implemented"); + } + + /** + * Registers the OUT parameter in ordinal position + * {@code parameterIndex} to the JDBC type + * {@code sqlType}. All OUT parameters must be registered + * before a stored procedure is executed. + *

    + * The JDBC type specified by {@code sqlType} for an OUT + * parameter determines the Java type that must be used + * in the {@code get} method to read the value of that parameter. + *

    + * If the JDBC type expected to be returned to this output parameter + * is specific to this particular database, {@code sqlType} + * may be {@code JDBCType.OTHER} or a {@code SQLType} that is supported by + * the JDBC driver. The method + * {@link #getObject} retrieves the value. + *

    + * The default implementation will throw {@code SQLFeatureNotSupportedException} + * + * @param parameterIndex the first parameter is 1, the second is 2, + * and so on + * @param sqlType the JDBC type code defined by {@code SQLType} to use to + * register the OUT Parameter. + * If the parameter is of JDBC type {@code JDBCType.NUMERIC} + * or {@code JDBCType.DECIMAL}, the version of + * {@code registerOutParameter} that accepts a scale value + * should be used. + * + * @exception SQLException if the parameterIndex is not valid; + * if a database access error occurs or + * this method is called on a closed {@code CallableStatement} + * @exception SQLFeatureNotSupportedException if + * the JDBC driver does not support this data type + * @see JDBCType + * @see SQLType + * @since 1.8 + */ + default void registerOutParameter(int parameterIndex, SQLType sqlType) + throws SQLException { + throw new SQLFeatureNotSupportedException("registerOutParameter not implemented"); + } + + /** + * Registers the parameter in ordinal position + * {@code parameterIndex} to be of JDBC type + * {@code sqlType}. All OUT parameters must be registered + * before a stored procedure is executed. + *

    + * The JDBC type specified by {@code sqlType} for an OUT + * parameter determines the Java type that must be used + * in the {@code get} method to read the value of that parameter. + *

    + * This version of {@code registrOutParameter} should be + * used when the parameter is of JDBC type {@code JDBCType.NUMERIC} + * or {@code JDBCType.DECIMAL}. + *

    + * The default implementation will throw {@code SQLFeatureNotSupportedException} + * + * @param parameterIndex the first parameter is 1, the second is 2, + * and so on + * @param sqlType the JDBC type code defined by {@code SQLType} to use to + * register the OUT Parameter. + * @param scale the desired number of digits to the right of the + * decimal point. It must be greater than or equal to zero. + * @exception SQLException if the parameterIndex is not valid; + * if a database access error occurs or + * this method is called on a closed {@code CallableStatement} + * @exception SQLFeatureNotSupportedException if + * the JDBC driver does not support this data type + * @see JDBCType + * @see SQLType + * @since 1.8 + */ + default void registerOutParameter(int parameterIndex, SQLType sqlType, + int scale) throws SQLException { + throw new SQLFeatureNotSupportedException("registerOutParameter not implemented"); + } + /** + * Registers the designated output parameter. + * This version of + * the method {@code registrOutParameter} + * should be used for a user-defined or {@code REF} output parameter. + * Examples + * of user-defined types include: {@code STRUCT}, {@code DISTINCT}, + * {@code JAVA_OBJECT}, and named array types. + *

    + * All OUT parameters must be registered + * before a stored procedure is executed. + *

    For a user-defined parameter, the fully-qualified SQL + * type name of the parameter should also be given, while a {@code REF} + * parameter requires that the fully-qualified type name of the + * referenced type be given. A JDBC driver that does not need the + * type code and type name information may ignore it. To be portable, + * however, applications should always provide these values for + * user-defined and {@code REF} parameters. + * + * Although it is intended for user-defined and {@code REF} parameters, + * this method may be used to register a parameter of any JDBC type. + * If the parameter does not have a user-defined or {@code REF} type, the + * typeName parameter is ignored. + * + *

    Note: When reading the value of an out parameter, you + * must use the getter method whose Java type corresponds to the + * parameter's registered SQL type. + *

    + * The default implementation will throw {@code SQLFeatureNotSupportedException} + * + * @param parameterIndex the first parameter is 1, the second is 2,... + * @param sqlType the JDBC type code defined by {@code SQLType} to use to + * register the OUT Parameter. + * @param typeName the fully-qualified name of an SQL structured type + * @exception SQLException if the parameterIndex is not valid; + * if a database access error occurs or + * this method is called on a closed {@code CallableStatement} + * @exception SQLFeatureNotSupportedException if + * the JDBC driver does not support this data type + * @see JDBCType + * @see SQLType + * @since 1.8 + */ + default void registerOutParameter (int parameterIndex, SQLType sqlType, + String typeName) throws SQLException { + throw new SQLFeatureNotSupportedException("registerOutParameter not implemented"); + } + + /** + * Registers the OUT parameter named + * parameterName to the JDBC type + * {@code sqlType}. All OUT parameters must be registered + * before a stored procedure is executed. + *

    + * The JDBC type specified by {@code sqlType} for an OUT + * parameter determines the Java type that must be used + * in the {@code get} method to read the value of that parameter. + *

    + * If the JDBC type expected to be returned to this output parameter + * is specific to this particular database, {@code sqlType} + * should be {@code JDBCType.OTHER} or a {@code SQLType} that is supported + * by the JDBC driver.. The method + * {@link #getObject} retrieves the value. + *

    + * The default implementation will throw {@code SQLFeatureNotSupportedException} + * + * @param parameterName the name of the parameter + * @param sqlType the JDBC type code defined by {@code SQLType} to use to + * register the OUT Parameter. + * If the parameter is of JDBC type {@code JDBCType.NUMERIC} + * or {@code JDBCType.DECIMAL}, the version of + * {@code registrOutParameter} that accepts a scale value + * should be used. + * @exception SQLException if parameterName does not correspond to a named + * parameter; if a database access error occurs or + * this method is called on a closed {@code CallableStatement} + * @exception SQLFeatureNotSupportedException if + * the JDBC driver does not support this data type + * or if the JDBC driver does not support + * this method + * @since 1.8 + * @see JDBCType + * @see SQLType + */ + default void registerOutParameter(String parameterName, SQLType sqlType) + throws SQLException { + throw new SQLFeatureNotSupportedException("registerOutParameter not implemented"); + } + + /** + * Registers the parameter named + * parameterName to be of JDBC type + * {@code sqlType}. All OUT parameters must be registered + * before a stored procedure is executed. + *

    + * The JDBC type specified by {@code sqlType} for an OUT + * parameter determines the Java type that must be used + * in the {@code get} method to read the value of that parameter. + *

    + * This version of {@code registrOutParameter} should be + * used when the parameter is of JDBC type {@code JDBCType.NUMERIC} + * or {@code JDBCType.DECIMAL}. + *

    + * The default implementation will throw {@code SQLFeatureNotSupportedException} + * + * @param parameterName the name of the parameter + * @param sqlType the JDBC type code defined by {@code SQLType} to use to + * register the OUT Parameter. + * @param scale the desired number of digits to the right of the + * decimal point. It must be greater than or equal to zero. + * @exception SQLException if parameterName does not correspond to a named + * parameter; if a database access error occurs or + * this method is called on a closed {@code CallableStatement} + * @exception SQLFeatureNotSupportedException if + * the JDBC driver does not support this data type + * or if the JDBC driver does not support + * this method + * @since 1.8 + * @see JDBCType + * @see SQLType + */ + default void registerOutParameter(String parameterName, SQLType sqlType, + int scale) throws SQLException { + throw new SQLFeatureNotSupportedException("registerOutParameter not implemented"); + } + + /** + * Registers the designated output parameter. This version of + * the method {@code registrOutParameter} + * should be used for a user-named or REF output parameter. Examples + * of user-named types include: STRUCT, DISTINCT, JAVA_OBJECT, and + * named array types. + *

    + * All OUT parameters must be registered + * before a stored procedure is executed. + *

    + * For a user-named parameter the fully-qualified SQL + * type name of the parameter should also be given, while a REF + * parameter requires that the fully-qualified type name of the + * referenced type be given. A JDBC driver that does not need the + * type code and type name information may ignore it. To be portable, + * however, applications should always provide these values for + * user-named and REF parameters. + * + * Although it is intended for user-named and REF parameters, + * this method may be used to register a parameter of any JDBC type. + * If the parameter does not have a user-named or REF type, the + * typeName parameter is ignored. + * + *

    Note: When reading the value of an out parameter, you + * must use the {@code getXXX} method whose Java type XXX corresponds to the + * parameter's registered SQL type. + *

    + * The default implementation will throw {@code SQLFeatureNotSupportedException} + * + * @param parameterName the name of the parameter + * @param sqlType the JDBC type code defined by {@code SQLType} to use to + * register the OUT Parameter. + * @param typeName the fully-qualified name of an SQL structured type + * @exception SQLException if parameterName does not correspond to a named + * parameter; if a database access error occurs or + * this method is called on a closed {@code CallableStatement} + * @exception SQLFeatureNotSupportedException if + * the JDBC driver does not support this data type + * or if the JDBC driver does not support this method + * @see JDBCType + * @see SQLType + * @since 1.8 + */ + default void registerOutParameter (String parameterName, SQLType sqlType, + String typeName) throws SQLException { + throw new SQLFeatureNotSupportedException("registerOutParameter not implemented"); + } } diff --git a/jdk/src/share/classes/java/sql/DatabaseMetaData.java b/jdk/src/share/classes/java/sql/DatabaseMetaData.java index 1ad5ea17358..7330b8eac02 100644 --- a/jdk/src/share/classes/java/sql/DatabaseMetaData.java +++ b/jdk/src/share/classes/java/sql/DatabaseMetaData.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2522,10 +2522,10 @@ public interface DatabaseMetaData extends Wrapper { *

  • ASC_OR_DESC String => column sort sequence, "A" => ascending, * "D" => descending, may be null if sort sequence is not supported; * null when TYPE is tableIndexStatistic - *
  • CARDINALITY int => When TYPE is tableIndexStatistic, then + *
  • CARDINALITY long => When TYPE is tableIndexStatistic, then * this is the number of rows in the table; otherwise, it is the * number of unique values in the index. - *
  • PAGES int => When TYPE is tableIndexStatisic then + *
  • PAGES long => When TYPE is tableIndexStatisic then * this is the number of pages used for the table, otherwise it * is the number of pages used for the current index. *
  • FILTER_CONDITION String => Filter condition, if any. @@ -2759,7 +2759,7 @@ public interface DatabaseMetaData extends Wrapper { /** * Retrieves whether this database supports batch updates. * - * @return true if this database supports batch upcates; + * @return true if this database supports batch updates; * false otherwise * @exception SQLException if a database access error occurs * @since 1.2 @@ -3652,4 +3652,37 @@ public interface DatabaseMetaData extends Wrapper { * @since 1.7 */ boolean generatedKeyAlwaysReturned() throws SQLException; + + //--------------------------JDBC 4.2 ----------------------------- + + /** + * + * Retrieves the maximum number of bytes this database allows for + * the logical size for a {@code LOB}. + *

    + * The default implementation will return {@code 0} + * + * @return the maximum number of bytes allowed; a result of zero + * means that there is no limit or the limit is not known + * @exception SQLException if a database access error occurs + * @since 1.8 + */ + default long getMaxLogicalLobSize() throws SQLException { + return 0; + } + + /** + * Retrieves whether this database supports REF CURSOR. + *

    + * The default implementation will return {@code false} + * + * @return {@code true} if this database supports REF CURSOR; + * {@code false} otherwise + * @exception SQLException if a database access error occurs + * @since 1.8 + */ + default boolean supportsRefCursors() throws SQLException{ + return false; + } + } diff --git a/jdk/src/share/classes/java/sql/Driver.java b/jdk/src/share/classes/java/sql/Driver.java index 4abc6b3c81f..1682b75c910 100644 --- a/jdk/src/share/classes/java/sql/Driver.java +++ b/jdk/src/share/classes/java/sql/Driver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -65,10 +65,15 @@ public interface Driver { * driver to connect to the given URL but has trouble connecting to * the database. * - *

    The java.util.Properties argument can be used to pass + *

    The {@code Properties} argument can be used to pass * arbitrary string tag/value pairs as connection arguments. * Normally at least "user" and "password" properties should be - * included in the Properties object. + * included in the {@code Properties} object. + *

    + * Note: If a property is specified as part of the {@code url} and + * is also specified in the {@code Properties} object, it is + * implementation-defined as to which value will take precedence. For + * maximum portability, an application should only specify a property once. * * @param url the URL of the database to which to connect * @param info a list of arbitrary string tag/value pairs as @@ -76,7 +81,8 @@ public interface Driver { * "password" property should be included. * @return a Connection object that represents a * connection to the URL - * @exception SQLException if a database access error occurs + * @exception SQLException if a database access error occurs or the url is + * {@code null} */ Connection connect(String url, java.util.Properties info) throws SQLException; @@ -84,13 +90,14 @@ public interface Driver { /** * Retrieves whether the driver thinks that it can open a connection * to the given URL. Typically drivers will return true if they - * understand the subprotocol specified in the URL and false if + * understand the sub-protocol specified in the URL and false if * they do not. * * @param url the URL of the database * @return true if this driver understands the given URL; * false otherwise - * @exception SQLException if a database access error occurs + * @exception SQLException if a database access error occurs or the url is + * {@code null} */ boolean acceptsURL(String url) throws SQLException; diff --git a/jdk/src/share/classes/java/sql/DriverManager.java b/jdk/src/share/classes/java/sql/DriverManager.java index 4021fa24435..b0ca1cd4dea 100644 --- a/jdk/src/share/classes/java/sql/DriverManager.java +++ b/jdk/src/share/classes/java/sql/DriverManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -172,6 +172,12 @@ public class DriverManager { * Attempts to establish a connection to the given database URL. * The DriverManager attempts to select an appropriate driver from * the set of registered JDBC drivers. + *

    + * Note: If a property is specified as part of the {@code url} and + * is also specified in the {@code Properties} object, it is + * implementation-defined as to which value will take precedence. + * For maximum portability, an application should only specify a + * property once. * * @param url a database url of the form * jdbc:subprotocol:subname @@ -179,7 +185,12 @@ public class DriverManager { * connection arguments; normally at least a "user" and * "password" property should be included * @return a Connection to the URL - * @exception SQLException if a database access error occurs + * @exception SQLException if a database access error occurs or the url is + * {@code null} + * @throws SQLTimeoutException when the driver has determined that the + * timeout value specified by the {@code setLoginTimeout} method + * has been exceeded and has at least tried to cancel the + * current database connection attempt */ public static Connection getConnection(String url, java.util.Properties info) throws SQLException { @@ -195,6 +206,12 @@ public class DriverManager { * Attempts to establish a connection to the given database URL. * The DriverManager attempts to select an appropriate driver from * the set of registered JDBC drivers. + *

    + * Note: If a property is specified as part of the {@code url} and + * is also specified in the {@code Properties} object, it is + * implementation-defined as to which value will take precedence. + * For maximum portability, an application should only specify a + * property once. * * @param url a database url of the form * jdbc:subprotocol:subname @@ -202,7 +219,12 @@ public class DriverManager { * made * @param password the user's password * @return a connection to the URL - * @exception SQLException if a database access error occurs + * @exception SQLException if a database access error occurs or the url is + * {@code null} + * @throws SQLTimeoutException when the driver has determined that the + * timeout value specified by the {@code setLoginTimeout} method + * has been exceeded and has at least tried to cancel the + * current database connection attempt */ public static Connection getConnection(String url, String user, String password) throws SQLException { @@ -230,7 +252,12 @@ public class DriverManager { * @param url a database url of the form * jdbc:subprotocol:subname * @return a connection to the URL - * @exception SQLException if a database access error occurs + * @exception SQLException if a database access error occurs or the url is + * {@code null} + * @throws SQLTimeoutException when the driver has determined that the + * timeout value specified by the {@code setLoginTimeout} method + * has been exceeded and has at least tried to cancel the + * current database connection attempt */ public static Connection getConnection(String url) throws SQLException { @@ -380,7 +407,8 @@ public class DriverManager { /** * Sets the maximum time in seconds that a driver will wait - * while attempting to connect to a database. + * while attempting to connect to a database once the driver has + * been identified. * * @param seconds the login time limit in seconds; zero means there is no limit * @see #getLoginTimeout diff --git a/jdk/src/share/classes/java/sql/JDBCType.java b/jdk/src/share/classes/java/sql/JDBCType.java new file mode 100644 index 00000000000..f1453059605 --- /dev/null +++ b/jdk/src/share/classes/java/sql/JDBCType.java @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.sql; + +/** + *

    Defines the constants that are used to identify generic + * SQL types, called JDBC types. + *

    + * @see SQLType + * @since 1.8 + */ +public enum JDBCType implements SQLType { + + /** + * Identifies the generic SQL type {@code BIT}. + */ + BIT(Types.BIT), + /** + * Identifies the generic SQL type {@code TINYINT}. + */ + TINYINT(Types.TINYINT), + /** + * Identifies the generic SQL type {@code SMALLINT}. + */ + SMALLINT(Types.SMALLINT), + /** + * Identifies the generic SQL type {@code INTEGER}. + */ + INTEGER(Types.INTEGER), + /** + * Identifies the generic SQL type {@code BIGINT}. + */ + BIGINT(Types.BIGINT), + /** + * Identifies the generic SQL type {@code FLOAT}. + */ + FLOAT(Types.FLOAT), + /** + * Identifies the generic SQL type {@code REAL}. + */ + REAL(Types.REAL), + /** + * Identifies the generic SQL type {@code DOUBLE}. + */ + DOUBLE(Types.DOUBLE), + /** + * Identifies the generic SQL type {@code NUMERIC}. + */ + NUMERIC(Types.NUMERIC), + /** + * Identifies the generic SQL type {@code DECIMAL}. + */ + DECIMAL(Types.DECIMAL), + /** + * Identifies the generic SQL type {@code CHAR}. + */ + CHAR(Types.CHAR), + /** + * Identifies the generic SQL type {@code VARCHAR}. + */ + VARCHAR(Types.VARCHAR), + /** + * Identifies the generic SQL type {@code LONGVARCHAR}. + */ + LONGVARCHAR(Types.LONGVARCHAR), + /** + * Identifies the generic SQL type {@code DATE}. + */ + DATE(Types.DATE), + /** + * Identifies the generic SQL type {@code TIME}. + */ + TIME(Types.TIME), + /** + * Identifies the generic SQL type {@code TIMESTAMP}. + */ + TIMESTAMP(Types.TIMESTAMP), + /** + * Identifies the generic SQL type {@code BINARY}. + */ + BINARY(Types.BINARY), + /** + * Identifies the generic SQL type {@code VARBINARY}. + */ + VARBINARY(Types.VARBINARY), + /** + * Identifies the generic SQL type {@code LONGVARBINARY}. + */ + LONGVARBINARY(Types.LONGVARBINARY), + /** + * Identifies the generic SQL value {@code NULL}. + */ + NULL(Types.NULL), + /** + * Indicates that the SQL type + * is database-specific and gets mapped to a Java object that can be + * accessed via the methods getObject and setObject. + */ + OTHER(Types.OTHER), + /** + * Indicates that the SQL type + * is database-specific and gets mapped to a Java object that can be + * accessed via the methods getObject and setObject. + */ + JAVA_OBJECT(Types.JAVA_OBJECT), + /** + * Identifies the generic SQL type {@code DISTINCT}. + */ + DISTINCT(Types.DISTINCT), + /** + * Identifies the generic SQL type {@code STRUCT}. + */ + STRUCT(Types.STRUCT), + /** + * Identifies the generic SQL type {@code ARRAY}. + */ + ARRAY(Types.ARRAY), + /** + * Identifies the generic SQL type {@code BLOB}. + */ + BLOB(Types.BLOB), + /** + * Identifies the generic SQL type {@code CLOB}. + */ + CLOB(Types.CLOB), + /** + * Identifies the generic SQL type {@code REF}. + */ + REF(Types.REF), + /** + * Identifies the generic SQL type {@code DATALINK}. + */ + DATALINK(Types.DATALINK), + /** + * Identifies the generic SQL type {@code BOOLEAN}. + */ + BOOLEAN(Types.BOOLEAN), + + /* JDBC 4.0 Types */ + + /** + * Identifies the SQL type {@code ROWID}. + */ + ROWID(Types.ROWID), + /** + * Identifies the generic SQL type {@code NCHAR}. + */ + NCHAR(Types.NCHAR), + /** + * Identifies the generic SQL type {@code NVARCHAR}. + */ + NVARCHAR(Types.NVARCHAR), + /** + * Identifies the generic SQL type {@code LONGNVARCHAR}. + */ + LONGNVARCHAR(Types.LONGNVARCHAR), + /** + * Identifies the generic SQL type {@code NCLOB}. + */ + NCLOB(Types.NCLOB), + /** + * Identifies the generic SQL type {@code SQLXML}. + */ + SQLXML(Types.SQLXML), + + /* JDBC 4.2 Types */ + + /** + * Identifies the generic SQL type {@code REF_CURSOR}. + */ + REF_CURSOR(Types.REF_CURSOR); + + /** + * The Integer value for the JDBCType. It maps to a value in + * {@code Types.java} + */ + private Integer type; + + /** + * Constructor to specify the data type value from {@code Types) for + * this data type. + * @param type The value from {@code Types) for this data type + */ + JDBCType(final Integer type) { + this.type = type; + } + + /** + * Returns the name of the data type. + * @return The name of the data type. + */ + public String getName() { + return name(); + } + /** + * Returns the name of the vendor that supports this data type. + * @return The name of the vendor for this data type which is + * {@literal java.sql} for JDBCType. + */ + public String getVendor() { + return "java.sql"; + } + + /** + * Returns the vendor specific type number for the data type. + * @return An Integer representing the data type. For {@code JDBCType}, + * the value will be the same value as in {@code Types} for the data type. + */ + public Integer getVendorTypeNumber() { + return type; + } + /** + * Returns the {@code JDBCType} that corresponds to the specified + * {@code Types} value + * @param type {@code Types} value + * @return The {@code JDBCType} constant + * @throws IllegalArgumentException if this enum type has no constant with + * the specified {@code Types} value + * @see Types + */ + public static JDBCType valueOf(int type) { + for( JDBCType sqlType : JDBCType.class.getEnumConstants()) { + if(type == sqlType.type) + return sqlType; + } + throw new IllegalArgumentException("Type:" + type + " is not a valid " + + "Types.java value."); + } +} diff --git a/jdk/src/share/classes/java/sql/PreparedStatement.java b/jdk/src/share/classes/java/sql/PreparedStatement.java index b7b8211b533..22b170777cf 100644 --- a/jdk/src/share/classes/java/sql/PreparedStatement.java +++ b/jdk/src/share/classes/java/sql/PreparedStatement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -388,23 +388,20 @@ public interface PreparedStatement extends Statement { /** * Sets the value of the designated parameter with the given object. - * This method is like the method setObject - * above, except that it assumes a scale of zero. + * + * This method is similar to {@link #setObject(int parameterIndex, + * Object x, int targetSqlType, int scaleOrLength)}, + * except that it assumes a scale of zero. * * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the object containing the input parameter value * @param targetSqlType the SQL type (as defined in java.sql.Types) to be * sent to the database * @exception SQLException if parameterIndex does not correspond to a parameter - * marker in the SQL statement; if a database access error occurs or - * this method is called on a closed PreparedStatement - * @exception SQLFeatureNotSupportedException if targetSqlType is - * a ARRAY, BLOB, CLOB, - * DATALINK, JAVA_OBJECT, NCHAR, - * NCLOB, NVARCHAR, LONGNVARCHAR, - * REF, ROWID, SQLXML - * or STRUCT data type and the JDBC driver does not support - * this data type + * marker in the SQL statement; if a database access error occurs or this + * method is called on a closed PreparedStatement + * @exception SQLFeatureNotSupportedException if + * the JDBC driver does not support this data type * @see Types */ void setObject(int parameterIndex, Object x, int targetSqlType) @@ -412,8 +409,6 @@ public interface PreparedStatement extends Statement { /** *

    Sets the value of the designated parameter using the given object. - * The second parameter must be of type Object; therefore, the - * java.lang equivalent objects should be used for built-in types. * *

    The JDBC specification specifies a standard mapping from * Java Object types to SQL types. The given argument @@ -914,9 +909,7 @@ public interface PreparedStatement extends Statement { void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException; /** - *

    Sets the value of the designated parameter with the given object. The second - * argument must be an object type; for integral values, the - * java.lang equivalent objects should be used. + *

    Sets the value of the designated parameter with the given object. * * If the second argument is an InputStream then the stream must contain * the number of bytes specified by scaleOrLength. If the second argument is a @@ -957,13 +950,8 @@ public interface PreparedStatement extends Statement { * if the Java Object specified by x is an InputStream * or Reader object and the value of the scale parameter is less * than zero - * @exception SQLFeatureNotSupportedException if targetSqlType is - * a ARRAY, BLOB, CLOB, - * DATALINK, JAVA_OBJECT, NCHAR, - * NCLOB, NVARCHAR, LONGNVARCHAR, - * REF, ROWID, SQLXML - * or STRUCT data type and the JDBC driver does not support - * this data type + * @exception SQLFeatureNotSupportedException if + * the JDBC driver does not support this data type * @see Types * * @since 1.6 @@ -1220,5 +1208,114 @@ public interface PreparedStatement extends Statement { void setNClob(int parameterIndex, Reader reader) throws SQLException; + //------------------------- JDBC 4.2 ----------------------------------- + /** + *

    Sets the value of the designated parameter with the given object. + * + * If the second argument is an {@code InputStream} then the stream + * must contain the number of bytes specified by scaleOrLength. + * If the second argument is a {@code Reader} then the reader must + * contain the number of characters specified by scaleOrLength. If these + * conditions are not true the driver will generate a + * {@code SQLException} when the prepared statement is executed. + * + *

    The given Java object will be converted to the given targetSqlType + * before being sent to the database. + * + * If the object has a custom mapping (is of a class implementing the + * interface {@code SQLData}), + * the JDBC driver should call the method {@code SQLData.writeSQL} to + * write it to the SQL data stream. + * If, on the other hand, the object is of a class implementing + * {@code Ref}, {@code Blob}, {@code Clob}, {@code NClob}, + * {@code Struct}, {@code java.net.URL}, + * or {@code Array}, the driver should pass it to the database as a + * value of the corresponding SQL type. + * + *

    Note that this method may be used to pass database-specific + * abstract data types. + *

    + * The default implementation will throw {@code SQLFeatureNotSupportedException} + * + * @param parameterIndex the first parameter is 1, the second is 2, ... + * @param x the object containing the input parameter value + * @param targetSqlType the SQL type to be sent to the database. The + * scale argument may further qualify this type. + * @param scaleOrLength for {@code java.sql.JDBCType.DECIMAL} + * or {@code java.sql.JDBCType.NUMERIC types}, + * this is the number of digits after the decimal point. For + * Java Object types {@code InputStream} and {@code Reader}, + * this is the length + * of the data in the stream or reader. For all other types, + * this value will be ignored. + * @exception SQLException if parameterIndex does not correspond to a + * parameter marker in the SQL statement; if a database access error occurs + * or this method is called on a closed {@code PreparedStatement} or + * if the Java Object specified by x is an InputStream + * or Reader object and the value of the scale parameter is less + * than zero + * @exception SQLFeatureNotSupportedException if + * the JDBC driver does not support this data type + * @see JDBCType + * @see SQLType + * @since 1.8 + */ + default void setObject(int parameterIndex, Object x, SQLType targetSqlType, + int scaleOrLength) throws SQLException { + throw new SQLFeatureNotSupportedException("setObject not implemented"); + } + + /** + * Sets the value of the designated parameter with the given object. + * + * This method is similar to {@link #setObject(int parameterIndex, + * Object x, SQLType targetSqlType, int scaleOrLength)}, + * except that it assumes a scale of zero. + *

    + * The default implementation will throw {@code SQLFeatureNotSupportedException} + * + * @param parameterIndex the first parameter is 1, the second is 2, ... + * @param x the object containing the input parameter value + * @param targetSqlType the SQL type to be sent to the database + * @exception SQLException if parameterIndex does not correspond to a + * parameter marker in the SQL statement; if a database access error occurs + * or this method is called on a closed {@code PreparedStatement} + * @exception SQLFeatureNotSupportedException if + * the JDBC driver does not support this data type + * @see JDBCType + * @see SQLType + * @since 1.8 + */ + default void setObject(int parameterIndex, Object x, SQLType targetSqlType) + throws SQLException { + throw new SQLFeatureNotSupportedException("setObject not implemented"); + } + + /** + * Executes the SQL statement in this PreparedStatement object, + * which must be an SQL Data Manipulation Language (DML) statement, + * such as INSERT, UPDATE or + * DELETE; or an SQL statement that returns nothing, + * such as a DDL statement. + *

    + * This method should be used when the returned row count may exceed + * {@link Integer.MAX_VALUE}. + *

    + * The default implementation will throw {@code UnsupportedOperationException} + * + * @return either (1) the row count for SQL Data Manipulation Language + * (DML) statements or (2) 0 for SQL statements that return nothing + * @exception SQLException if a database access error occurs; + * this method is called on a closed PreparedStatement + * or the SQL statement returns a ResultSet object + * @throws SQLTimeoutException when the driver has determined that the + * timeout value that was specified by the {@code setQueryTimeout} + * method has been exceeded and has at least attempted to cancel + * the currently running {@code Statement} + * @since 1.8 + */ + default long executeLargeUpdate() throws SQLException { + throw new UnsupportedOperationException("executeLargeUpdate not implemented"); + } } diff --git a/jdk/src/share/classes/java/sql/ResultSet.java b/jdk/src/share/classes/java/sql/ResultSet.java index c4beabac5b1..ab547733128 100644 --- a/jdk/src/share/classes/java/sql/ResultSet.java +++ b/jdk/src/share/classes/java/sql/ResultSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1834,6 +1834,7 @@ public interface ResultSet extends Wrapper, AutoCloseable { /** * Updates the designated column with an Object value. + * * The updater methods are used to update column values in the * current row or the insert row. The updater methods do not * update the underlying database; instead the updateRow or @@ -1866,6 +1867,7 @@ public interface ResultSet extends Wrapper, AutoCloseable { /** * Updates the designated column with an Object value. + * * The updater methods are used to update column values in the * current row or the insert row. The updater methods do not * update the underlying database; instead the updateRow or @@ -2224,6 +2226,7 @@ public interface ResultSet extends Wrapper, AutoCloseable { /** * Updates the designated column with an Object value. + * * The updater methods are used to update column values in the * current row or the insert row. The updater methods do not * update the underlying database; instead the updateRow or @@ -2256,6 +2259,7 @@ public interface ResultSet extends Wrapper, AutoCloseable { /** * Updates the designated column with an Object value. + * * The updater methods are used to update column values in the * current row or the insert row. The updater methods do not * update the underlying database; instead the updateRow or @@ -4142,4 +4146,145 @@ public interface ResultSet extends Wrapper, AutoCloseable { */ public T getObject(String columnLabel, Class type) throws SQLException; + //------------------------- JDBC 4.2 ----------------------------------- + + /** + * Updates the designated column with an {@code Object} value. + * + * The updater methods are used to update column values in the + * current row or the insert row. The updater methods do not + * update the underlying database; instead the {@code updateRow} or + * {@code insertRow} methods are called to update the database. + *

    + * If the second argument is an {@code InputStream} then the stream must contain + * the number of bytes specified by scaleOrLength. If the second argument is a + * {@code Reader} then the reader must contain the number of characters specified + * by scaleOrLength. If these conditions are not true the driver will generate a + * {@code SQLException} when the statement is executed. + *

    + * The default implementation will throw {@code SQLFeatureNotSupportedException} + * + * @param columnIndex the first column is 1, the second is 2, ... + * @param x the new column value + * @param targetSqlType the SQL type to be sent to the database + * @param scaleOrLength for an object of {@code java.math.BigDecimal} , + * this is the number of digits after the decimal point. For + * Java Object types {@code InputStream} and {@code Reader}, + * this is the length + * of the data in the stream or reader. For all other types, + * this value will be ignored. + * @exception SQLException if the columnIndex is not valid; + * if a database access error occurs; + * the result set concurrency is {@code CONCUR_READ_ONLY} + * or this method is called on a closed result set + * @exception SQLFeatureNotSupportedException if the JDBC driver does not + * support this method; if the JDBC driver does not support this data type + * @see JDBCType + * @see SQLType + * @since 1.8 + */ + default void updateObject(int columnIndex, Object x, + SQLType targetSqlType, int scaleOrLength) throws SQLException { + throw new SQLFeatureNotSupportedException("updateObject not implemented"); + } + + /** + * Updates the designated column with an {@code Object} value. + * + * The updater methods are used to update column values in the + * current row or the insert row. The updater methods do not + * update the underlying database; instead the {@code updateRow} or + * {@code insertRow} methods are called to update the database. + *

    + * If the second argument is an {@code InputStream} then the stream must + * contain number of bytes specified by scaleOrLength. If the second + * argument is a {@code Reader} then the reader must contain the number + * of characters specified by scaleOrLength. If these conditions are not + * true the driver will generate a + * {@code SQLException} when the statement is executed. + *

    + * The default implementation will throw {@code SQLFeatureNotSupportedException} + * + * @param columnLabel the label for the column specified with the SQL AS + * clause. If the SQL AS clause was not specified, then the label is + * the name of the column + * @param targetSqlType the SQL type to be sent to the database + * @param scaleOrLength for an object of {@code java.math.BigDecimal} , + * this is the number of digits after the decimal point. For + * Java Object types {@code InputStream} and {@code Reader}, + * this is the length + * of the data in the stream or reader. For all other types, + * this value will be ignored. + * @exception SQLException if the columnLabel is not valid; + * if a database access error occurs; + * the result set concurrency is {@code CONCUR_READ_ONLY} + * or this method is called on a closed result set + * @exception SQLFeatureNotSupportedException if the JDBC driver does not + * support this method; if the JDBC driver does not support this data type + * @see JDBCType + * @see SQLType + * @since 1.8 + */ + default void updateObject(String columnLabel, Object x, + SQLType targetSqlType, int scaleOrLength) throws SQLException { + throw new SQLFeatureNotSupportedException("updateObject not implemented"); + } + + /** + * Updates the designated column with an {@code Object} value. + * + * The updater methods are used to update column values in the + * current row or the insert row. The updater methods do not + * update the underlying database; instead the {@code updateRow} or + * {@code insertRow} methods are called to update the database. + *

    + * The default implementation will throw {@code SQLFeatureNotSupportedException} + * + * @param columnIndex the first column is 1, the second is 2, ... + * @param x the new column value + * @param targetSqlType the SQL type to be sent to the database + * @exception SQLException if the columnIndex is not valid; + * if a database access error occurs; + * the result set concurrency is {@code CONCUR_READ_ONLY} + * or this method is called on a closed result set + * @exception SQLFeatureNotSupportedException if the JDBC driver does not + * support this method; if the JDBC driver does not support this data type + * @see JDBCType + * @see SQLType + * @since 1.8 + */ + default void updateObject(int columnIndex, Object x, SQLType targetSqlType) + throws SQLException { + throw new SQLFeatureNotSupportedException("updateObject not implemented"); + } + + /** + * Updates the designated column with an {@code Object} value. + * + * The updater methods are used to update column values in the + * current row or the insert row. The updater methods do not + * update the underlying database; instead the {@code updateRow} or + * {@code insertRow} methods are called to update the database. + *

    + * The default implementation will throw {@code SQLFeatureNotSupportedException} + * + * @param columnLabel the label for the column specified with the SQL AS + * clause. If the SQL AS clause was not specified, then the label is + * the name of the column + * @param x the new column value + * @param targetSqlType the SQL type to be sent to the database + * @exception SQLException if the columnLabel is not valid; + * if a database access error occurs; + * the result set concurrency is {@code CONCUR_READ_ONLY} + * or this method is called on a closed result set + * @exception SQLFeatureNotSupportedException if the JDBC driver does not + * support this method; if the JDBC driver does not support this data type + * @see JDBCType + * @see SQLType + * @since 1.8 + */ + default void updateObject(String columnLabel, Object x, + SQLType targetSqlType) throws SQLException { + throw new SQLFeatureNotSupportedException("updateObject not implemented"); + } } diff --git a/jdk/src/share/classes/java/sql/SQLTimeoutException.java b/jdk/src/share/classes/java/sql/SQLTimeoutException.java index 57abd3be184..a55ee05a74d 100644 --- a/jdk/src/share/classes/java/sql/SQLTimeoutException.java +++ b/jdk/src/share/classes/java/sql/SQLTimeoutException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,8 +26,10 @@ package java.sql; /** - *

    The subclass of {@link SQLException} thrown when the timeout specified by Statement - * has expired. + *

    The subclass of {@link SQLException} thrown when the timeout specified by + * {@code Statement.setQueryTimeout}, {@code DriverManager.setLoginTimeout}, + * {@code DataSource.setLoginTimeout},{@code XADataSource.setLoginTimeout} + * has expired. *

    This exception does not correspond to a standard SQLState. * * @since 1.6 diff --git a/jdk/src/share/classes/java/sql/SQLType.java b/jdk/src/share/classes/java/sql/SQLType.java new file mode 100644 index 00000000000..c0a2b6a6c5f --- /dev/null +++ b/jdk/src/share/classes/java/sql/SQLType.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.sql; + +/** + * An object that is used to identify a generic SQL type, called a JDBC type or + * a vendor specific data type. + * + * @since 1.8 + */ +public interface SQLType { + + /** + * Returns the {@code SQLType} name that represents a SQL data type. + * + * @return The name of this {@code SQLType}. + */ + String getName(); + + /** + * Returns the name of the vendor that supports this data type. The value + * returned typically is the package name for this vendor. + * + * @return The name of the vendor for this data type + */ + String getVendor(); + + /** + * Returns the vendor specific type number for the data type. + * + * @return An Integer representing the vendor specific data type + */ + Integer getVendorTypeNumber(); +} diff --git a/jdk/src/share/classes/java/sql/Statement.java b/jdk/src/share/classes/java/sql/Statement.java index d249a2f46aa..584f17c0a17 100644 --- a/jdk/src/share/classes/java/sql/Statement.java +++ b/jdk/src/share/classes/java/sql/Statement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -183,7 +183,15 @@ public interface Statement extends Wrapper, AutoCloseable { * Sets escape processing on or off. * If escape scanning is on (the default), the driver will do * escape substitution before sending the SQL statement to the database. - * + *

    + * The {@code Connection} and {@code DataSource} property + * {@code escapeProcessing} may be used to change the default escape processing + * behavior. A value of true (the default) enables escape Processing for + * all {@code Statement} objects. A value of false disables escape processing + * for all {@code Statement} objects. The {@code setEscapeProcessing} + * method may be used to specify the escape processing behavior for an + * individual {@code Statement} object. + *

    * Note: Since prepared statements have usually been parsed prior * to making this call, disabling escape processing for * PreparedStatements objects will have no effect. @@ -1060,4 +1068,304 @@ public interface Statement extends Wrapper, AutoCloseable { */ public boolean isCloseOnCompletion() throws SQLException; + + //--------------------------JDBC 4.2 ----------------------------- + + /** + * Retrieves the current result as an update count; if the result + * is a ResultSet object or there are no more results, -1 + * is returned. This method should be called only once per result. + *

    + * This method should be used when the returned row count may exceed + * {@link Integer.MAX_VALUE}. + *

    + * The default implementation will throw {@code UnsupportedOperationException} + * + * @return the current result as an update count; -1 if the current result + * is a ResultSet object or there are no more results + * @exception SQLException if a database access error occurs or + * this method is called on a closed Statement + * @see #execute + * @since 1.8 + */ + default long getLargeUpdateCount() throws SQLException { + throw new UnsupportedOperationException("getLargeUpdateCount not implemented"); + } + + /** + * Sets the limit for the maximum number of rows that any + * ResultSet object generated by this Statement + * object can contain to the given number. + * If the limit is exceeded, the excess + * rows are silently dropped. + *

    + * This method should be used when the row limit may exceed + * {@link Integer.MAX_VALUE}. + *

    + * The default implementation will throw {@code UnsupportedOperationException} + * + * @param max the new max rows limit; zero means there is no limit + * @exception SQLException if a database access error occurs, + * this method is called on a closed Statement + * or the condition max >= 0 is not satisfied + * @see #getMaxRows + * @since 1.8 + */ + default void setLargeMaxRows(long max) throws SQLException { + throw new UnsupportedOperationException("setLargeMaxRows not implemented"); + } + + /** + * Retrieves the maximum number of rows that a + * ResultSet object produced by this + * Statement object can contain. If this limit is exceeded, + * the excess rows are silently dropped. + *

    + * This method should be used when the returned row limit may exceed + * {@link Integer.MAX_VALUE}. + *

    + * The default implementation will return {@code 0} + * + * @return the current maximum number of rows for a ResultSet + * object produced by this Statement object; + * zero means there is no limit + * @exception SQLException if a database access error occurs or + * this method is called on a closed Statement + * @see #setMaxRows + * @since 1.8 + */ + default long getLargeMaxRows() throws SQLException { + return 0; + } + + /** + * Submits a batch of commands to the database for execution and + * if all commands execute successfully, returns an array of update counts. + * The long elements of the array that is returned are ordered + * to correspond to the commands in the batch, which are ordered + * according to the order in which they were added to the batch. + * The elements in the array returned by the method {@code executeLargeBatch} + * may be one of the following: + *

      + *
    1. A number greater than or equal to zero -- indicates that the + * command was processed successfully and is an update count giving the + * number of rows in the database that were affected by the command's + * execution + *
    2. A value of SUCCESS_NO_INFO -- indicates that the command was + * processed successfully but that the number of rows affected is + * unknown + *

      + * If one of the commands in a batch update fails to execute properly, + * this method throws a BatchUpdateException, and a JDBC + * driver may or may not continue to process the remaining commands in + * the batch. However, the driver's behavior must be consistent with a + * particular DBMS, either always continuing to process commands or never + * continuing to process commands. If the driver continues processing + * after a failure, the array returned by the method + * BatchUpdateException.getLargeUpdateCounts + * will contain as many elements as there are commands in the batch, and + * at least one of the elements will be the following: + *

      + *

    3. A value of EXECUTE_FAILED -- indicates that the command failed + * to execute successfully and occurs only if a driver continues to + * process commands after a command fails + *
    + *

    + * This method should be used when the returned row count may exceed + * {@link Integer.MAX_VALUE}. + *

    + * The default implementation will throw {@code UnsupportedOperationException} + * + * @return an array of update counts containing one element for each + * command in the batch. The elements of the array are ordered according + * to the order in which commands were added to the batch. + * @exception SQLException if a database access error occurs, + * this method is called on a closed Statement or the + * driver does not support batch statements. Throws {@link BatchUpdateException} + * (a subclass of SQLException) if one of the commands sent to the + * database fails to execute properly or attempts to return a result set. + * @throws SQLTimeoutException when the driver has determined that the + * timeout value that was specified by the {@code setQueryTimeout} + * method has been exceeded and has at least attempted to cancel + * the currently running {@code Statement} + * + * @see #addBatch + * @see DatabaseMetaData#supportsBatchUpdates + * @since 1.8 + */ + default long[] executeLargeBatch() throws SQLException { + throw new UnsupportedOperationException("executeLargeBatch not implemented"); + } + + /** + * Executes the given SQL statement, which may be an INSERT, + * UPDATE, or DELETE statement or an + * SQL statement that returns nothing, such as an SQL DDL statement. + *

    + * This method should be used when the returned row count may exceed + * {@link Integer.MAX_VALUE}. + *

    + * Note:This method cannot be called on a + * PreparedStatement or CallableStatement. + *

    + * The default implementation will throw {@code UnsupportedOperationException} + * + * @param sql an SQL Data Manipulation Language (DML) statement, + * such as INSERT, UPDATE or + * DELETE; or an SQL statement that returns nothing, + * such as a DDL statement. + * + * @return either (1) the row count for SQL Data Manipulation Language + * (DML) statements or (2) 0 for SQL statements that return nothing + * + * @exception SQLException if a database access error occurs, + * this method is called on a closed Statement, the given + * SQL statement produces a ResultSet object, the method is called on a + * PreparedStatement or CallableStatement + * @throws SQLTimeoutException when the driver has determined that the + * timeout value that was specified by the {@code setQueryTimeout} + * method has been exceeded and has at least attempted to cancel + * the currently running {@code Statement} + * @since 1.8 + */ + default long executeLargeUpdate(String sql) throws SQLException { + throw new UnsupportedOperationException("executeLargeUpdate not implemented"); + } + + /** + * Executes the given SQL statement and signals the driver with the + * given flag about whether the + * auto-generated keys produced by this Statement object + * should be made available for retrieval. The driver will ignore the + * flag if the SQL statement + * is not an INSERT statement, or an SQL statement able to return + * auto-generated keys (the list of such statements is vendor-specific). + *

    + * This method should be used when the returned row count may exceed + * {@link Integer.MAX_VALUE}. + *

    + * Note:This method cannot be called on a + * PreparedStatement or CallableStatement. + *

    + * The default implementation will throw {@code SQLFeatureNotSupportedException} + * + * @param sql an SQL Data Manipulation Language (DML) statement, + * such as INSERT, UPDATE or + * DELETE; or an SQL statement that returns nothing, + * such as a DDL statement. + * + * @param autoGeneratedKeys a flag indicating whether auto-generated keys + * should be made available for retrieval; + * one of the following constants: + * Statement.RETURN_GENERATED_KEYS + * Statement.NO_GENERATED_KEYS + * @return either (1) the row count for SQL Data Manipulation Language (DML) statements + * or (2) 0 for SQL statements that return nothing + * + * @exception SQLException if a database access error occurs, + * this method is called on a closed Statement, the given + * SQL statement returns a ResultSet object, + * the given constant is not one of those allowed, the method is called on a + * PreparedStatement or CallableStatement + * @exception SQLFeatureNotSupportedException if the JDBC driver does not support + * this method with a constant of Statement.RETURN_GENERATED_KEYS + * @throws SQLTimeoutException when the driver has determined that the + * timeout value that was specified by the {@code setQueryTimeout} + * method has been exceeded and has at least attempted to cancel + * the currently running {@code Statement} + * @since 1.8 + */ + default long executeLargeUpdate(String sql, int autoGeneratedKeys) + throws SQLException { + throw new SQLFeatureNotSupportedException("executeLargeUpdate not implemented"); + } + + /** + * Executes the given SQL statement and signals the driver that the + * auto-generated keys indicated in the given array should be made available + * for retrieval. This array contains the indexes of the columns in the + * target table that contain the auto-generated keys that should be made + * available. The driver will ignore the array if the SQL statement + * is not an INSERT statement, or an SQL statement able to return + * auto-generated keys (the list of such statements is vendor-specific). + *

    + * This method should be used when the returned row count may exceed + * {@link Integer.MAX_VALUE}. + *

    + * Note:This method cannot be called on a + * PreparedStatement or CallableStatement. + *

    + * The default implementation will throw {@code SQLFeatureNotSupportedException} + * + * @param sql an SQL Data Manipulation Language (DML) statement, + * such as INSERT, UPDATE or + * DELETE; or an SQL statement that returns nothing, + * such as a DDL statement. + * + * @param columnIndexes an array of column indexes indicating the columns + * that should be returned from the inserted row + * @return either (1) the row count for SQL Data Manipulation Language (DML) statements + * or (2) 0 for SQL statements that return nothing + * + * @exception SQLException if a database access error occurs, + * this method is called on a closed Statement, the SQL + * statement returns a ResultSet object,the second argument + * supplied to this method is not an + * int array whose elements are valid column indexes, the method is called on a + * PreparedStatement or CallableStatement + * @throws SQLFeatureNotSupportedException if the JDBC driver does not support this method + * @throws SQLTimeoutException when the driver has determined that the + * timeout value that was specified by the {@code setQueryTimeout} + * method has been exceeded and has at least attempted to cancel + * the currently running {@code Statement} + * @since 1.8 + */ + default long executeLargeUpdate(String sql, int columnIndexes[]) throws SQLException { + throw new SQLFeatureNotSupportedException("executeLargeUpdate not implemented"); + } + + /** + * Executes the given SQL statement and signals the driver that the + * auto-generated keys indicated in the given array should be made available + * for retrieval. This array contains the names of the columns in the + * target table that contain the auto-generated keys that should be made + * available. The driver will ignore the array if the SQL statement + * is not an INSERT statement, or an SQL statement able to return + * auto-generated keys (the list of such statements is vendor-specific). + *

    + * This method should be used when the returned row count may exceed + * {@link Integer.MAX_VALUE}. + *

    + * Note:This method cannot be called on a + * PreparedStatement or CallableStatement. + *

    + * The default implementation will throw {@code SQLFeatureNotSupportedException} + * + * @param sql an SQL Data Manipulation Language (DML) statement, + * such as INSERT, UPDATE or + * DELETE; or an SQL statement that returns nothing, + * such as a DDL statement. + * @param columnNames an array of the names of the columns that should be + * returned from the inserted row + * @return either the row count for INSERT, UPDATE, + * or DELETE statements, or 0 for SQL statements + * that return nothing + * @exception SQLException if a database access error occurs, + * this method is called on a closed Statement, the SQL + * statement returns a ResultSet object, the + * second argument supplied to this method is not a String array + * whose elements are valid column names, the method is called on a + * PreparedStatement or CallableStatement + * @throws SQLFeatureNotSupportedException if the JDBC driver does not support this method + * @throws SQLTimeoutException when the driver has determined that the + * timeout value that was specified by the {@code setQueryTimeout} + * method has been exceeded and has at least attempted to cancel + * the currently running {@code Statement} + * @since 1.8 + */ + default long executeLargeUpdate(String sql, String columnNames[]) + throws SQLException { + throw new SQLFeatureNotSupportedException("executeLargeUpdate not implemented"); + } } + diff --git a/jdk/src/share/classes/java/sql/Types.java b/jdk/src/share/classes/java/sql/Types.java index 5b800a926f7..d6fc80a3232 100644 --- a/jdk/src/share/classes/java/sql/Types.java +++ b/jdk/src/share/classes/java/sql/Types.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -309,6 +309,16 @@ public class Types { */ public static final int SQLXML = 2009; + //--------------------------JDBC 4.2 ----------------------------- + + /** + * The constant in the Java programming language, sometimes referred to + * as a type code, that identifies the generic SQL type {@code REF CURSOR}. + * + * @since 1.8 + */ + public static final int REF_CURSOR = 2012; + // Prevent instantiation private Types() {} } diff --git a/jdk/src/share/classes/java/sql/package.html b/jdk/src/share/classes/java/sql/package.html index 65d906b9a69..d6c97126a2a 100644 --- a/jdk/src/share/classes/java/sql/package.html +++ b/jdk/src/share/classes/java/sql/package.html @@ -2,7 +2,7 @@ + +

    + A new Date and Time API for Java. + The design includes a relatively large number of classes and methods, + however each follows a common design language, especially in method prefixes. + Once the prefixes are understood, the API is relatively simple to comprehend. +

    +

    + The Java Time API is composed of several packages, each with a primary function: +

    +

    + {@link java.time} contains the main API based on the ISO-8601 standard. + The classes defined here represent the principal date-time concepts, + including instants, durations, dates, times, time-zones and periods. + They are based on the ISO calendar system, which is the de facto world + calendar following the proleptic Gregorian rules. + All the classes are immutable and thread-safe. +

    +

    + {@link java.time.temporal} contains the API for accessing the fields and units + of date-time. Units are measurable, such as years, months and hours. + For example, the expression "2 hours later" uses the hours unit. + By contrast, fields are mini-calculations, defining a value. + For example, month-of-year, day-of-week and hour-of-day are all fields. + The set of supported units and fields can be extended by applications if desired. +

    +

    + It also contains the basic part of the calendar neutral API. + This is intended for use by applications that need to use localized calendars. + Ensure that you read the class documentation of {@link java.time.temporal.ChronoLocalDate} + before using non-ISO calendar systems. +

    +

    + {@link java.time.format} contains the API to print and parse fields into date-time + objects and to customize parsing and printing. + Formatters can be created in a variety of ways, including constants, patterns, + localized styles and a builder. + Formatters are immutable and thread-safe. +

    +

    + {@link java.time.zone} contains the API to handle time-zones. + Detailed information is made available about the rules of each time-zone. +

    +

    + The {@link java.time.calendar} package contains alternate calendar systems. + This is intended for use by applications that need to use localized calendars. + Support is provided for the Hijrah, Japanese, Minguo, and Thai Buddhist Calendars. +

    +

    Design notes

    +

    + Where possible, the API avoids the use of null. + All methods define whether they accept or return null in the Javadoc. + As a general rule, methods do not accept or return null. + A key exception is any method that takes an object and returns a boolean, for the purpose + of checking or validating, will generally return false for null. +

    +

    + The API is designed to be type-safe where reasonable in the main high-level API. + Thus, there are separate classes for the distinct concepts of date, time and date-time, plus variants + for offset and time-zones. The core 7 date-time classes, plus Instant, handle the needs of most applications. + Further classes handle other combinations - year, year-month and month-day in a type-safe manner. +

    +

    + In a language like Java, the use of many different types tends to cause API bloat. + This is handled here through the use of common method naming patterns throughout the API. + The common prefixes are 'of', 'get', 'is', 'with', 'plus', 'minus', 'to' and 'at'. + See {@link java.time.LocalDate} for an example of each of these methods. +

    +

    + Following type-safety to its logical conclusion would result in more classes, especially for time - + hour-minute, hour-minute-second and hour-minute-second-nanosecond. + While logically pure, this was not possible in practice, as the additional classes would have + excessively complicated the API. Notably, there would be additional combinations at the offset + and date-time levels, such as offset-date-hour-minute. + To avoid this explosion of types, {@link java.time.LocalTime} is used for all precisions of time. + By contrast, some additional classes were used for dates, such as {@link java.time.temporal.YearMonth}. + This proved necessary, as the API for year-month is significantly different to that for a date, whereas + an absence of nanoseconds in a time can be approximated by returning zero. +

    +

    + Similarly, full type-safety might argue for a separate class for each field in date-time, + such as a class for HourOfDay and another for DayOfMonth. + This approach was tried, but was excessively complicated in the Java language, lacking usability. + A similar problem occurs with periods. + There is a case for a separate class for each period unit, such as a type for Years and a type for Minutes. + However, this yields a lot of classes and a problem of type conversion. + As such, general access to fields and units is not wrapped in a class. +

    +

    + Multiple calendar systems is an awkward addition to the design challenges. + The first principal is that most users want the standard ISO calendar system. + As such, the main classes are ISO-only. The second principal is that most of those that want a + non-ISO calendar system want it for user interaction, thus it is a UI localization issue. + As such, date and time objects should be held as ISO objects in the data model and persistent + storage, only being converted to and from a local calendar for display. + The calendar system would be stored separately in the user preferences. +

    +

    + There are, however, some limited use cases where users believe they need to store and use + dates in arbitrary calendar systems throughout the application. + This is supported by {@link java.time.temporal.ChronoLocalDate}, however it is vital to read + all the associated warnings in the Javadoc of that interface before using it. + In summary, applications that require general interoperation between multiple calendar systems + typically need to be written in a very different way to those only using the ISO calendar, + thus most applications should just use ISO and avoid {@code ChronoLocalDate}. +

    +

    + Throughout all of this, a key goal was to allow date-time fields and units to be defined by applications. + This has been achieved having tried many different designs. +

    + diff --git a/jdk/src/share/classes/java/time/package-info.java b/jdk/src/share/classes/java/time/package-info.java new file mode 100644 index 00000000000..9d06ff9352c --- /dev/null +++ b/jdk/src/share/classes/java/time/package-info.java @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + *

    + * The main API for dates, times, instants, and durations. + *

    + *

    + * The classes defined here represent the principal date-time concepts, + * including instants, durations, dates, times, time-zones and periods. + * They are based on the ISO calendar system, which is the de facto world + * calendar following the proleptic Gregorian rules. + * All the classes are immutable and thread-safe. + *

    + *

    + * Each date time instance is composed of fields that are conveniently + * made available by the APIs. For lower level access to the fields refer + * to the {@code java.time.temporal} package. + * Each class includes support for printing and parsing all manner of dates and times. + * Refer to the {@code java.time.format} package for customization options. + *

    + *

    + * The {@code java.time.temporal} package also contains the calendar neutral API + * {@link java.time.temporal.ChronoLocalDate ChronoLocalDate}, + * {@link java.time.temporal.ChronoLocalDateTime ChronoLocalDateTime}, + * {@link java.time.temporal.ChronoZonedDateTime ChronoZonedDateTime} and + * {@link java.time.temporal.Era Era}. + * This is intended for use by applications that need to use localized calendars. + * It is recommended that applications use the ISO-8601 dates and time classes from + * this package across system boundaries, such as to the database or across the network. + * The calendar neutral API should be reserved for interactions with users. + *

    + * + *

    Dates and Times

    + *

    + * {@link java.time.Instant} is essentially a numeric timestamp. + * The current Instant can be retrieved from a {@link java.time.Clock}. + * This is useful for logging and persistence of a point in time + * and has in the past been associated with storing the result + * from {@link java.lang.System#currentTimeMillis()}. + *

    + *

    + * {@link java.time.LocalDate} stores a date without a time. + * This stores a date like '2010-12-03' and could be used to store a birthday. + *

    + *

    + * {@link java.time.LocalTime} stores a time without a date. + * This stores a time like '11:30' and could be used to store an opening or closing time. + *

    + *

    + * {@link java.time.LocalDateTime} stores a date and time. + * This stores a date-time like '2010-12-03T11:30'. + *

    + *

    + * {@link java.time.ZonedDateTime} stores a date and time with a time-zone. + * This is useful if you want to perform accurate calculations of + * dates and times taking into account the {@link java.time.ZoneId}, such as 'Europe/Paris'. + * Where possible, it is recommended to use a simpler class without a time-zone. + * The widespread use of time-zones tends to add considerable complexity to an application. + *

    + * + *

    Duration and Period

    + *

    + * Beyond dates and times, the API also allows the storage of period and durations of time. + * A {@link java.time.Duration} is a simple measure of time along the time-line in nanoseconds. + * A {@link java.time.Period} expresses an amount of time in units meaningful to humans, such as years or hours. + *

    + * + *

    Additional value types

    + *

    + * {@link java.time.Month} stores a month on its own. + * This stores a single month-of-year in isolation, such as 'DECEMBER'. + *

    + *

    + * {@link java.time.DayOfWeek} stores a day-of-week on its own. + * This stores a single day-of-week in isolation, such as 'TUESDAY'. + *

    + *

    + * {@link java.time.temporal.Year} stores a year on its own. + * This stores a single year in isolation, such as '2010'. + *

    + *

    + * {@link java.time.temporal.YearMonth} stores a year and month without a day or time. + * This stores a year and month, such as '2010-12' and could be used for a credit card expiry. + *

    + *

    + * {@link java.time.temporal.MonthDay} stores a month and day without a year or time. + * This stores a month and day-of-month, such as '--12-03' and + * could be used to store an annual event like a birthday without storing the year. + *

    + *

    + * {@link java.time.temporal.OffsetTime} stores a time and offset from UTC without a date. + * This stores a date like '11:30+01:00'. + * The {@link java.time.ZoneOffset ZoneOffset} is of the form '+01:00'. + *

    + *

    + * {@link java.time.temporal.OffsetDate} stores a date and offset from UTC without a time. + * This stores a time like '2010-12-03+01:00'. + *

    + *

    + * {@link java.time.temporal.OffsetDateTime} stores a date and time and offset from UTC. + * This stores a date-time like '2010-12-03T11:30+01:00'. + * This is sometimes found in XML messages and other forms of persistence, + * but contains less information than a full time-zone. + *

    + * + *

    Package specification

    + *

    + * Unless otherwise noted, passing a null argument to a constructor or method in any class or interface + * in this package will cause a {@link java.lang.NullPointerException NullPointerException} to be thrown. + * The Javadoc "@param" definition is used to summarise the null-behavior. + * The "@throws {@link java.lang.NullPointerException}" is not explicitly documented in each method. + *

    + *

    + * All calculations should check for numeric overflow and throw either an {@link java.lang.ArithmeticException} + * or a {@link java.time.DateTimeException}. + *

    + * + *

    Design notes (non normative)

    + *

    + * The API has been designed to reject null early and to be clear about this behavior. + * A key exception is any method that takes an object and returns a boolean, for the purpose + * of checking or validating, will generally return false for null. + *

    + *

    + * The API is designed to be type-safe where reasonable in the main high-level API. + * Thus, there are separate classes for the distinct concepts of date, time and date-time, + * plus variants for offset and time-zone. + * This can seem like a lot of classes, but most applications can begin with just five date/time types. + *

      + *
    • {@link java.time.Instant} - a timestamp
    • + *
    • {@link java.time.LocalDate} - a date without a time, or any reference to an offset or time-zone
    • + *
    • {@link java.time.LocalTime} - a time without a date, or any reference to an offset or time-zone
    • + *
    • {@link java.time.LocalDateTime} - combines date and time, but still without any offset or time-zone
    • + *
    • {@link java.time.ZonedDateTime} - a "full" date-time with time-zone and resolved offset from UTC/Greenwich
    • + *
    + *

    + * {@code Instant} is the closest equivalent class to {@code java.util.Date}. + * {@code ZonedDateTime} is the closest equivalent class to {@code java.util.GregorianCalendar}. + *

    + *

    + * Where possible, applications should use {@code LocalDate}, {@code LocalTime} and {@code LocalDateTime} + * to better model the domain. For example, a birthday should be stored in a code {@code LocalDate}. + * Bear in mind that any use of a {@linkplain java.time.ZoneId time-zone}, such as 'Europe/Paris', adds + * considerable complexity to a calculation. + * Many applications can be written only using {@code LocalDate}, {@code LocalTime} and {@code Instant}, + * with the time-zone added at the user interface (UI) layer. + *

    + *

    + * The offset-based date-time types, {@code OffsetDate}, {@code OffsetTime} and {@code OffsetDateTime}, + * are intended primarily for use with network protocols and database access. + * For example, most databases cannot automatically store a time-zone like 'Europe/Paris', but + * they can store an offset like '+02:00'. + *

    + *

    + * Classes are also provided for the most important sub-parts of a date, including {@code Month}, + * {@code DayOfWeek}, {@code Year}, {@code YearMonth} and {@code MonthDay}. + * These can be used to model more complex date-time concepts. + * For example, {@code YearMonth} is useful for representing a credit card expiry. + *

    + *

    + * Note that while there are a large number of classes representing different aspects of dates, + * there are relatively few dealing with different aspects of time. + * Following type-safety to its logical conclusion would have resulted in classes for + * hour-minute, hour-minute-second and hour-minute-second-nanosecond. + * While logically pure, this was not a practical option as it would have almost tripled the + * number of classes due to the combinations of date and time. + * Thus, {@code LocalTime} is used for all precisions of time, with zeroes used to imply lower precision. + *

    + *

    + * Following full type-safety to its ultimate conclusion might also argue for a separate class + * for each field in date-time, such as a class for HourOfDay and another for DayOfMonth. + * This approach was tried, but was excessively complicated in the Java language, lacking usability. + * A similar problem occurs with periods. + * There is a case for a separate class for each period unit, such as a type for Years and a type for Minutes. + * However, this yields a lot of classes and a problem of type conversion. + * Thus, the set of date-time types provided is a compromise between purity and practicality. + *

    + *

    + * The API has a relatively large surface area in terms of number of methods. + * This is made manageable through the use of consistent method prefixes. + *

      + *
    • {@code of} - static factory method
    • + *
    • {@code parse} - static factory method focussed on parsing
    • + *
    • {@code get} - gets the value of something
    • + *
    • {@code is} - checks if something is true
    • + *
    • {@code with} - the immutable equivalent of a setter
    • + *
    • {@code plus} - adds an amount to an object
    • + *
    • {@code minus} - subtracts an amount from an object
    • + *
    • {@code to} - converts this object to another type
    • + *
    • {@code at} - combines this object with another, such as {@code date.atTime(time)}
    • + *
    + *

    + * Multiple calendar systems is an awkward addition to the design challenges. + * The first principal is that most users want the standard ISO calendar system. + * As such, the main classes are ISO-only. The second principal is that most of those that want a + * non-ISO calendar system want it for user interaction, thus it is a UI localization issue. + * As such, date and time objects should be held as ISO objects in the data model and persistent + * storage, only being converted to and from a local calendar for display. + * The calendar system would be stored separately in the user preferences. + *

    + *

    + * There are, however, some limited use cases where users believe they need to store and use + * dates in arbitrary calendar systems throughout the application. + * This is supported by {@link java.time.temporal.ChronoLocalDate}, however it is vital to read + * all the associated warnings in the Javadoc of that interface before using it. + * In summary, applications that require general interoperation between multiple calendar systems + * typically need to be written in a very different way to those only using the ISO calendar, + * thus most applications should just use ISO and avoid {@code ChronoLocalDate}. + *

    + *

    + * The API is also designed for user extensibility, as there are many ways of calculating time. + * The {@linkplain java.time.temporal.TemporalField field} and {@linkplain java.time.temporal.TemporalUnit unit} + * API, accessed via {@link java.time.temporal.TemporalAccessor TemporalAccessor} and + * {@link java.time.temporal.Temporal Temporal} provide considerable flexibility to applications. + * In addition, the {@link java.time.temporal.TemporalQuery TemporalQuery} and + * {@link java.time.temporal.TemporalAdjuster TemporalAdjuster} interfaces provide day-to-day + * power, allowing code to read close to business requirements: + *

    + *
    + *   LocalDate customerBirthday = customer.loadBirthdayFromDatabase();
    + *   LocalDate today = LocalDate.now();
    + *   if (customerBirthday.equals(today)) {
    + *     LocalDate specialOfferExpiryDate = today.plusWeeks(2).with(next(FRIDAY));
    + *     customer.sendBirthdaySpecialOffer(specialOfferExpiryDate);
    + *   }
    + *
    + * 
    + * + * @since JDK1.8 + */ +package java.time; diff --git a/jdk/src/share/classes/java/time/temporal/Adjusters.java b/jdk/src/share/classes/java/time/temporal/Adjusters.java new file mode 100644 index 00000000000..45ccfd1a9f5 --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/Adjusters.java @@ -0,0 +1,502 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.DAY_OF_WEEK; +import static java.time.temporal.ChronoField.DAY_OF_YEAR; +import static java.time.temporal.ChronoUnit.DAYS; +import static java.time.temporal.ChronoUnit.MONTHS; +import static java.time.temporal.ChronoUnit.YEARS; + +import java.time.DayOfWeek; +import java.util.Objects; + +/** + * Common implementations of {@code TemporalAdjuster}. + *

    + * This class provides common implementations of {@link TemporalAdjuster}. + * They are especially useful to document the intent of business logic and + * often link well to requirements. + * For example, these two pieces of code do the same thing, but the second + * one is clearer (assuming that there is a static import of this class): + *

    + *  // direct manipulation
    + *  date.withDayOfMonth(1).plusMonths(1).minusDays(1);
    + *  // use of an adjuster from this class
    + *  date.with(lastDayOfMonth());
    + * 
    + * There are two equivalent ways of using a {@code TemporalAdjuster}. + * The first is to invoke the method on the interface directly. + * The second is to use {@link Temporal#with(TemporalAdjuster)}: + *
    + *   // these two lines are equivalent, but the second approach is recommended
    + *   dateTime = adjuster.adjustInto(dateTime);
    + *   dateTime = dateTime.with(adjuster);
    + * 
    + * It is recommended to use the second approach, {@code with(TemporalAdjuster)}, + * as it is a lot clearer to read in code. + * + *

    Specification for implementors

    + * This is a thread-safe utility class. + * All returned adjusters are immutable and thread-safe. + * + * @since 1.8 + */ +public final class Adjusters { + + /** + * Private constructor since this is a utility class. + */ + private Adjusters() { + } + + //----------------------------------------------------------------------- + /** + * Returns the "first day of month" adjuster, which returns a new date set to + * the first day of the current month. + *

    + * The ISO calendar system behaves as follows:
    + * The input 2011-01-15 will return 2011-01-01.
    + * The input 2011-02-15 will return 2011-02-01. + *

    + * The behavior is suitable for use with most calendar systems. + * It is equivalent to: + *

    +     *  temporal.with(DAY_OF_MONTH, 1);
    +     * 
    + * + * @return the first day-of-month adjuster, not null + */ + public static TemporalAdjuster firstDayOfMonth() { + return Impl.FIRST_DAY_OF_MONTH; + } + + /** + * Returns the "last day of month" adjuster, which returns a new date set to + * the last day of the current month. + *

    + * The ISO calendar system behaves as follows:
    + * The input 2011-01-15 will return 2011-01-31.
    + * The input 2011-02-15 will return 2011-02-28.
    + * The input 2012-02-15 will return 2012-02-29 (leap year).
    + * The input 2011-04-15 will return 2011-04-30. + *

    + * The behavior is suitable for use with most calendar systems. + * It is equivalent to: + *

    +     *  long lastDay = temporal.range(DAY_OF_MONTH).getMaximum();
    +     *  temporal.with(DAY_OF_MONTH, lastDay);
    +     * 
    + * + * @return the last day-of-month adjuster, not null + */ + public static TemporalAdjuster lastDayOfMonth() { + return Impl.LAST_DAY_OF_MONTH; + } + + /** + * Returns the "first day of next month" adjuster, which returns a new date set to + * the first day of the next month. + *

    + * The ISO calendar system behaves as follows:
    + * The input 2011-01-15 will return 2011-02-01.
    + * The input 2011-02-15 will return 2011-03-01. + *

    + * The behavior is suitable for use with most calendar systems. + * It is equivalent to: + *

    +     *  temporal.with(DAY_OF_MONTH, 1).plus(1, MONTHS);
    +     * 
    + * + * @return the first day of next month adjuster, not null + */ + public static TemporalAdjuster firstDayOfNextMonth() { + return Impl.FIRST_DAY_OF_NEXT_MONTH; + } + + //----------------------------------------------------------------------- + /** + * Returns the "first day of year" adjuster, which returns a new date set to + * the first day of the current year. + *

    + * The ISO calendar system behaves as follows:
    + * The input 2011-01-15 will return 2011-01-01.
    + * The input 2011-02-15 will return 2011-01-01.
    + *

    + * The behavior is suitable for use with most calendar systems. + * It is equivalent to: + *

    +     *  temporal.with(DAY_OF_YEAR, 1);
    +     * 
    + * + * @return the first day-of-year adjuster, not null + */ + public static TemporalAdjuster firstDayOfYear() { + return Impl.FIRST_DAY_OF_YEAR; + } + + /** + * Returns the "last day of year" adjuster, which returns a new date set to + * the last day of the current year. + *

    + * The ISO calendar system behaves as follows:
    + * The input 2011-01-15 will return 2011-12-31.
    + * The input 2011-02-15 will return 2011-12-31.
    + *

    + * The behavior is suitable for use with most calendar systems. + * It is equivalent to: + *

    +     *  long lastDay = temporal.range(DAY_OF_YEAR).getMaximum();
    +     *  temporal.with(DAY_OF_YEAR, lastDay);
    +     * 
    + * + * @return the last day-of-year adjuster, not null + */ + public static TemporalAdjuster lastDayOfYear() { + return Impl.LAST_DAY_OF_YEAR; + } + + /** + * Returns the "first day of next year" adjuster, which returns a new date set to + * the first day of the next year. + *

    + * The ISO calendar system behaves as follows:
    + * The input 2011-01-15 will return 2012-01-01. + *

    + * The behavior is suitable for use with most calendar systems. + * It is equivalent to: + *

    +     *  temporal.with(DAY_OF_YEAR, 1).plus(1, YEARS);
    +     * 
    + * + * @return the first day of next month adjuster, not null + */ + public static TemporalAdjuster firstDayOfNextYear() { + return Impl.FIRST_DAY_OF_NEXT_YEAR; + } + + //----------------------------------------------------------------------- + /** + * Enum implementing the adjusters. + */ + private static class Impl implements TemporalAdjuster { + /** First day of month adjuster. */ + private static final Impl FIRST_DAY_OF_MONTH = new Impl(0); + /** Last day of month adjuster. */ + private static final Impl LAST_DAY_OF_MONTH = new Impl(1); + /** First day of next month adjuster. */ + private static final Impl FIRST_DAY_OF_NEXT_MONTH = new Impl(2); + /** First day of year adjuster. */ + private static final Impl FIRST_DAY_OF_YEAR = new Impl(3); + /** Last day of year adjuster. */ + private static final Impl LAST_DAY_OF_YEAR = new Impl(4); + /** First day of next month adjuster. */ + private static final Impl FIRST_DAY_OF_NEXT_YEAR = new Impl(5); + /** The ordinal. */ + private final int ordinal; + private Impl(int ordinal) { + this.ordinal = ordinal; + } + @Override + public Temporal adjustInto(Temporal temporal) { + switch (ordinal) { + case 0: return temporal.with(DAY_OF_MONTH, 1); + case 1: return temporal.with(DAY_OF_MONTH, temporal.range(DAY_OF_MONTH).getMaximum()); + case 2: return temporal.with(DAY_OF_MONTH, 1).plus(1, MONTHS); + case 3: return temporal.with(DAY_OF_YEAR, 1); + case 4: return temporal.with(DAY_OF_YEAR, temporal.range(DAY_OF_YEAR).getMaximum()); + case 5: return temporal.with(DAY_OF_YEAR, 1).plus(1, YEARS); + } + throw new IllegalStateException("Unreachable"); + } + } + + //----------------------------------------------------------------------- + /** + * Returns the first in month adjuster, which returns a new date + * in the same month with the first matching day-of-week. + * This is used for expressions like 'first Tuesday in March'. + *

    + * The ISO calendar system behaves as follows:
    + * The input 2011-12-15 for (MONDAY) will return 2011-12-05.
    + * The input 2011-12-15 for (FRIDAY) will return 2011-12-02.
    + *

    + * The behavior is suitable for use with most calendar systems. + * It uses the {@code DAY_OF_WEEK} and {@code DAY_OF_MONTH} fields + * and the {@code DAYS} unit, and assumes a seven day week. + * + * @param dayOfWeek the day-of-week, not null + * @return the first in month adjuster, not null + */ + public static TemporalAdjuster firstInMonth(DayOfWeek dayOfWeek) { + Objects.requireNonNull(dayOfWeek, "dayOfWeek"); + return new DayOfWeekInMonth(1, dayOfWeek); + } + + /** + * Returns the last in month adjuster, which returns a new date + * in the same month with the last matching day-of-week. + * This is used for expressions like 'last Tuesday in March'. + *

    + * The ISO calendar system behaves as follows:
    + * The input 2011-12-15 for (MONDAY) will return 2011-12-26.
    + * The input 2011-12-15 for (FRIDAY) will return 2011-12-30.
    + *

    + * The behavior is suitable for use with most calendar systems. + * It uses the {@code DAY_OF_WEEK} and {@code DAY_OF_MONTH} fields + * and the {@code DAYS} unit, and assumes a seven day week. + * + * @param dayOfWeek the day-of-week, not null + * @return the first in month adjuster, not null + */ + public static TemporalAdjuster lastInMonth(DayOfWeek dayOfWeek) { + Objects.requireNonNull(dayOfWeek, "dayOfWeek"); + return new DayOfWeekInMonth(-1, dayOfWeek); + } + + /** + * Returns the day-of-week in month adjuster, which returns a new date + * in the same month with the ordinal day-of-week. + * This is used for expressions like the 'second Tuesday in March'. + *

    + * The ISO calendar system behaves as follows:
    + * The input 2011-12-15 for (1,TUESDAY) will return 2011-12-06.
    + * The input 2011-12-15 for (2,TUESDAY) will return 2011-12-13.
    + * The input 2011-12-15 for (3,TUESDAY) will return 2011-12-20.
    + * The input 2011-12-15 for (4,TUESDAY) will return 2011-12-27.
    + * The input 2011-12-15 for (5,TUESDAY) will return 2012-01-03.
    + * The input 2011-12-15 for (-1,TUESDAY) will return 2011-12-27 (last in month).
    + * The input 2011-12-15 for (-4,TUESDAY) will return 2011-12-06 (3 weeks before last in month).
    + * The input 2011-12-15 for (-5,TUESDAY) will return 2011-11-29 (4 weeks before last in month).
    + * The input 2011-12-15 for (0,TUESDAY) will return 2011-11-29 (last in previous month).
    + *

    + * For a positive or zero ordinal, the algorithm is equivalent to finding the first + * day-of-week that matches within the month and then adding a number of weeks to it. + * For a negative ordinal, the algorithm is equivalent to finding the last + * day-of-week that matches within the month and then subtracting a number of weeks to it. + * The ordinal number of weeks is not validated and is interpreted leniently + * according to this algorithm. This definition means that an ordinal of zero finds + * the last matching day-of-week in the previous month. + *

    + * The behavior is suitable for use with most calendar systems. + * It uses the {@code DAY_OF_WEEK} and {@code DAY_OF_MONTH} fields + * and the {@code DAYS} unit, and assumes a seven day week. + * + * @param ordinal the week within the month, unbound but typically from -5 to 5 + * @param dayOfWeek the day-of-week, not null + * @return the day-of-week in month adjuster, not null + * @throws IllegalArgumentException if the ordinal is invalid + */ + public static TemporalAdjuster dayOfWeekInMonth(int ordinal, DayOfWeek dayOfWeek) { + Objects.requireNonNull(dayOfWeek, "dayOfWeek"); + return new DayOfWeekInMonth(ordinal, dayOfWeek); + } + + /** + * Class implementing day-of-week in month adjuster. + */ + private static final class DayOfWeekInMonth implements TemporalAdjuster { + /** The ordinal. */ + private final int ordinal; + /** The day-of-week value, from 1 to 7. */ + private final int dowValue; + + private DayOfWeekInMonth(int ordinal, DayOfWeek dow) { + super(); + this.ordinal = ordinal; + this.dowValue = dow.getValue(); + } + @Override + public Temporal adjustInto(Temporal temporal) { + if (ordinal >= 0) { + Temporal temp = temporal.with(DAY_OF_MONTH, 1); + int curDow = temp.get(DAY_OF_WEEK); + int dowDiff = (dowValue - curDow + 7) % 7; + dowDiff += (ordinal - 1L) * 7L; // safe from overflow + return temp.plus(dowDiff, DAYS); + } else { + Temporal temp = temporal.with(DAY_OF_MONTH, temporal.range(DAY_OF_MONTH).getMaximum()); + int curDow = temp.get(DAY_OF_WEEK); + int daysDiff = dowValue - curDow; + daysDiff = (daysDiff == 0 ? 0 : (daysDiff > 0 ? daysDiff - 7 : daysDiff)); + daysDiff -= (-ordinal - 1L) * 7L; // safe from overflow + return temp.plus(daysDiff, DAYS); + } + } + } + + //----------------------------------------------------------------------- + /** + * Returns the next day-of-week adjuster, which adjusts the date to the + * first occurrence of the specified day-of-week after the date being adjusted. + *

    + * The ISO calendar system behaves as follows:
    + * The input 2011-01-15 (a Saturday) for parameter (MONDAY) will return 2011-01-17 (two days later).
    + * The input 2011-01-15 (a Saturday) for parameter (WEDNESDAY) will return 2011-01-19 (four days later).
    + * The input 2011-01-15 (a Saturday) for parameter (SATURDAY) will return 2011-01-22 (seven days later). + *

    + * The behavior is suitable for use with most calendar systems. + * It uses the {@code DAY_OF_WEEK} field and the {@code DAYS} unit, + * and assumes a seven day week. + * + * @param dayOfWeek the day-of-week to move the date to, not null + * @return the next day-of-week adjuster, not null + */ + public static TemporalAdjuster next(DayOfWeek dayOfWeek) { + return new RelativeDayOfWeek(2, dayOfWeek); + } + + /** + * Returns the next-or-same day-of-week adjuster, which adjusts the date to the + * first occurrence of the specified day-of-week after the date being adjusted + * unless it is already on that day in which case the same object is returned. + *

    + * The ISO calendar system behaves as follows:
    + * The input 2011-01-15 (a Saturday) for parameter (MONDAY) will return 2011-01-17 (two days later).
    + * The input 2011-01-15 (a Saturday) for parameter (WEDNESDAY) will return 2011-01-19 (four days later).
    + * The input 2011-01-15 (a Saturday) for parameter (SATURDAY) will return 2011-01-15 (same as input). + *

    + * The behavior is suitable for use with most calendar systems. + * It uses the {@code DAY_OF_WEEK} field and the {@code DAYS} unit, + * and assumes a seven day week. + * + * @param dayOfWeek the day-of-week to check for or move the date to, not null + * @return the next-or-same day-of-week adjuster, not null + */ + public static TemporalAdjuster nextOrSame(DayOfWeek dayOfWeek) { + return new RelativeDayOfWeek(0, dayOfWeek); + } + + /** + * Returns the previous day-of-week adjuster, which adjusts the date to the + * first occurrence of the specified day-of-week before the date being adjusted. + *

    + * The ISO calendar system behaves as follows:
    + * The input 2011-01-15 (a Saturday) for parameter (MONDAY) will return 2011-01-10 (five days earlier).
    + * The input 2011-01-15 (a Saturday) for parameter (WEDNESDAY) will return 2011-01-12 (three days earlier).
    + * The input 2011-01-15 (a Saturday) for parameter (SATURDAY) will return 2011-01-08 (seven days earlier). + *

    + * The behavior is suitable for use with most calendar systems. + * It uses the {@code DAY_OF_WEEK} field and the {@code DAYS} unit, + * and assumes a seven day week. + * + * @param dayOfWeek the day-of-week to move the date to, not null + * @return the previous day-of-week adjuster, not null + */ + public static TemporalAdjuster previous(DayOfWeek dayOfWeek) { + return new RelativeDayOfWeek(3, dayOfWeek); + } + + /** + * Returns the previous-or-same day-of-week adjuster, which adjusts the date to the + * first occurrence of the specified day-of-week before the date being adjusted + * unless it is already on that day in which case the same object is returned. + *

    + * The ISO calendar system behaves as follows:
    + * The input 2011-01-15 (a Saturday) for parameter (MONDAY) will return 2011-01-10 (five days earlier).
    + * The input 2011-01-15 (a Saturday) for parameter (WEDNESDAY) will return 2011-01-12 (three days earlier).
    + * The input 2011-01-15 (a Saturday) for parameter (SATURDAY) will return 2011-01-15 (same as input). + *

    + * The behavior is suitable for use with most calendar systems. + * It uses the {@code DAY_OF_WEEK} field and the {@code DAYS} unit, + * and assumes a seven day week. + * + * @param dayOfWeek the day-of-week to check for or move the date to, not null + * @return the previous-or-same day-of-week adjuster, not null + */ + public static TemporalAdjuster previousOrSame(DayOfWeek dayOfWeek) { + return new RelativeDayOfWeek(1, dayOfWeek); + } + + /** + * Implementation of next, previous or current day-of-week. + */ + private static final class RelativeDayOfWeek implements TemporalAdjuster { + /** Whether the current date is a valid answer. */ + private final int relative; + /** The day-of-week value, from 1 to 7. */ + private final int dowValue; + + private RelativeDayOfWeek(int relative, DayOfWeek dayOfWeek) { + Objects.requireNonNull(dayOfWeek, "dayOfWeek"); + this.relative = relative; + this.dowValue = dayOfWeek.getValue(); + } + + @Override + public Temporal adjustInto(Temporal temporal) { + int calDow = temporal.get(DAY_OF_WEEK); + if (relative < 2 && calDow == dowValue) { + return temporal; + } + if ((relative & 1) == 0) { + int daysDiff = calDow - dowValue; + return temporal.plus(daysDiff >= 0 ? 7 - daysDiff : -daysDiff, DAYS); + } else { + int daysDiff = dowValue - calDow; + return temporal.minus(daysDiff >= 0 ? 7 - daysDiff : -daysDiff, DAYS); + } + } + } + +} diff --git a/jdk/src/share/classes/java/time/temporal/Chrono.java b/jdk/src/share/classes/java/time/temporal/Chrono.java new file mode 100644 index 00000000000..ff4660884f8 --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/Chrono.java @@ -0,0 +1,858 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectStreamException; +import java.time.Clock; +import java.time.DateTimeException; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.calendar.HijrahChrono; +import java.time.calendar.JapaneseChrono; +import java.time.calendar.MinguoChrono; +import java.time.calendar.ThaiBuddhistChrono; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.TextStyle; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.ServiceLoader; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * A calendar system, used to organize and identify dates. + *

    + * The main date and time API is built on the ISO calendar system. + * This class operates behind the scenes to represent the general concept of a calendar system. + * For example, the Japanese, Minguo, Thai Buddhist and others. + *

    + * Most other calendar systems also operate on the shared concepts of year, month and day, + * linked to the cycles of the Earth around the Sun, and the Moon around the Earth. + * These shared concepts are defined by {@link ChronoField} and are availalbe + * for use by any {@code Chrono} implementation: + *

    + *   LocalDate isoDate = ...
    + *   ChronoLocalDate<ThaiBuddhistChrono> thaiDate = ...
    + *   int isoYear = isoDate.get(ChronoField.YEAR);
    + *   int thaiYear = thaiDate.get(ChronoField.YEAR);
    + * 
    + * As shown, although the date objects are in different calendar systems, represented by different + * {@code Chrono} instances, both can be queried using the same constant on {@code ChronoField}. + * For a full discussion of the implications of this, see {@link ChronoLocalDate}. + * In general, the advice is to use the known ISO-based {@code LocalDate}, rather than + * {@code ChronoLocalDate}. + *

    + * While a {@code Chrono} object typically uses {@code ChronoField} and is based on + * an era, year-of-era, month-of-year, day-of-month model of a date, this is not required. + * A {@code Chrono} instance may represent a totally different kind of calendar system, + * such as the Mayan. + *

    + * In practical terms, the {@code Chrono} instance also acts as a factory. + * The {@link #of(String)} method allows an instance to be looked up by identifier, + * while the {@link #ofLocale(Locale)} method allows lookup by locale. + *

    + * The {@code Chrono} instance provides a set of methods to create {@code ChronoLocalDate} instances. + * The date classes are used to manipulate specific dates. + *

      + *
    • {@link #dateNow() dateNow()} + *
    • {@link #dateNow(Clock) dateNow(clock)} + *
    • {@link #dateNow(ZoneId) dateNow(zone)} + *
    • {@link #date(int, int, int) date(yearProleptic, month, day)} + *
    • {@link #date(Era, int, int, int) date(era, yearOfEra, month, day)} + *
    • {@link #dateYearDay(int, int) dateYearDay(yearProleptic, dayOfYear)} + *
    • {@link #dateYearDay(Era, int, int) dateYearDay(era, yearOfEra, dayOfYear)} + *
    • {@link #date(TemporalAccessor) date(TemporalAccessor)} + *

    + * + *

    Adding New Calendars

    + * The set of available chronologies can be extended by applications. + * Adding a new calendar system requires the writing of an implementation of + * {@code Chrono}, {@code ChronoLocalDate} and {@code Era}. + * The majority of the logic specific to the calendar system will be in + * {@code ChronoLocalDate}. The {@code Chrono} subclass acts as a factory. + *

    + * To permit the discovery of additional chronologies, the {@link java.util.ServiceLoader ServiceLoader} + * is used. A file must be added to the {@code META-INF/services} directory with the + * name 'java.time.temporal.Chrono' listing the implementation classes. + * See the ServiceLoader for more details on service loading. + * For lookup by id or calendarType, the system provided calendars are found + * first followed by application provided calendars. + *

    + * Each chronology must define a chronology ID that is unique within the system. + * If the chronology represents a calendar system defined by the + * Unicode Locale Data Markup Language (LDML) specification then that + * calendar type should also be specified. + * + *

    Specification for implementors

    + * This class must be implemented with care to ensure other classes operate correctly. + * All implementations that can be instantiated must be final, immutable and thread-safe. + * Subclasses should be Serializable wherever possible. + * + * @param the type of the implementing subclass + * @since 1.8 + */ +public abstract class Chrono> implements Comparable> { + + /** + * Map of available calendars by ID. + */ + private static final ConcurrentHashMap> CHRONOS_BY_ID = new ConcurrentHashMap<>(); + /** + * Map of available calendars by calendar type. + */ + private static final ConcurrentHashMap> CHRONOS_BY_TYPE = new ConcurrentHashMap<>(); + + /** + * Register a Chrono by ID and type for lookup by {@link #of(java.lang.String)}. + * Chronos must not be registered until they are completely constructed. + * Specifically, not in the constructor of Chrono. + * @param chrono the chronology to register; not null + */ + private static void registerChrono(Chrono chrono) { + Chrono prev = CHRONOS_BY_ID.putIfAbsent(chrono.getId(), chrono); + if (prev == null) { + String type = chrono.getCalendarType(); + if (type != null) { + CHRONOS_BY_TYPE.putIfAbsent(type, chrono); + } + } + } + + /** + * Initialization of the maps from id and type to Chrono. + * The ServiceLoader is used to find and register any implementations + * of {@link javax.time.temporal.Chrono} found in the bootclass loader. + * The built-in chronologies are registered explicitly. + * Calendars configured via the Thread's context classloader are local + * to that thread and are ignored. + *

    + * The initialization is done only once using the registration + * of the ISOChrono as the test and the final step. + * Multiple threads may perform the initialization concurrently. + * Only the first registration of each Chrono is retained by the + * ConcurrentHashMap. + * @return true if the cache was initialized + */ + private static boolean initCache() { + if (CHRONOS_BY_ID.get("ISO") == null) { + // Initialization is incomplete + @SuppressWarnings("rawtypes") + ServiceLoader loader = ServiceLoader.load(Chrono.class, null); + for (Chrono chrono : loader) { + registerChrono(chrono); + } + + // Register these calendars; the ServiceLoader configuration is not used + registerChrono(HijrahChrono.INSTANCE); + registerChrono(JapaneseChrono.INSTANCE); + registerChrono(MinguoChrono.INSTANCE); + registerChrono(ThaiBuddhistChrono.INSTANCE); + + // finally, register ISOChrono to mark initialization is complete + registerChrono(ISOChrono.INSTANCE); + return true; + } + return false; + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code Chrono} from a temporal object. + *

    + * A {@code TemporalAccessor} represents some form of date and time information. + * This factory converts the arbitrary temporal object to an instance of {@code Chrono}. + * If the specified temporal object does not have a chronology, {@link ISOChrono} is returned. + *

    + * The conversion will obtain the chronology using {@link Queries#chrono()}. + *

    + * This method matches the signature of the functional interface {@link TemporalQuery} + * allowing it to be used in queries via method reference, {@code Chrono::from}. + * + * @param temporal the temporal to convert, not null + * @return the chronology, not null + * @throws DateTimeException if unable to convert to an {@code Chrono} + */ + public static Chrono from(TemporalAccessor temporal) { + Objects.requireNonNull(temporal, "temporal"); + Chrono obj = temporal.query(Queries.chrono()); + return (obj != null ? obj : ISOChrono.INSTANCE); + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code Chrono} from a locale. + *

    + * The locale can be used to identify a calendar. + * This uses {@link Locale#getUnicodeLocaleType(String)} to obtain the "ca" key + * to identify the calendar system. + *

    + * If the locale does not contain calendar system information, the standard + * ISO calendar system is used. + * + * @param locale the locale to use to obtain the calendar system, not null + * @return the calendar system associated with the locale, not null + * @throws DateTimeException if the locale-specified calendar cannot be found + */ + public static Chrono ofLocale(Locale locale) { + Objects.requireNonNull(locale, "locale"); + String type = locale.getUnicodeLocaleType("ca"); + if (type == null) { + return ISOChrono.INSTANCE; + } else if ("iso".equals(type) || "iso8601".equals(type)) { + return ISOChrono.INSTANCE; + } else { + Chrono chrono = CHRONOS_BY_TYPE.get(type); + if (chrono == null) { + throw new DateTimeException("Unknown calendar system: " + type); + } + return chrono; + } + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code Chrono} from a chronology ID or + * calendar system type. + *

    + * This returns a chronology based on either the ID or the type. + * The {@link #getId() chronology ID} uniquely identifies the chronology. + * The {@link #getCalendarType() calendar system type} is defined by the LDML specification. + *

    + * The chronology may be a system chronology or a chronology + * provided by the application via ServiceLoader configuration. + *

    + * Since some calendars can be customized, the ID or type typically refers + * to the default customization. For example, the Gregorian calendar can have multiple + * cutover dates from the Julian, but the lookup only provides the default cutover date. + * + * @param id the chronology ID or calendar system type, not null + * @return the chronology with the identifier requested, not null + * @throws DateTimeException if the chronology cannot be found + */ + public static Chrono of(String id) { + Objects.requireNonNull(id, "id"); + do { + Chrono chrono = of0(id); + if (chrono != null) { + return chrono; + } + // If not found, do the initialization (once) and repeat the lookup + } while (initCache()); + + // Look for a Chrono using ServiceLoader of the Thread's ContextClassLoader + // Application provided Chronologies must not be cached + @SuppressWarnings("rawtypes") + ServiceLoader loader = ServiceLoader.load(Chrono.class); + for (Chrono chrono : loader) { + if (id.equals(chrono.getId()) || id.equals(chrono.getCalendarType())) { + return chrono; + } + } + throw new DateTimeException("Unknown chronology: " + id); + } + + /** + * Obtains an instance of {@code Chrono} from a chronology ID or + * calendar system type. + * + * @param id the chronology ID or calendar system type, not null + * @return the chronology with the identifier requested, or {@code null} if not found + */ + private static Chrono of0(String id) { + Chrono chrono = CHRONOS_BY_ID.get(id); + if (chrono == null) { + chrono = CHRONOS_BY_TYPE.get(id); + } + return chrono; + } + + /** + * Returns the available chronologies. + *

    + * Each returned {@code Chrono} is available for use in the system. + * The set of chronologies includes the system chronologies and + * any chronologies provided by the application via ServiceLoader + * configuration. + * + * @return the independent, modifiable set of the available chronology IDs, not null + */ + public static Set> getAvailableChronologies() { + initCache(); // force initialization + HashSet> chronos = new HashSet<>(CHRONOS_BY_ID.values()); + + /// Add in Chronologies from the ServiceLoader configuration + @SuppressWarnings("rawtypes") + ServiceLoader loader = ServiceLoader.load(Chrono.class); + for (Chrono chrono : loader) { + chronos.add(chrono); + } + return chronos; + } + + //----------------------------------------------------------------------- + /** + * Obtains a local date-time from the a date and time. + *

    + * This combines a {@link ChronoLocalDate}, which provides the {@code Chrono}, + * with a {@link LocalTime} to produce a {@link ChronoLocalDateTime}. + *

    + * This method is intended for chronology implementations. + * It uses a standard implementation that is shared for all chronologies. + * + * @param the chronology of the date + * @param date the date, not null + * @param time the time, not null + * @return the local date-time combining the input date and time, not null + */ + public static > ChronoLocalDateTime dateTime(ChronoLocalDate date, LocalTime time) { + return ChronoLocalDateTimeImpl.of(date, time); + } + + //----------------------------------------------------------------------- + /** + * Creates an instance. + */ + protected Chrono() { + } + + //----------------------------------------------------------------------- + /** + * Casts the {@code Temporal} to {@code ChronoLocalDate} with the same chronology. + * + * @param temporal a date-time to cast, not null + * @return the date-time checked and cast to {@code ChronoLocalDate}, not null + * @throws ClassCastException if the date-time cannot be cast to ChronoLocalDate + * or the chronology is not equal this Chrono + */ + ChronoLocalDate ensureChronoLocalDate(Temporal temporal) { + @SuppressWarnings("unchecked") + ChronoLocalDate other = (ChronoLocalDate) temporal; + if (this.equals(other.getChrono()) == false) { + throw new ClassCastException("Chrono mismatch, expected: " + getId() + ", actual: " + other.getChrono().getId()); + } + return other; + } + + /** + * Casts the {@code Temporal} to {@code ChronoLocalDateTime} with the same chronology. + * + * @param temporal a date-time to cast, not null + * @return the date-time checked and cast to {@code ChronoLocalDateTime}, not null + * @throws ClassCastException if the date-time cannot be cast to ChronoLocalDateTimeImpl + * or the chronology is not equal this Chrono + */ + ChronoLocalDateTimeImpl ensureChronoLocalDateTime(Temporal temporal) { + @SuppressWarnings("unchecked") + ChronoLocalDateTimeImpl other = (ChronoLocalDateTimeImpl) temporal; + if (this.equals(other.getDate().getChrono()) == false) { + throw new ClassCastException("Chrono mismatch, required: " + getId() + + ", supplied: " + other.getDate().getChrono().getId()); + } + return other; + } + + /** + * Casts the {@code Temporal} to {@code ChronoZonedDateTimeImpl} with the same chronology. + * + * @param temporal a date-time to cast, not null + * @return the date-time checked and cast to {@code ChronoZonedDateTimeImpl}, not null + * @throws ClassCastException if the date-time cannot be cast to ChronoZonedDateTimeImpl + * or the chronology is not equal this Chrono + */ + ChronoZonedDateTimeImpl ensureChronoZonedDateTime(Temporal temporal) { + @SuppressWarnings("unchecked") + ChronoZonedDateTimeImpl other = (ChronoZonedDateTimeImpl) temporal; + if (this.equals(other.getDate().getChrono()) == false) { + throw new ClassCastException("Chrono mismatch, required: " + getId() + + ", supplied: " + other.getDate().getChrono().getId()); + } + return other; + } + + //----------------------------------------------------------------------- + /** + * Gets the ID of the chronology. + *

    + * The ID uniquely identifies the {@code Chrono}. + * It can be used to lookup the {@code Chrono} using {@link #of(String)}. + * + * @return the chronology ID, not null + * @see #getCalendarType() + */ + public abstract String getId(); + + /** + * Gets the calendar type of the underlying calendar system. + *

    + * The calendar type is an identifier defined by the + * Unicode Locale Data Markup Language (LDML) specification. + * It can be used to lookup the {@code Chrono} using {@link #of(String)}. + * It can also be used as part of a locale, accessible via + * {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'. + * + * @return the calendar system type, null if the calendar is not defined by LDML + * @see #getId() + */ + public abstract String getCalendarType(); + + //----------------------------------------------------------------------- + /** + * Obtains a local date in this chronology from the era, year-of-era, + * month-of-year and day-of-month fields. + * + * @param era the era of the correct type for the chronology, not null + * @param yearOfEra the chronology year-of-era + * @param month the chronology month-of-year + * @param dayOfMonth the chronology day-of-month + * @return the local date in this chronology, not null + * @throws DateTimeException if unable to create the date + */ + public ChronoLocalDate date(Era era, int yearOfEra, int month, int dayOfMonth) { + return date(prolepticYear(era, yearOfEra), month, dayOfMonth); + } + + /** + * Obtains a local date in this chronology from the proleptic-year, + * month-of-year and day-of-month fields. + * + * @param prolepticYear the chronology proleptic-year + * @param month the chronology month-of-year + * @param dayOfMonth the chronology day-of-month + * @return the local date in this chronology, not null + * @throws DateTimeException if unable to create the date + */ + public abstract ChronoLocalDate date(int prolepticYear, int month, int dayOfMonth); + + /** + * Obtains a local date in this chronology from the era, year-of-era and + * day-of-year fields. + * + * @param era the era of the correct type for the chronology, not null + * @param yearOfEra the chronology year-of-era + * @param dayOfYear the chronology day-of-year + * @return the local date in this chronology, not null + * @throws DateTimeException if unable to create the date + */ + public ChronoLocalDate dateYearDay(Era era, int yearOfEra, int dayOfYear) { + return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear); + } + + /** + * Obtains a local date in this chronology from the proleptic-year and + * day-of-year fields. + * + * @param prolepticYear the chronology proleptic-year + * @param dayOfYear the chronology day-of-year + * @return the local date in this chronology, not null + * @throws DateTimeException if unable to create the date + */ + public abstract ChronoLocalDate dateYearDay(int prolepticYear, int dayOfYear); + + /** + * Obtains a local date in this chronology from another temporal object. + *

    + * This creates a date in this chronology based on the specified {@code TemporalAccessor}. + *

    + * The standard mechanism for conversion between date types is the + * {@link ChronoField#EPOCH_DAY local epoch-day} field. + * + * @param temporal the temporal object to convert, not null + * @return the local date in this chronology, not null + * @throws DateTimeException if unable to create the date + */ + public abstract ChronoLocalDate date(TemporalAccessor temporal); + + //----------------------------------------------------------------------- + /** + * Obtains the current local date in this chronology from the system clock in the default time-zone. + *

    + * This will query the {@link Clock#systemDefaultZone() system clock} in the default + * time-zone to obtain the current date. + *

    + * Using this method will prevent the ability to use an alternate clock for testing + * because the clock is hard-coded. + *

    + * This implementation uses {@link #dateNow(Clock)}. + * + * @return the current local date using the system clock and default time-zone, not null + * @throws DateTimeException if unable to create the date + */ + public ChronoLocalDate dateNow() { + return dateNow(Clock.systemDefaultZone()); + } + + /** + * Obtains the current local date in this chronology from the system clock in the specified time-zone. + *

    + * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. + * Specifying the time-zone avoids dependence on the default time-zone. + *

    + * Using this method will prevent the ability to use an alternate clock for testing + * because the clock is hard-coded. + * + * @param zone the zone ID to use, not null + * @return the current local date using the system clock, not null + * @throws DateTimeException if unable to create the date + */ + public ChronoLocalDate dateNow(ZoneId zone) { + return dateNow(Clock.system(zone)); + } + + /** + * Obtains the current local date in this chronology from the specified clock. + *

    + * This will query the specified clock to obtain the current date - today. + * Using this method allows the use of an alternate clock for testing. + * The alternate clock may be introduced using {@link Clock dependency injection}. + * + * @param clock the clock to use, not null + * @return the current local date, not null + * @throws DateTimeException if unable to create the date + */ + public ChronoLocalDate dateNow(Clock clock) { + Objects.requireNonNull(clock, "clock"); + return date(LocalDate.now(clock)); + } + + //----------------------------------------------------------------------- + /** + * Obtains a local date-time in this chronology from another temporal object. + *

    + * This creates a date-time in this chronology based on the specified {@code TemporalAccessor}. + *

    + * The date of the date-time should be equivalent to that obtained by calling + * {@link #date(TemporalAccessor)}. + * The standard mechanism for conversion between time types is the + * {@link ChronoField#NANO_OF_DAY nano-of-day} field. + * + * @param temporal the temporal object to convert, not null + * @return the local date-time in this chronology, not null + * @throws DateTimeException if unable to create the date-time + */ + public ChronoLocalDateTime localDateTime(TemporalAccessor temporal) { + try { + return date(temporal).atTime(LocalTime.from(temporal)); + } catch (DateTimeException ex) { + throw new DateTimeException("Unable to obtain ChronoLocalDateTime from TemporalAccessor: " + temporal.getClass(), ex); + } + } + + /** + * Obtains a zoned date-time in this chronology from another temporal object. + *

    + * This creates a date-time in this chronology based on the specified {@code TemporalAccessor}. + *

    + * This should obtain a {@code ZoneId} using {@link ZoneId#from(TemporalAccessor)}. + * The date-time should be obtained by obtaining an {@code Instant}. + * If that fails, the local date-time should be used. + * + * @param temporal the temporal object to convert, not null + * @return the zoned date-time in this chronology, not null + * @throws DateTimeException if unable to create the date-time + */ + public ChronoZonedDateTime zonedDateTime(TemporalAccessor temporal) { + try { + ZoneId zone = ZoneId.from(temporal); + try { + Instant instant = Instant.from(temporal); + return zonedDateTime(instant, zone); + + } catch (DateTimeException ex1) { + ChronoLocalDateTimeImpl cldt = ensureChronoLocalDateTime(localDateTime(temporal)); + return ChronoZonedDateTimeImpl.ofBest(cldt, zone, null); + } + } catch (DateTimeException ex) { + throw new DateTimeException("Unable to obtain ChronoZonedDateTime from TemporalAccessor: " + temporal.getClass(), ex); + } + } + + /** + * Obtains a zoned date-time in this chronology from an {@code Instant}. + *

    + * This creates a zoned date-time with the same instant as that specified. + * + * @param instant the instant to create the date-time from, not null + * @param zone the time-zone, not null + * @return the zoned date-time, not null + * @throws DateTimeException if the result exceeds the supported range + */ + public ChronoZonedDateTime zonedDateTime(Instant instant, ZoneId zone) { + return ChronoZonedDateTimeImpl.ofInstant(this, instant, zone); + } + + //----------------------------------------------------------------------- + /** + * Checks if the specified year is a leap year. + *

    + * A leap-year is a year of a longer length than normal. + * The exact meaning is determined by the chronology according to the following constraints. + *

      + *
    • a leap-year must imply a year-length longer than a non leap-year. + *
    • a chronology that does not support the concept of a year must return false. + *

    + * + * @param prolepticYear the proleptic-year to check, not validated for range + * @return true if the year is a leap year + */ + public abstract boolean isLeapYear(long prolepticYear); + + /** + * Calculates the proleptic-year given the era and year-of-era. + *

    + * This combines the era and year-of-era into the single proleptic-year field. + * + * @param era the era of the correct type for the chronology, not null + * @param yearOfEra the chronology year-of-era + * @return the proleptic-year + * @throws DateTimeException if unable to convert + */ + public abstract int prolepticYear(Era era, int yearOfEra); + + /** + * Creates the chronology era object from the numeric value. + *

    + * The era is, conceptually, the largest division of the time-line. + * Most calendar systems have a single epoch dividing the time-line into two eras. + * However, some have multiple eras, such as one for the reign of each leader. + * The exact meaning is determined by the chronology according to the following constraints. + *

    + * The era in use at 1970-01-01 must have the value 1. + * Later eras must have sequentially higher values. + * Earlier eras must have sequentially lower values. + * Each chronology must refer to an enum or similar singleton to provide the era values. + *

    + * This method returns the singleton era of the correct type for the specified era value. + * + * @param eraValue the era value + * @return the calendar system era, not null + * @throws DateTimeException if unable to create the era + */ + public abstract Era eraOf(int eraValue); + + /** + * Gets the list of eras for the chronology. + *

    + * Most calendar systems have an era, within which the year has meaning. + * If the calendar system does not support the concept of eras, an empty + * list must be returned. + * + * @return the list of eras for the chronology, may be immutable, not null + */ + public abstract List> eras(); + + //----------------------------------------------------------------------- + /** + * Gets the range of valid values for the specified field. + *

    + * All fields can be expressed as a {@code long} integer. + * This method returns an object that describes the valid range for that value. + *

    + * Note that the result only describes the minimum and maximum valid values + * and it is important not to read too much into them. For example, there + * could be values within the range that are invalid for the field. + *

    + * This method will return a result whether or not the chronology supports the field. + * + * @param field the field to get the range for, not null + * @return the range of valid values for the field, not null + * @throws DateTimeException if the range for the field cannot be obtained + */ + public abstract ValueRange range(ChronoField field); + + //----------------------------------------------------------------------- + /** + * Gets the textual representation of this chronology. + *

    + * This returns the textual name used to identify the chronology. + * The parameters control the style of the returned text and the locale. + * + * @param style the style of the text required, not null + * @param locale the locale to use, not null + * @return the text value of the chronology, not null + */ + public String getText(TextStyle style, Locale locale) { + return new DateTimeFormatterBuilder().appendChronoText(style).toFormatter(locale).print(new TemporalAccessor() { + @Override + public boolean isSupported(TemporalField field) { + return false; + } + @Override + public long getLong(TemporalField field) { + throw new DateTimeException("Unsupported field: " + field); + } + @SuppressWarnings("unchecked") + @Override + public R query(TemporalQuery query) { + if (query == Queries.chrono()) { + return (R) Chrono.this; + } + return TemporalAccessor.super.query(query); + } + }); + } + + //----------------------------------------------------------------------- + /** + * Compares this chronology to another chronology. + *

    + * The comparison order first by the chronology ID string, then by any + * additional information specific to the subclass. + * It is "consistent with equals", as defined by {@link Comparable}. + *

    + * The default implementation compares the chronology ID. + * Subclasses must compare any additional state that they store. + * + * @param other the other chronology to compare to, not null + * @return the comparator value, negative if less, positive if greater + */ + @Override + public int compareTo(Chrono other) { + return getId().compareTo(other.getId()); + } + + /** + * Checks if this chronology is equal to another chronology. + *

    + * The comparison is based on the entire state of the object. + *

    + * The default implementation checks the type and calls {@link #compareTo(Chrono)}. + * + * @param obj the object to check, null returns false + * @return true if this is equal to the other chronology + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof Chrono) { + return compareTo((Chrono) obj) == 0; + } + return false; + } + + /** + * A hash code for this chronology. + *

    + * The default implementation is based on the ID and class. + * Subclasses should add any additional state that they store. + * + * @return a suitable hash code + */ + @Override + public int hashCode() { + return getClass().hashCode() ^ getId().hashCode(); + } + + //----------------------------------------------------------------------- + /** + * Outputs this chronology as a {@code String}, using the ID. + * + * @return a string representation of this chronology, not null + */ + @Override + public String toString() { + return getId(); + } + + //----------------------------------------------------------------------- + /** + * Writes the object using a + * dedicated serialized form. + *

    +     *  out.writeByte(7);  // identifies this as a Chrono
    +     * out.writeUTF(chronoId);
    +     * 
    + * + * @return the instance of {@code Ser}, not null + */ + private Object writeReplace() { + return new Ser(Ser.CHRONO_TYPE, this); + } + + /** + * Defend against malicious streams. + * @return never + * @throws InvalidObjectException always + */ + private Object readResolve() throws ObjectStreamException { + throw new InvalidObjectException("Deserialization via serialization delegate"); + } + + void writeExternal(DataOutput out) throws IOException { + out.writeUTF(getId()); + } + + static Chrono readExternal(DataInput in) throws IOException { + String id = in.readUTF(); + return Chrono.of(id); + } + +} diff --git a/jdk/src/share/classes/java/time/temporal/ChronoField.java b/jdk/src/share/classes/java/time/temporal/ChronoField.java new file mode 100644 index 00000000000..f163654225b --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/ChronoField.java @@ -0,0 +1,610 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import static java.time.temporal.ChronoUnit.DAYS; +import static java.time.temporal.ChronoUnit.ERAS; +import static java.time.temporal.ChronoUnit.FOREVER; +import static java.time.temporal.ChronoUnit.HALF_DAYS; +import static java.time.temporal.ChronoUnit.HOURS; +import static java.time.temporal.ChronoUnit.MICROS; +import static java.time.temporal.ChronoUnit.MILLIS; +import static java.time.temporal.ChronoUnit.MINUTES; +import static java.time.temporal.ChronoUnit.MONTHS; +import static java.time.temporal.ChronoUnit.NANOS; +import static java.time.temporal.ChronoUnit.SECONDS; +import static java.time.temporal.ChronoUnit.WEEKS; +import static java.time.temporal.ChronoUnit.YEARS; + +import java.time.DayOfWeek; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.format.DateTimeBuilder; + +/** + * A standard set of fields. + *

    + * This set of fields provide field-based access to manipulate a date, time or date-time. + * The standard set of fields can be extended by implementing {@link TemporalField}. + *

    + * These fields are intended to be applicable in multiple calendar systems. + * For example, most non-ISO calendar systems define dates as a year, month and day, + * just with slightly different rules. + * The documentation of each field explains how it operates. + * + *

    Specification for implementors

    + * This is a final, immutable and thread-safe enum. + * + * @since 1.8 + */ +public enum ChronoField implements TemporalField { + + /** + * The nano-of-second. + *

    + * This counts the nanosecond within the second, from 0 to 999,999,999. + * This field has the same meaning for all calendar systems. + *

    + * This field is used to represent the nano-of-second handling any fraction of the second. + * Implementations of {@code TemporalAccessor} should provide a value for this field if + * they can return a value for {@link #SECOND_OF_MINUTE}, {@link #SECOND_OF_DAY} or + * {@link #INSTANT_SECONDS} filling unknown precision with zero. + *

    + * When this field is used for setting a value, it should set as much precision as the + * object stores, using integer division to remove excess precision. + * For example, if the {@code TemporalAccessor} stores time to millisecond precision, + * then the nano-of-second must be divided by 1,000,000 before replacing the milli-of-second. + */ + NANO_OF_SECOND("NanoOfSecond", NANOS, SECONDS, ValueRange.of(0, 999_999_999)), + /** + * The nano-of-day. + *

    + * This counts the nanosecond within the day, from 0 to (24 * 60 * 60 * 1,000,000,000) - 1. + * This field has the same meaning for all calendar systems. + *

    + * This field is used to represent the nano-of-day handling any fraction of the second. + * Implementations of {@code TemporalAccessor} should provide a value for this field if + * they can return a value for {@link #SECOND_OF_DAY} filling unknown precision with zero. + */ + NANO_OF_DAY("NanoOfDay", NANOS, DAYS, ValueRange.of(0, 86400L * 1000_000_000L - 1)), + /** + * The micro-of-second. + *

    + * This counts the microsecond within the second, from 0 to 999,999. + * This field has the same meaning for all calendar systems. + *

    + * This field is used to represent the micro-of-second handling any fraction of the second. + * Implementations of {@code TemporalAccessor} should provide a value for this field if + * they can return a value for {@link #SECOND_OF_MINUTE}, {@link #SECOND_OF_DAY} or + * {@link #INSTANT_SECONDS} filling unknown precision with zero. + *

    + * When this field is used for setting a value, it should behave in the same way as + * setting {@link #NANO_OF_SECOND} with the value multiplied by 1,000. + */ + MICRO_OF_SECOND("MicroOfSecond", MICROS, SECONDS, ValueRange.of(0, 999_999)), + /** + * The micro-of-day. + *

    + * This counts the microsecond within the day, from 0 to (24 * 60 * 60 * 1,000,000) - 1. + * This field has the same meaning for all calendar systems. + *

    + * This field is used to represent the micro-of-day handling any fraction of the second. + * Implementations of {@code TemporalAccessor} should provide a value for this field if + * they can return a value for {@link #SECOND_OF_DAY} filling unknown precision with zero. + *

    + * When this field is used for setting a value, it should behave in the same way as + * setting {@link #NANO_OF_DAY} with the value multiplied by 1,000. + */ + MICRO_OF_DAY("MicroOfDay", MICROS, DAYS, ValueRange.of(0, 86400L * 1000_000L - 1)), + /** + * The milli-of-second. + *

    + * This counts the millisecond within the second, from 0 to 999. + * This field has the same meaning for all calendar systems. + *

    + * This field is used to represent the milli-of-second handling any fraction of the second. + * Implementations of {@code TemporalAccessor} should provide a value for this field if + * they can return a value for {@link #SECOND_OF_MINUTE}, {@link #SECOND_OF_DAY} or + * {@link #INSTANT_SECONDS} filling unknown precision with zero. + *

    + * When this field is used for setting a value, it should behave in the same way as + * setting {@link #NANO_OF_SECOND} with the value multiplied by 1,000,000. + */ + MILLI_OF_SECOND("MilliOfSecond", MILLIS, SECONDS, ValueRange.of(0, 999)), + /** + * The milli-of-day. + *

    + * This counts the millisecond within the day, from 0 to (24 * 60 * 60 * 1,000) - 1. + * This field has the same meaning for all calendar systems. + *

    + * This field is used to represent the milli-of-day handling any fraction of the second. + * Implementations of {@code TemporalAccessor} should provide a value for this field if + * they can return a value for {@link #SECOND_OF_DAY} filling unknown precision with zero. + *

    + * When this field is used for setting a value, it should behave in the same way as + * setting {@link #NANO_OF_DAY} with the value multiplied by 1,000,000. + */ + MILLI_OF_DAY("MilliOfDay", MILLIS, DAYS, ValueRange.of(0, 86400L * 1000L - 1)), + /** + * The second-of-minute. + *

    + * This counts the second within the minute, from 0 to 59. + * This field has the same meaning for all calendar systems. + */ + SECOND_OF_MINUTE("SecondOfMinute", SECONDS, MINUTES, ValueRange.of(0, 59)), + /** + * The second-of-day. + *

    + * This counts the second within the day, from 0 to (24 * 60 * 60) - 1. + * This field has the same meaning for all calendar systems. + */ + SECOND_OF_DAY("SecondOfDay", SECONDS, DAYS, ValueRange.of(0, 86400L - 1)), + /** + * The minute-of-hour. + *

    + * This counts the minute within the hour, from 0 to 59. + * This field has the same meaning for all calendar systems. + */ + MINUTE_OF_HOUR("MinuteOfHour", MINUTES, HOURS, ValueRange.of(0, 59)), + /** + * The minute-of-day. + *

    + * This counts the minute within the day, from 0 to (24 * 60) - 1. + * This field has the same meaning for all calendar systems. + */ + MINUTE_OF_DAY("MinuteOfDay", MINUTES, DAYS, ValueRange.of(0, (24 * 60) - 1)), + /** + * The hour-of-am-pm. + *

    + * This counts the hour within the AM/PM, from 0 to 11. + * This is the hour that would be observed on a standard 12-hour digital clock. + * This field has the same meaning for all calendar systems. + */ + HOUR_OF_AMPM("HourOfAmPm", HOURS, HALF_DAYS, ValueRange.of(0, 11)), + /** + * The clock-hour-of-am-pm. + *

    + * This counts the hour within the AM/PM, from 1 to 12. + * This is the hour that would be observed on a standard 12-hour analog wall clock. + * This field has the same meaning for all calendar systems. + */ + CLOCK_HOUR_OF_AMPM("ClockHourOfAmPm", HOURS, HALF_DAYS, ValueRange.of(1, 12)), + /** + * The hour-of-day. + *

    + * This counts the hour within the day, from 0 to 23. + * This is the hour that would be observed on a standard 24-hour digital clock. + * This field has the same meaning for all calendar systems. + */ + HOUR_OF_DAY("HourOfDay", HOURS, DAYS, ValueRange.of(0, 23)), + /** + * The clock-hour-of-day. + *

    + * This counts the hour within the AM/PM, from 1 to 24. + * This is the hour that would be observed on a 24-hour analog wall clock. + * This field has the same meaning for all calendar systems. + */ + CLOCK_HOUR_OF_DAY("ClockHourOfDay", HOURS, DAYS, ValueRange.of(1, 24)), + /** + * The am-pm-of-day. + *

    + * This counts the AM/PM within the day, from 0 (AM) to 1 (PM). + * This field has the same meaning for all calendar systems. + */ + AMPM_OF_DAY("AmPmOfDay", HALF_DAYS, DAYS, ValueRange.of(0, 1)), + /** + * The day-of-week, such as Tuesday. + *

    + * This represents the standard concept of the day of the week. + * In the default ISO calendar system, this has values from Monday (1) to Sunday (7). + * The {@link DayOfWeek} class can be used to interpret the result. + *

    + * Most non-ISO calendar systems also define a seven day week that aligns with ISO. + * Those calendar systems must also use the same numbering system, from Monday (1) to + * Sunday (7), which allows {@code DayOfWeek} to be used. + *

    + * Calendar systems that do not have a standard seven day week should implement this field + * if they have a similar concept of named or numbered days within a period similar + * to a week. It is recommended that the numbering starts from 1. + */ + DAY_OF_WEEK("DayOfWeek", DAYS, WEEKS, ValueRange.of(1, 7)), + /** + * The aligned day-of-week within a month. + *

    + * This represents concept of the count of days within the period of a week + * where the weeks are aligned to the start of the month. + * This field is typically used with {@link #ALIGNED_WEEK_OF_MONTH}. + *

    + * For example, in a calendar systems with a seven day week, the first aligned-week-of-month + * starts on day-of-month 1, the second aligned-week starts on day-of-month 8, and so on. + * Within each of these aligned-weeks, the days are numbered from 1 to 7 and returned + * as the value of this field. + * As such, day-of-month 1 to 7 will have aligned-day-of-week values from 1 to 7. + * And day-of-month 8 to 14 will repeat this with aligned-day-of-week values from 1 to 7. + *

    + * Calendar systems that do not have a seven day week should typically implement this + * field in the same way, but using the alternate week length. + */ + ALIGNED_DAY_OF_WEEK_IN_MONTH("AlignedDayOfWeekInMonth", DAYS, WEEKS, ValueRange.of(1, 7)), + /** + * The aligned day-of-week within a year. + *

    + * This represents concept of the count of days within the period of a week + * where the weeks are aligned to the start of the year. + * This field is typically used with {@link #ALIGNED_WEEK_OF_YEAR}. + *

    + * For example, in a calendar systems with a seven day week, the first aligned-week-of-year + * starts on day-of-year 1, the second aligned-week starts on day-of-year 8, and so on. + * Within each of these aligned-weeks, the days are numbered from 1 to 7 and returned + * as the value of this field. + * As such, day-of-year 1 to 7 will have aligned-day-of-week values from 1 to 7. + * And day-of-year 8 to 14 will repeat this with aligned-day-of-week values from 1 to 7. + *

    + * Calendar systems that do not have a seven day week should typically implement this + * field in the same way, but using the alternate week length. + */ + ALIGNED_DAY_OF_WEEK_IN_YEAR("AlignedDayOfWeekInYear", DAYS, WEEKS, ValueRange.of(1, 7)), + /** + * The day-of-month. + *

    + * This represents the concept of the day within the month. + * In the default ISO calendar system, this has values from 1 to 31 in most months. + * April, June, September, November have days from 1 to 30, while February has days + * from 1 to 28, or 29 in a leap year. + *

    + * Non-ISO calendar systems should implement this field using the most recognized + * day-of-month values for users of the calendar system. + * Normally, this is a count of days from 1 to the length of the month. + */ + DAY_OF_MONTH("DayOfMonth", DAYS, MONTHS, ValueRange.of(1, 28, 31)), + /** + * The day-of-year. + *

    + * This represents the concept of the day within the year. + * In the default ISO calendar system, this has values from 1 to 365 in standard + * years and 1 to 366 in leap years. + *

    + * Non-ISO calendar systems should implement this field using the most recognized + * day-of-year values for users of the calendar system. + * Normally, this is a count of days from 1 to the length of the year. + */ + DAY_OF_YEAR("DayOfYear", DAYS, YEARS, ValueRange.of(1, 365, 366)), + /** + * The epoch-day, based on the Java epoch of 1970-01-01 (ISO). + *

    + * This field is the sequential count of days where 1970-01-01 (ISO) is zero. + * Note that this uses the local time-line, ignoring offset and time-zone. + *

    + * This field is strictly defined to have the same meaning in all calendar systems. + * This is necessary to ensure interoperation between calendars. + */ + EPOCH_DAY("EpochDay", DAYS, FOREVER, ValueRange.of((long) (Year.MIN_VALUE * 365.25), (long) (Year.MAX_VALUE * 365.25))), + /** + * The aligned week within a month. + *

    + * This represents concept of the count of weeks within the period of a month + * where the weeks are aligned to the start of the month. + * This field is typically used with {@link #ALIGNED_DAY_OF_WEEK_IN_MONTH}. + *

    + * For example, in a calendar systems with a seven day week, the first aligned-week-of-month + * starts on day-of-month 1, the second aligned-week starts on day-of-month 8, and so on. + * Thus, day-of-month values 1 to 7 are in aligned-week 1, while day-of-month values + * 8 to 14 are in aligned-week 2, and so on. + *

    + * Calendar systems that do not have a seven day week should typically implement this + * field in the same way, but using the alternate week length. + */ + ALIGNED_WEEK_OF_MONTH("AlignedWeekOfMonth", WEEKS, MONTHS, ValueRange.of(1, 4, 5)), + /** + * The aligned week within a year. + *

    + * This represents concept of the count of weeks within the period of a year + * where the weeks are aligned to the start of the year. + * This field is typically used with {@link #ALIGNED_DAY_OF_WEEK_IN_YEAR}. + *

    + * For example, in a calendar systems with a seven day week, the first aligned-week-of-year + * starts on day-of-year 1, the second aligned-week starts on day-of-year 8, and so on. + * Thus, day-of-year values 1 to 7 are in aligned-week 1, while day-of-year values + * 8 to 14 are in aligned-week 2, and so on. + *

    + * Calendar systems that do not have a seven day week should typically implement this + * field in the same way, but using the alternate week length. + */ + ALIGNED_WEEK_OF_YEAR("AlignedWeekOfYear", WEEKS, YEARS, ValueRange.of(1, 53)), + /** + * The month-of-year, such as March. + *

    + * This represents the concept of the month within the year. + * In the default ISO calendar system, this has values from January (1) to December (12). + *

    + * Non-ISO calendar systems should implement this field using the most recognized + * month-of-year values for users of the calendar system. + * Normally, this is a count of months starting from 1. + */ + MONTH_OF_YEAR("MonthOfYear", MONTHS, YEARS, ValueRange.of(1, 12)), + /** + * The epoch-month based on the Java epoch of 1970-01-01. + *

    + * This field is the sequential count of months where January 1970 (ISO) is zero. + * Note that this uses the local time-line, ignoring offset and time-zone. + *

    + * Non-ISO calendar systems should also implement this field to represent a sequential + * count of months. It is recommended to define zero as the month of 1970-01-01 (ISO). + */ + EPOCH_MONTH("EpochMonth", MONTHS, FOREVER, ValueRange.of((Year.MIN_VALUE - 1970L) * 12, (Year.MAX_VALUE - 1970L) * 12L - 1L)), + /** + * The year within the era. + *

    + * This represents the concept of the year within the era. + * This field is typically used with {@link #ERA}. + *

    + * The standard mental model for a date is based on three concepts - year, month and day. + * These map onto the {@code YEAR}, {@code MONTH_OF_YEAR} and {@code DAY_OF_MONTH} fields. + * Note that there is no reference to eras. + * The full model for a date requires four concepts - era, year, month and day. These map onto + * the {@code ERA}, {@code YEAR_OF_ERA}, {@code MONTH_OF_YEAR} and {@code DAY_OF_MONTH} fields. + * Whether this field or {@code YEAR} is used depends on which mental model is being used. + * See {@link ChronoLocalDate} for more discussion on this topic. + *

    + * In the default ISO calendar system, there are two eras defined, 'BCE' and 'CE'. + * The era 'CE' is the one currently in use and year-of-era runs from 1 to the maximum value. + * The era 'BCE' is the previous era, and the year-of-era runs backwards. + *

    + * For example, subtracting a year each time yield the following:
    + * - year-proleptic 2 = 'CE' year-of-era 2
    + * - year-proleptic 1 = 'CE' year-of-era 1
    + * - year-proleptic 0 = 'BCE' year-of-era 1
    + * - year-proleptic -1 = 'BCE' year-of-era 2
    + *

    + * Note that the ISO-8601 standard does not actually define eras. + * Note also that the ISO eras do not align with the well-known AD/BC eras due to the + * change between the Julian and Gregorian calendar systems. + *

    + * Non-ISO calendar systems should implement this field using the most recognized + * year-of-era value for users of the calendar system. + * Since most calendar systems have only two eras, the year-of-era numbering approach + * will typically be the same as that used by the ISO calendar system. + * The year-of-era value should typically always be positive, however this is not required. + */ + YEAR_OF_ERA("YearOfEra", YEARS, FOREVER, ValueRange.of(1, Year.MAX_VALUE, Year.MAX_VALUE + 1)), + /** + * The proleptic year, such as 2012. + *

    + * This represents the concept of the year, counting sequentially and using negative numbers. + * The proleptic year is not interpreted in terms of the era. + * See {@link #YEAR_OF_ERA} for an example showing the mapping from proleptic year to year-of-era. + *

    + * The standard mental model for a date is based on three concepts - year, month and day. + * These map onto the {@code YEAR}, {@code MONTH_OF_YEAR} and {@code DAY_OF_MONTH} fields. + * Note that there is no reference to eras. + * The full model for a date requires four concepts - era, year, month and day. These map onto + * the {@code ERA}, {@code YEAR_OF_ERA}, {@code MONTH_OF_YEAR} and {@code DAY_OF_MONTH} fields. + * Whether this field or {@code YEAR_OF_ERA} is used depends on which mental model is being used. + * See {@link ChronoLocalDate} for more discussion on this topic. + *

    + * Non-ISO calendar systems should implement this field as follows. + * If the calendar system has only two eras, before and after a fixed date, then the + * proleptic-year value must be the same as the year-of-era value for the later era, + * and increasingly negative for the earlier era. + * If the calendar system has more than two eras, then the proleptic-year value may be + * defined with any appropriate value, although defining it to be the same as ISO may be + * the best option. + */ + YEAR("Year", YEARS, FOREVER, ValueRange.of(Year.MIN_VALUE, Year.MAX_VALUE)), + /** + * The era. + *

    + * This represents the concept of the era, which is the largest division of the time-line. + * This field is typically used with {@link #YEAR_OF_ERA}. + *

    + * In the default ISO calendar system, there are two eras defined, 'BCE' and 'CE'. + * The era 'CE' is the one currently in use and year-of-era runs from 1 to the maximum value. + * The era 'BCE' is the previous era, and the year-of-era runs backwards. + * See {@link #YEAR_OF_ERA} for a full example. + *

    + * Non-ISO calendar systems should implement this field to define eras. + * The value of the era that was active on 1970-01-01 (ISO) must be assigned the value 1. + * Earlier eras must have sequentially smaller values. + * Later eras must have sequentially larger values, + */ + ERA("Era", ERAS, FOREVER, ValueRange.of(0, 1)), + /** + * The instant epoch-seconds. + *

    + * This represents the concept of the sequential count of seconds where + * 1970-01-01T00:00Z (ISO) is zero. + * This field may be used with {@link #NANO_OF_DAY} to represent the fraction of the day. + *

    + * An {@link Instant} represents an instantaneous point on the time-line. + * On their own they have no elements which allow a local date-time to be obtained. + * Only when paired with an offset or time-zone can the local date or time be found. + * This field allows the seconds part of the instant to be queried. + *

    + * This field is strictly defined to have the same meaning in all calendar systems. + * This is necessary to ensure interoperation between calendars. + */ + INSTANT_SECONDS("InstantSeconds", SECONDS, FOREVER, ValueRange.of(Long.MIN_VALUE, Long.MAX_VALUE)), + /** + * The offset from UTC/Greenwich. + *

    + * This represents the concept of the offset in seconds of local time from UTC/Greenwich. + *

    + * A {@link ZoneOffset} represents the period of time that local time differs from UTC/Greenwich. + * This is usually a fixed number of hours and minutes. + * It is equivalent to the {@link ZoneOffset#getTotalSeconds() total amount} of the offset in seconds. + * For example, during the winter Paris has an offset of {@code +01:00}, which is 3600 seconds. + *

    + * This field is strictly defined to have the same meaning in all calendar systems. + * This is necessary to ensure interoperation between calendars. + */ + OFFSET_SECONDS("OffsetSeconds", SECONDS, FOREVER, ValueRange.of(-18 * 3600, 18 * 3600)); + + private final String name; + private final TemporalUnit baseUnit; + private final TemporalUnit rangeUnit; + private final ValueRange range; + + private ChronoField(String name, TemporalUnit baseUnit, TemporalUnit rangeUnit, ValueRange range) { + this.name = name; + this.baseUnit = baseUnit; + this.rangeUnit = rangeUnit; + this.range = range; + } + + //----------------------------------------------------------------------- + @Override + public String getName() { + return name; + } + + @Override + public TemporalUnit getBaseUnit() { + return baseUnit; + } + + @Override + public TemporalUnit getRangeUnit() { + return rangeUnit; + } + + @Override + public ValueRange range() { + return range; + } + + //----------------------------------------------------------------------- + /** + * Checks if this field represents a component of a date. + * + * @return true if it is a component of a date + */ + public boolean isDateField() { + return ordinal() >= DAY_OF_WEEK.ordinal() && ordinal() <= ERA.ordinal(); + } + + /** + * Checks if this field represents a component of a time. + * + * @return true if it is a component of a time + */ + public boolean isTimeField() { + return ordinal() < DAY_OF_WEEK.ordinal(); + } + + //----------------------------------------------------------------------- + /** + * Checks that the specified value is valid for this field. + *

    + * This validates that the value is within the outer range of valid values + * returned by {@link #range()}. + * + * @param value the value to check + * @return the value that was passed in + */ + public long checkValidValue(long value) { + return range().checkValidValue(value, this); + } + + /** + * Checks that the specified value is valid and fits in an {@code int}. + *

    + * This validates that the value is within the outer range of valid values + * returned by {@link #range()}. + * It also checks that all valid values are within the bounds of an {@code int}. + * + * @param value the value to check + * @return the value that was passed in + */ + public int checkValidIntValue(long value) { + return range().checkValidIntValue(value, this); + } + + //----------------------------------------------------------------------- + @Override + public boolean doIsSupported(TemporalAccessor temporal) { + return temporal.isSupported(this); + } + + @Override + public ValueRange doRange(TemporalAccessor temporal) { + return temporal.range(this); + } + + @Override + public long doGet(TemporalAccessor temporal) { + return temporal.getLong(this); + } + + @SuppressWarnings("unchecked") + @Override + public R doWith(R temporal, long newValue) { + return (R) temporal.with(this, newValue); + } + + //----------------------------------------------------------------------- + @Override + public boolean resolve(DateTimeBuilder builder, long value) { + return false; // resolve implemented in builder + } + + //----------------------------------------------------------------------- + @Override + public String toString() { + return getName(); + } + +} diff --git a/jdk/src/share/classes/java/time/temporal/ChronoLocalDate.java b/jdk/src/share/classes/java/time/temporal/ChronoLocalDate.java new file mode 100644 index 00000000000..4701296bd7a --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/ChronoLocalDate.java @@ -0,0 +1,691 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import static java.time.temporal.ChronoField.EPOCH_DAY; +import static java.time.temporal.ChronoField.ERA; +import static java.time.temporal.ChronoField.YEAR; +import static java.time.temporal.ChronoUnit.DAYS; + +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.Comparator; +import java.util.Objects; + +/** + * A date without time-of-day or time-zone in an arbitrary chronology, intended + * for advanced globalization use cases. + *

    + * Most applications should declare method signatures, fields and variables + * as {@link LocalDate}, not this interface. + *

    + * A {@code ChronoLocalDate} is the abstract representation of a date where the + * {@code Chrono chronology}, or calendar system, is pluggable. + * The date is defined in terms of fields expressed by {@link TemporalField}, + * where most common implementations are defined in {@link ChronoField}. + * The chronology defines how the calendar system operates and the meaning of + * the standard fields. + * + *

    When to use this interface

    + * The design of the API encourages the use of {@code LocalDate} rather than this + * interface, even in the case where the application needs to deal with multiple + * calendar systems. The rationale for this is explored in the following documentation. + *

    + * The primary use case where this interface should be used is where the generic + * type parameter {@code } is fully defined as a specific chronology. + * In that case, the assumptions of that chronology are known at development + * time and specified in the code. + *

    + * When the chronology is defined in the generic type parameter as ? or otherwise + * unknown at development time, the rest of the discussion below applies. + *

    + * To emphasize the point, declaring a method signature, field or variable as this + * interface type can initially seem like the sensible way to globalize an application, + * however it is usually the wrong approach. + * As such, it should be considered an application-wide architectural decision to choose + * to use this interface as opposed to {@code LocalDate}. + * + *

    Architectural issues to consider

    + * These are some of the points that must be considered before using this interface + * throughout an application. + *

    + * 1) Applications using this interface, as opposed to using just {@code LocalDate}, + * face a significantly higher probability of bugs. This is because the calendar system + * in use is not known at development time. A key cause of bugs is where the developer + * applies assumptions from their day-to-day knowledge of the ISO calendar system + * to code that is intended to deal with any arbitrary calendar system. + * The section below outlines how those assumptions can cause problems + * The primary mechanism for reducing this increased risk of bugs is a strong code review process. + * This should also be considered a extra cost in maintenance for the lifetime of the code. + *

    + * 2) This interface does not enforce immutability of implementations. + * While the implementation notes indicate that all implementations must be immutable + * there is nothing in the code or type system to enforce this. Any method declared + * to accept a {@code ChronoLocalDate} could therefore be passed a poorly or + * maliciously written mutable implementation. + *

    + * 3) Applications using this interface must consider the impact of eras. + * {@code LocalDate} shields users from the concept of eras, by ensuring that {@code getYear()} + * returns the proleptic year. That decision ensures that developers can think of + * {@code LocalDate} instances as consisting of three fields - year, month-of-year and day-of-month. + * By contrast, users of this interface must think of dates as consisting of four fields - + * era, year-of-era, month-of-year and day-of-month. The extra era field is frequently + * forgotten, yet it is of vital importance to dates in an arbitrary calendar system. + * For example, in the Japanese calendar system, the era represents the reign of an Emperor. + * Whenever one reign ends and another starts, the year-of-era is reset to one. + *

    + * 4) The only agreed international standard for passing a date between two systems + * is the ISO-8601 standard which requires the ISO calendar system. Using this interface + * throughout the application will inevitably lead to the requirement to pass the date + * across a network or component boundary, requiring an application specific protocol or format. + *

    + * 5) Long term persistence, such as a database, will almost always only accept dates in the + * ISO-8601 calendar system (or the related Julian-Gregorian). Passing around dates in other + * calendar systems increases the complications of interacting with persistence. + *

    + * 6) Most of the time, passing a {@code ChronoLocalDate} throughout an application + * is unnecessary, as discussed in the last section below. + * + *

    False assumptions causing bugs in multi-calendar system code

    + * As indicated above, there are many issues to consider when try to use and manipulate a + * date in an arbitrary calendar system. These are some of the key issues. + *

    + * Code that queries the day-of-month and assumes that the value will never be more than + * 31 is invalid. Some calendar systems have more than 31 days in some months. + *

    + * Code that adds 12 months to a date and assumes that a year has been added is invalid. + * Some calendar systems have a different number of months, such as 13 in the Coptic or Ethiopic. + *

    + * Code that adds one month to a date and assumes that the month-of-year value will increase + * by one or wrap to the next year is invalid. Some calendar systems have a variable number + * of months in a year, such as the Hebrew. + *

    + * Code that adds one month, then adds a second one month and assumes that the day-of-month + * will remain close to its original value is invalid. Some calendar systems have a large difference + * between the length of the longest month and the length of the shortest month. + * For example, the Coptic or Ethiopic have 12 months of 30 days and 1 month of 5 days. + *

    + * Code that adds seven days and assumes that a week has been added is invalid. + * Some calendar systems have weeks of other than seven days, such as the French Revolutionary. + *

    + * Code that assumes that because the year of {@code date1} is greater than the year of {@code date2} + * then {@code date1} is after {@code date2} is invalid. This is invalid for all calendar systems + * when referring to the year-of-era, and especially untrue of the Japanese calendar system + * where the year-of-era restarts with the reign of every new Emperor. + *

    + * Code that treats month-of-year one and day-of-month one as the start of the year is invalid. + * Not all calendar systems start the year when the month value is one. + *

    + * In general, manipulating a date, and even querying a date, is wide open to bugs when the + * calendar system is unknown at development time. This is why it is essential that code using + * this interface is subjected to additional code reviews. It is also why an architectural + * decision to avoid this interface type is usually the correct one. + * + *

    Using LocalDate instead

    + * The primary alternative to using this interface throughout your application is as follows. + *

      + *
    • Declare all method signatures referring to dates in terms of {@code LocalDate}. + *
    • Either store the chronology (calendar system) in the user profile or lookup + * the chronology from the user locale + *
    • Convert the ISO {@code LocalDate} to and from the user's preferred calendar system during + * printing and parsing + *

    + * This approach treats the problem of globalized calendar systems as a localization issue + * and confines it to the UI layer. This approach is in keeping with other localization + * issues in the java platform. + *

    + * As discussed above, performing calculations on a date where the rules of the calendar system + * are pluggable requires skill and is not recommended. + * Fortunately, the need to perform calculations on a date in an arbitrary calendar system + * is extremely rare. For example, it is highly unlikely that the business rules of a library + * book rental scheme will allow rentals to be for one month, where meaning of the month + * is dependent on the user's preferred calendar system. + *

    + * A key use case for calculations on a date in an arbitrary calendar system is producing + * a month-by-month calendar for display and user interaction. Again, this is a UI issue, + * and use of this interface solely within a few methods of the UI layer may be justified. + *

    + * In any other part of the system, where a date must be manipulated in a calendar system + * other than ISO, the use case will generally specify the calendar system to use. + * For example, an application may need to calculate the next Islamic or Hebrew holiday + * which may require manipulating the date. + * This kind of use case can be handled as follows: + *

      + *
    • start from the ISO {@code LocalDate} being passed to the method + *
    • convert the date to the alternate calendar system, which for this use case is known + * rather than arbitrary + *
    • perform the calculation + *
    • convert back to {@code LocalDate} + *

    + * Developers writing low-level frameworks or libraries should also avoid this interface. + * Instead, one of the two general purpose access interfaces should be used. + * Use {@link TemporalAccessor} if read-only access is required, or use {@link Temporal} + * if read-write access is required. + * + *

    Specification for implementors

    + * This interface must be implemented with care to ensure other classes operate correctly. + * All implementations that can be instantiated must be final, immutable and thread-safe. + * Subclasses should be Serializable wherever possible. + *

    + * Additional calendar systems may be added to the system. + * See {@link Chrono} for more details. + * + * @param the chronology of this date + * @since 1.8 + */ +public interface ChronoLocalDate> + extends Temporal, TemporalAdjuster, Comparable> { + + /** + * Comparator for two {@code ChronoLocalDate}s ignoring the chronology. + *

    + * This comparator differs from the comparison in {@link #compareTo} in that it + * only compares the underlying date and not the chronology. + * This allows dates in different calendar systems to be compared based + * on the time-line position. + * This is equivalent to using {@code Long.compare(date1.toEpochDay(), date2.toEpochDay())}. + * + * @see #isAfter + * @see #isBefore + * @see #isEqual + */ + public static final Comparator> DATE_COMPARATOR = + new Comparator>() { + @Override + public int compare(ChronoLocalDate date1, ChronoLocalDate date2) { + return Long.compare(date1.toEpochDay(), date2.toEpochDay()); + } + }; + + //----------------------------------------------------------------------- + /** + * Gets the chronology of this date. + *

    + * The {@code Chrono} represents the calendar system in use. + * The era and other fields in {@link ChronoField} are defined by the chronology. + * + * @return the chronology, not null + */ + C getChrono(); + + /** + * Gets the era, as defined by the chronology. + *

    + * The era is, conceptually, the largest division of the time-line. + * Most calendar systems have a single epoch dividing the time-line into two eras. + * However, some have multiple eras, such as one for the reign of each leader. + * The exact meaning is determined by the {@code Chrono}. + *

    + * All correctly implemented {@code Era} classes are singletons, thus it + * is valid code to write {@code date.getEra() == SomeChrono.ERA_NAME)}. + *

    + * This default implementation uses {@link Chrono#eraOf(int)}. + * + * @return the chronology specific era constant applicable at this date, not null + */ + public default Era getEra() { + return getChrono().eraOf(get(ERA)); + } + + /** + * Checks if the year is a leap year, as defined by the calendar system. + *

    + * A leap-year is a year of a longer length than normal. + * The exact meaning is determined by the chronology with the constraint that + * a leap-year must imply a year-length longer than a non leap-year. + *

    + * This default implementation uses {@link Chrono#isLeapYear(long)}. + * + * @return true if this date is in a leap year, false otherwise + */ + public default boolean isLeapYear() { + return getChrono().isLeapYear(getLong(YEAR)); + } + + /** + * Returns the length of the month represented by this date, as defined by the calendar system. + *

    + * This returns the length of the month in days. + * + * @return the length of the month in days + */ + int lengthOfMonth(); + + /** + * Returns the length of the year represented by this date, as defined by the calendar system. + *

    + * This returns the length of the year in days. + *

    + * The default implementation uses {@link #isLeapYear()} and returns 365 or 366. + * + * @return the length of the year in days + */ + public default int lengthOfYear() { + return (isLeapYear() ? 366 : 365); + } + + @Override + public default boolean isSupported(TemporalField field) { + if (field instanceof ChronoField) { + return ((ChronoField) field).isDateField(); + } + return field != null && field.doIsSupported(this); + } + + //----------------------------------------------------------------------- + // override for covariant return type + /** + * {@inheritDoc} + * @throws DateTimeException {@inheritDoc} + * @throws ArithmeticException {@inheritDoc} + */ + @Override + public default ChronoLocalDate with(TemporalAdjuster adjuster) { + return getChrono().ensureChronoLocalDate(Temporal.super.with(adjuster)); + } + + /** + * {@inheritDoc} + * @throws DateTimeException {@inheritDoc} + * @throws ArithmeticException {@inheritDoc} + */ + @Override + public default ChronoLocalDate with(TemporalField field, long newValue) { + if (field instanceof ChronoField) { + throw new DateTimeException("Unsupported field: " + field.getName()); + } + return getChrono().ensureChronoLocalDate(field.doWith(this, newValue)); + } + + /** + * {@inheritDoc} + * @throws DateTimeException {@inheritDoc} + * @throws ArithmeticException {@inheritDoc} + */ + @Override + public default ChronoLocalDate plus(TemporalAdder adder) { + return getChrono().ensureChronoLocalDate(Temporal.super.plus(adder)); + } + + /** + * {@inheritDoc} + * @throws DateTimeException {@inheritDoc} + * @throws ArithmeticException {@inheritDoc} + */ + @Override + public default ChronoLocalDate plus(long amountToAdd, TemporalUnit unit) { + if (unit instanceof ChronoUnit) { + throw new DateTimeException("Unsupported unit: " + unit.getName()); + } + return getChrono().ensureChronoLocalDate(unit.doPlus(this, amountToAdd)); + } + + /** + * {@inheritDoc} + * @throws DateTimeException {@inheritDoc} + * @throws ArithmeticException {@inheritDoc} + */ + @Override + public default ChronoLocalDate minus(TemporalSubtractor subtractor) { + return getChrono().ensureChronoLocalDate(Temporal.super.minus(subtractor)); + } + + /** + * {@inheritDoc} + * @throws DateTimeException {@inheritDoc} + * @throws ArithmeticException {@inheritDoc} + */ + @Override + public default ChronoLocalDate minus(long amountToSubtract, TemporalUnit unit) { + return getChrono().ensureChronoLocalDate(Temporal.super.minus(amountToSubtract, unit)); + } + + //----------------------------------------------------------------------- + /** + * Queries this date using the specified query. + *

    + * This queries this date using the specified query strategy object. + * The {@code TemporalQuery} object defines the logic to be used to + * obtain the result. Read the documentation of the query to understand + * what the result of this method will be. + *

    + * The result of this method is obtained by invoking the + * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the + * specified query passing {@code this} as the argument. + * + * @param the type of the result + * @param query the query to invoke, not null + * @return the query result, null may be returned (defined by the query) + * @throws DateTimeException if unable to query (defined by the query) + * @throws ArithmeticException if numeric overflow occurs (defined by the query) + */ + @SuppressWarnings("unchecked") + @Override + public default R query(TemporalQuery query) { + if (query == Queries.chrono()) { + return (R) getChrono(); + } + if (query == Queries.precision()) { + return (R) DAYS; + } + // inline TemporalAccessor.super.query(query) as an optimization + if (query == Queries.zoneId() || query == Queries.zone() || query == Queries.offset()) { + return null; + } + return query.queryFrom(this); + } + + /** + * Adjusts the specified temporal object to have the same date as this object. + *

    + * This returns a temporal object of the same observable type as the input + * with the date changed to be the same as this. + *

    + * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)} + * passing {@link ChronoField#EPOCH_DAY} as the field. + *

    + * In most cases, it is clearer to reverse the calling pattern by using + * {@link Temporal#with(TemporalAdjuster)}: + *

    +     *   // these two lines are equivalent, but the second approach is recommended
    +     *   temporal = thisLocalDate.adjustInto(temporal);
    +     *   temporal = temporal.with(thisLocalDate);
    +     * 
    + *

    + * This instance is immutable and unaffected by this method call. + * + * @param temporal the target object to be adjusted, not null + * @return the adjusted object, not null + * @throws DateTimeException if unable to make the adjustment + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public default Temporal adjustInto(Temporal temporal) { + return temporal.with(EPOCH_DAY, toEpochDay()); + } + + /** + * Calculates the period between this date and another date in + * terms of the specified unit. + *

    + * This calculates the period between two dates in terms of a single unit. + * The start and end points are {@code this} and the specified date. + * The result will be negative if the end is before the start. + * The {@code Temporal} passed to this method must be a + * {@code ChronoLocalDate} in the same chronology. + * The calculation returns a whole number, representing the number of + * complete units between the two dates. + * For example, the period in days between two dates can be calculated + * using {@code startDate.periodUntil(endDate, DAYS)}. + *

    + * This method operates in association with {@link TemporalUnit#between}. + * The result of this method is a {@code long} representing the amount of + * the specified unit. By contrast, the result of {@code between} is an + * object that can be used directly in addition/subtraction: + *

    +     *   long period = start.periodUntil(end, MONTHS);   // this method
    +     *   dateTime.plus(MONTHS.between(start, end));      // use in plus/minus
    +     * 
    + *

    + * The calculation is implemented in this method for {@link ChronoUnit}. + * The units {@code DAYS}, {@code WEEKS}, {@code MONTHS}, {@code YEARS}, + * {@code DECADES}, {@code CENTURIES}, {@code MILLENNIA} and {@code ERAS} + * should be supported by all implementations. + * Other {@code ChronoUnit} values will throw an exception. + *

    + * If the unit is not a {@code ChronoUnit}, then the result of this method + * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)} + * passing {@code this} as the first argument and the input temporal as + * the second argument. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param endDate the end date, which must be a {@code ChronoLocalDate} + * in the same chronology, not null + * @param unit the unit to measure the period in, not null + * @return the amount of the period between this date and the end date + * @throws DateTimeException if the period cannot be calculated + * @throws ArithmeticException if numeric overflow occurs + */ + @Override // override for Javadoc + public abstract long periodUntil(Temporal endDate, TemporalUnit unit); + + //----------------------------------------------------------------------- + /** + * Returns a date-time formed from this date at the specified time. + *

    + * This merges the two objects - {@code this} and the specified time - + * to form an instance of {@code ChronoLocalDateTime}. + *

    + * This instance is immutable and unaffected by this method call. + *

    + * This default implementation creates the date-time. + * + * @param localTime the local time to use, not null + * @return the local date-time formed from this date and the specified time, not null + */ + public default ChronoLocalDateTime atTime(LocalTime localTime) { + return Chrono.dateTime(this, localTime); + } + + //----------------------------------------------------------------------- + /** + * Converts this date to the Epoch Day. + *

    + * The {@link ChronoField#EPOCH_DAY Epoch Day count} is a simple + * incrementing count of days where day 0 is 1970-01-01 (ISO). + * This definition is the same for all chronologies, enabling conversion. + *

    + * This default implementation queries the {@code EPOCH_DAY} field. + * + * @return the Epoch Day equivalent to this date + */ + public default long toEpochDay() { + return getLong(EPOCH_DAY); + } + + //----------------------------------------------------------------------- + /** + * Compares this date to another date, including the chronology. + *

    + * The comparison is based first on the underlying time-line date, then + * on the chronology. + * It is "consistent with equals", as defined by {@link Comparable}. + *

    + * For example, the following is the comparator order: + *

      + *
    1. {@code 2012-12-03 (ISO)}
    2. + *
    3. {@code 2012-12-04 (ISO)}
    4. + *
    5. {@code 2555-12-04 (ThaiBuddhist)}
    6. + *
    7. {@code 2012-12-05 (ISO)}
    8. + *
    + * Values #2 and #3 represent the same date on the time-line. + * When two values represent the same date, the chronology ID is compared to distinguish them. + * This step is needed to make the ordering "consistent with equals". + *

    + * If all the date objects being compared are in the same chronology, then the + * additional chronology stage is not required and only the local date is used. + * To compare the dates of two {@code TemporalAccessor} instances, including dates + * in two different chronologies, use {@link ChronoField#EPOCH_DAY} as a comparator. + *

    + * This default implementation performs the comparison defined above. + * + * @param other the other date to compare to, not null + * @return the comparator value, negative if less, positive if greater + */ + @Override + public default int compareTo(ChronoLocalDate other) { + int cmp = Long.compare(toEpochDay(), other.toEpochDay()); + if (cmp == 0) { + cmp = getChrono().compareTo(other.getChrono()); + } + return cmp; + } + + /** + * Checks if this date is after the specified date ignoring the chronology. + *

    + * This method differs from the comparison in {@link #compareTo} in that it + * only compares the underlying date and not the chronology. + * This allows dates in different calendar systems to be compared based + * on the time-line position. + * This is equivalent to using {@code date1.toEpochDay() > date2.toEpochDay()}. + *

    + * This default implementation performs the comparison based on the epoch-day. + * + * @param other the other date to compare to, not null + * @return true if this is after the specified date + */ + public default boolean isAfter(ChronoLocalDate other) { + return this.toEpochDay() > other.toEpochDay(); + } + + /** + * Checks if this date is before the specified date ignoring the chronology. + *

    + * This method differs from the comparison in {@link #compareTo} in that it + * only compares the underlying date and not the chronology. + * This allows dates in different calendar systems to be compared based + * on the time-line position. + * This is equivalent to using {@code date1.toEpochDay() < date2.toEpochDay()}. + *

    + * This default implementation performs the comparison based on the epoch-day. + * + * @param other the other date to compare to, not null + * @return true if this is before the specified date + */ + public default boolean isBefore(ChronoLocalDate other) { + return this.toEpochDay() < other.toEpochDay(); + } + + /** + * Checks if this date is equal to the specified date ignoring the chronology. + *

    + * This method differs from the comparison in {@link #compareTo} in that it + * only compares the underlying date and not the chronology. + * This allows dates in different calendar systems to be compared based + * on the time-line position. + * This is equivalent to using {@code date1.toEpochDay() == date2.toEpochDay()}. + *

    + * This default implementation performs the comparison based on the epoch-day. + * + * @param other the other date to compare to, not null + * @return true if the underlying date is equal to the specified date + */ + public default boolean isEqual(ChronoLocalDate other) { + return this.toEpochDay() == other.toEpochDay(); + } + + //----------------------------------------------------------------------- + /** + * Checks if this date is equal to another date, including the chronology. + *

    + * Compares this date with another ensuring that the date and chronology are the same. + *

    + * To compare the dates of two {@code TemporalAccessor} instances, including dates + * in two different chronologies, use {@link ChronoField#EPOCH_DAY} as a comparator. + * + * @param obj the object to check, null returns false + * @return true if this is equal to the other date + */ + @Override + boolean equals(Object obj); + + /** + * A hash code for this date. + * + * @return a suitable hash code + */ + @Override + int hashCode(); + + //----------------------------------------------------------------------- + /** + * Outputs this date as a {@code String}. + *

    + * The output will include the full local date and the chronology ID. + * + * @return the formatted date, not null + */ + @Override + String toString(); + + /** + * Outputs this date as a {@code String} using the formatter. + *

    + * The default implementation must behave as follows: + *

    +     *  return formatter.print(this);
    +     * 
    + * + * @param formatter the formatter to use, not null + * @return the formatted date string, not null + * @throws DateTimeException if an error occurs during printing + */ + public default String toString(DateTimeFormatter formatter) { + Objects.requireNonNull(formatter, "formatter"); + return formatter.print(this); + } + +} diff --git a/jdk/src/share/classes/java/time/temporal/ChronoLocalDateTime.java b/jdk/src/share/classes/java/time/temporal/ChronoLocalDateTime.java new file mode 100644 index 00000000000..1fc12d8e07f --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/ChronoLocalDateTime.java @@ -0,0 +1,499 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import static java.time.temporal.ChronoField.EPOCH_DAY; +import static java.time.temporal.ChronoField.NANO_OF_DAY; +import static java.time.temporal.ChronoUnit.NANOS; + +import java.time.DateTimeException; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.zone.ZoneRules; +import java.util.Comparator; +import java.util.Objects; + +/** + * A date-time without a time-zone in an arbitrary chronology, intended + * for advanced globalization use cases. + *

    + * Most applications should declare method signatures, fields and variables + * as {@link LocalDateTime}, not this interface. + *

    + * A {@code ChronoLocalDateTime} is the abstract representation of a local date-time + * where the {@code Chrono chronology}, or calendar system, is pluggable. + * The date-time is defined in terms of fields expressed by {@link TemporalField}, + * where most common implementations are defined in {@link ChronoField}. + * The chronology defines how the calendar system operates and the meaning of + * the standard fields. + * + *

    When to use this interface

    + * The design of the API encourages the use of {@code LocalDateTime} rather than this + * interface, even in the case where the application needs to deal with multiple + * calendar systems. The rationale for this is explored in detail in {@link ChronoLocalDate}. + *

    + * Ensure that the discussion in {@code ChronoLocalDate} has been read and understood + * before using this interface. + * + *

    Specification for implementors

    + * This interface must be implemented with care to ensure other classes operate correctly. + * All implementations that can be instantiated must be final, immutable and thread-safe. + * Subclasses should be Serializable wherever possible. + * + * @param the chronology of this date-time + * @since 1.8 + */ +public interface ChronoLocalDateTime> + extends Temporal, TemporalAdjuster, Comparable> { + + /** + * Comparator for two {@code ChronoLocalDateTime} instances ignoring the chronology. + *

    + * This method differs from the comparison in {@link #compareTo} in that it + * only compares the underlying date and not the chronology. + * This allows dates in different calendar systems to be compared based + * on the time-line position. + * + * @see #isAfter + * @see #isBefore + * @see #isEqual + */ + Comparator> DATE_TIME_COMPARATOR = + new Comparator>() { + @Override + public int compare(ChronoLocalDateTime datetime1, ChronoLocalDateTime datetime2) { + int cmp = Long.compare(datetime1.getDate().toEpochDay(), datetime2.getDate().toEpochDay()); + if (cmp == 0) { + cmp = Long.compare(datetime1.getTime().toNanoOfDay(), datetime2.getTime().toNanoOfDay()); + } + return cmp; + } + }; + + /** + * Gets the local date part of this date-time. + *

    + * This returns a local date with the same year, month and day + * as this date-time. + * + * @return the date part of this date-time, not null + */ + ChronoLocalDate getDate() ; + + /** + * Gets the local time part of this date-time. + *

    + * This returns a local time with the same hour, minute, second and + * nanosecond as this date-time. + * + * @return the time part of this date-time, not null + */ + LocalTime getTime(); + + + //----------------------------------------------------------------------- + // override for covariant return type + /** + * {@inheritDoc} + * @throws DateTimeException {@inheritDoc} + * @throws ArithmeticException {@inheritDoc} + */ + @Override + public default ChronoLocalDateTime with(TemporalAdjuster adjuster) { + return getDate().getChrono().ensureChronoLocalDateTime(Temporal.super.with(adjuster)); + } + + /** + * {@inheritDoc} + * @throws DateTimeException {@inheritDoc} + * @throws ArithmeticException {@inheritDoc} + */ + @Override + ChronoLocalDateTime with(TemporalField field, long newValue); + + /** + * {@inheritDoc} + * @throws DateTimeException {@inheritDoc} + * @throws ArithmeticException {@inheritDoc} + */ + @Override + public default ChronoLocalDateTime plus(TemporalAdder adder) { + return getDate().getChrono().ensureChronoLocalDateTime(Temporal.super.plus(adder)); + } + + /** + * {@inheritDoc} + * @throws DateTimeException {@inheritDoc} + * @throws ArithmeticException {@inheritDoc} + */ + @Override + ChronoLocalDateTime plus(long amountToAdd, TemporalUnit unit); + + /** + * {@inheritDoc} + * @throws DateTimeException {@inheritDoc} + * @throws ArithmeticException {@inheritDoc} + */ + @Override + public default ChronoLocalDateTime minus(TemporalSubtractor subtractor) { + return getDate().getChrono().ensureChronoLocalDateTime(Temporal.super.minus(subtractor)); + } + + /** + * {@inheritDoc} + * @throws DateTimeException {@inheritDoc} + * @throws ArithmeticException {@inheritDoc} + */ + @Override + public default ChronoLocalDateTime minus(long amountToSubtract, TemporalUnit unit) { + return getDate().getChrono().ensureChronoLocalDateTime(Temporal.super.minus(amountToSubtract, unit)); + } + + //----------------------------------------------------------------------- + /** + * Queries this date-time using the specified query. + *

    + * This queries this date-time using the specified query strategy object. + * The {@code TemporalQuery} object defines the logic to be used to + * obtain the result. Read the documentation of the query to understand + * what the result of this method will be. + *

    + * The result of this method is obtained by invoking the + * {@link java.time.temporal.TemporalQuery#queryFrom(TemporalAccessor)} method on the + * specified query passing {@code this} as the argument. + * + * @param the type of the result + * @param query the query to invoke, not null + * @return the query result, null may be returned (defined by the query) + * @throws DateTimeException if unable to query (defined by the query) + * @throws ArithmeticException if numeric overflow occurs (defined by the query) + */ + @SuppressWarnings("unchecked") + @Override + public default R query(TemporalQuery query) { + if (query == Queries.chrono()) { + return (R) getDate().getChrono(); + } + if (query == Queries.precision()) { + return (R) NANOS; + } + // inline TemporalAccessor.super.query(query) as an optimization + if (query == Queries.zoneId() || query == Queries.zone() || query == Queries.offset()) { + return null; + } + return query.queryFrom(this); + } + + /** + * Adjusts the specified temporal object to have the same date and time as this object. + *

    + * This returns a temporal object of the same observable type as the input + * with the date and time changed to be the same as this. + *

    + * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)} + * twice, passing {@link ChronoField#EPOCH_DAY} and + * {@link ChronoField#NANO_OF_DAY} as the fields. + *

    + * In most cases, it is clearer to reverse the calling pattern by using + * {@link Temporal#with(TemporalAdjuster)}: + *

    +     *   // these two lines are equivalent, but the second approach is recommended
    +     *   temporal = thisLocalDateTime.adjustInto(temporal);
    +     *   temporal = temporal.with(thisLocalDateTime);
    +     * 
    + *

    + * This instance is immutable and unaffected by this method call. + * + * @param temporal the target object to be adjusted, not null + * @return the adjusted object, not null + * @throws DateTimeException if unable to make the adjustment + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public default Temporal adjustInto(Temporal temporal) { + return temporal + .with(EPOCH_DAY, getDate().toEpochDay()) + .with(NANO_OF_DAY, getTime().toNanoOfDay()); + } + + //----------------------------------------------------------------------- + /** + * Returns a zoned date-time formed from this date-time and the specified time-zone. + *

    + * This creates a zoned date-time matching the input date-time as closely as possible. + * Time-zone rules, such as daylight savings, mean that not every local date-time + * is valid for the specified zone, thus the local date-time may be adjusted. + *

    + * The local date-time is resolved to a single instant on the time-line. + * This is achieved by finding a valid offset from UTC/Greenwich for the local + * date-time as defined by the {@link ZoneRules rules} of the zone ID. + *

    + * In most cases, there is only one valid offset for a local date-time. + * In the case of an overlap, where clocks are set back, there are two valid offsets. + * This method uses the earlier offset typically corresponding to "summer". + *

    + * In the case of a gap, where clocks jump forward, there is no valid offset. + * Instead, the local date-time is adjusted to be later by the length of the gap. + * For a typical one hour daylight savings change, the local date-time will be + * moved one hour later into the offset typically corresponding to "summer". + *

    + * To obtain the later offset during an overlap, call + * {@link ChronoZonedDateTime#withLaterOffsetAtOverlap()} on the result of this method. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param zone the time-zone to use, not null + * @return the zoned date-time formed from this date-time, not null + */ + ChronoZonedDateTime atZone(ZoneId zone); + + //----------------------------------------------------------------------- + /** + * Converts this date-time to an {@code Instant}. + *

    + * This combines this local date-time and the specified offset to form + * an {@code Instant}. + *

    + * This default implementation calculates from the epoch-day of the date and the + * second-of-day of the time. + * + * @param offset the offset to use for the conversion, not null + * @return an {@code Instant} representing the same instant, not null + */ + public default Instant toInstant(ZoneOffset offset) { + return Instant.ofEpochSecond(toEpochSecond(offset), getTime().getNano()); + } + + /** + * Converts this date-time to the number of seconds from the epoch + * of 1970-01-01T00:00:00Z. + *

    + * This combines this local date-time and the specified offset to calculate the + * epoch-second value, which is the number of elapsed seconds from 1970-01-01T00:00:00Z. + * Instants on the time-line after the epoch are positive, earlier are negative. + *

    + * This default implementation calculates from the epoch-day of the date and the + * second-of-day of the time. + * + * @param offset the offset to use for the conversion, not null + * @return the number of seconds from the epoch of 1970-01-01T00:00:00Z + */ + public default long toEpochSecond(ZoneOffset offset) { + Objects.requireNonNull(offset, "offset"); + long epochDay = getDate().toEpochDay(); + long secs = epochDay * 86400 + getTime().toSecondOfDay(); + secs -= offset.getTotalSeconds(); + return secs; + } + + //----------------------------------------------------------------------- + /** + * Compares this date-time to another date-time, including the chronology. + *

    + * The comparison is based first on the underlying time-line date-time, then + * on the chronology. + * It is "consistent with equals", as defined by {@link Comparable}. + *

    + * For example, the following is the comparator order: + *

      + *
    1. {@code 2012-12-03T12:00 (ISO)}
    2. + *
    3. {@code 2012-12-04T12:00 (ISO)}
    4. + *
    5. {@code 2555-12-04T12:00 (ThaiBuddhist)}
    6. + *
    7. {@code 2012-12-05T12:00 (ISO)}
    8. + *
    + * Values #2 and #3 represent the same date-time on the time-line. + * When two values represent the same date-time, the chronology ID is compared to distinguish them. + * This step is needed to make the ordering "consistent with equals". + *

    + * If all the date-time objects being compared are in the same chronology, then the + * additional chronology stage is not required and only the local date-time is used. + *

    + * This default implementation performs the comparison defined above. + * + * @param other the other date-time to compare to, not null + * @return the comparator value, negative if less, positive if greater + */ + @Override + public default int compareTo(ChronoLocalDateTime other) { + int cmp = getDate().compareTo(other.getDate()); + if (cmp == 0) { + cmp = getTime().compareTo(other.getTime()); + if (cmp == 0) { + cmp = getDate().getChrono().compareTo(other.getDate().getChrono()); + } + } + return cmp; + } + + /** + * Checks if this date-time is after the specified date-time ignoring the chronology. + *

    + * This method differs from the comparison in {@link #compareTo} in that it + * only compares the underlying date-time and not the chronology. + * This allows dates in different calendar systems to be compared based + * on the time-line position. + *

    + * This default implementation performs the comparison based on the epoch-day + * and nano-of-day. + * + * @param other the other date-time to compare to, not null + * @return true if this is after the specified date-time + */ + public default boolean isAfter(ChronoLocalDateTime other) { + long thisEpDay = this.getDate().toEpochDay(); + long otherEpDay = other.getDate().toEpochDay(); + return thisEpDay > otherEpDay || + (thisEpDay == otherEpDay && this.getTime().toNanoOfDay() > other.getTime().toNanoOfDay()); + } + + /** + * Checks if this date-time is before the specified date-time ignoring the chronology. + *

    + * This method differs from the comparison in {@link #compareTo} in that it + * only compares the underlying date-time and not the chronology. + * This allows dates in different calendar systems to be compared based + * on the time-line position. + *

    + * This default implementation performs the comparison based on the epoch-day + * and nano-of-day. + * + * @param other the other date-time to compare to, not null + * @return true if this is before the specified date-time + */ + public default boolean isBefore(ChronoLocalDateTime other) { + long thisEpDay = this.getDate().toEpochDay(); + long otherEpDay = other.getDate().toEpochDay(); + return thisEpDay < otherEpDay || + (thisEpDay == otherEpDay && this.getTime().toNanoOfDay() < other.getTime().toNanoOfDay()); + } + + /** + * Checks if this date-time is equal to the specified date-time ignoring the chronology. + *

    + * This method differs from the comparison in {@link #compareTo} in that it + * only compares the underlying date and time and not the chronology. + * This allows date-times in different calendar systems to be compared based + * on the time-line position. + *

    + * This default implementation performs the comparison based on the epoch-day + * and nano-of-day. + * + * @param other the other date-time to compare to, not null + * @return true if the underlying date-time is equal to the specified date-time on the timeline + */ + public default boolean isEqual(ChronoLocalDateTime other) { + // Do the time check first, it is cheaper than computing EPOCH day. + return this.getTime().toNanoOfDay() == other.getTime().toNanoOfDay() && + this.getDate().toEpochDay() == other.getDate().toEpochDay(); + } + + /** + * Checks if this date-time is equal to another date-time, including the chronology. + *

    + * Compares this date-time with another ensuring that the date-time and chronology are the same. + * + * @param obj the object to check, null returns false + * @return true if this is equal to the other date + */ + @Override + boolean equals(Object obj); + + /** + * A hash code for this date-time. + * + * @return a suitable hash code + */ + @Override + int hashCode(); + + //----------------------------------------------------------------------- + /** + * Outputs this date-time as a {@code String}. + *

    + * The output will include the full local date-time and the chronology ID. + * + * @return a string representation of this date-time, not null + */ + @Override + String toString(); + + /** + * Outputs this date-time as a {@code String} using the formatter. + *

    + * The default implementation must behave as follows: + *

    +     *  return formatter.print(this);
    +     * 
    + * + * @param formatter the formatter to use, not null + * @return the formatted date-time string, not null + * @throws DateTimeException if an error occurs during printing + */ + public default String toString(DateTimeFormatter formatter) { + Objects.requireNonNull(formatter, "formatter"); + return formatter.print(this); + } +} diff --git a/jdk/src/share/classes/java/time/temporal/ChronoLocalDateTimeImpl.java b/jdk/src/share/classes/java/time/temporal/ChronoLocalDateTimeImpl.java new file mode 100644 index 00000000000..4baeec053f2 --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/ChronoLocalDateTimeImpl.java @@ -0,0 +1,426 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import static java.time.temporal.ChronoField.EPOCH_DAY; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.time.DateTimeException; +import java.time.LocalTime; +import java.time.ZoneId; +import java.util.Objects; + +/** + * A date-time without a time-zone for the calendar neutral API. + *

    + * {@code ChronoLocalDateTime} is an immutable date-time object that represents a date-time, often + * viewed as year-month-day-hour-minute-second. This object can also access other + * fields such as day-of-year, day-of-week and week-of-year. + *

    + * This class stores all date and time fields, to a precision of nanoseconds. + * It does not store or represent a time-zone. For example, the value + * "2nd October 2007 at 13:45.30.123456789" can be stored in an {@code ChronoLocalDateTime}. + * + *

    Specification for implementors

    + * This class is immutable and thread-safe. + * + * @param the chronology of this date + * @since 1.8 + */ +final class ChronoLocalDateTimeImpl> + implements ChronoLocalDateTime, Temporal, TemporalAdjuster, Serializable { + + /** + * Serialization version. + */ + private static final long serialVersionUID = 4556003607393004514L; + /** + * Hours per day. + */ + static final int HOURS_PER_DAY = 24; + /** + * Minutes per hour. + */ + static final int MINUTES_PER_HOUR = 60; + /** + * Minutes per day. + */ + static final int MINUTES_PER_DAY = MINUTES_PER_HOUR * HOURS_PER_DAY; + /** + * Seconds per minute. + */ + static final int SECONDS_PER_MINUTE = 60; + /** + * Seconds per hour. + */ + static final int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR; + /** + * Seconds per day. + */ + static final int SECONDS_PER_DAY = SECONDS_PER_HOUR * HOURS_PER_DAY; + /** + * Milliseconds per day. + */ + static final long MILLIS_PER_DAY = SECONDS_PER_DAY * 1000L; + /** + * Microseconds per day. + */ + static final long MICROS_PER_DAY = SECONDS_PER_DAY * 1000_000L; + /** + * Nanos per second. + */ + static final long NANOS_PER_SECOND = 1000_000_000L; + /** + * Nanos per minute. + */ + static final long NANOS_PER_MINUTE = NANOS_PER_SECOND * SECONDS_PER_MINUTE; + /** + * Nanos per hour. + */ + static final long NANOS_PER_HOUR = NANOS_PER_MINUTE * MINUTES_PER_HOUR; + /** + * Nanos per day. + */ + static final long NANOS_PER_DAY = NANOS_PER_HOUR * HOURS_PER_DAY; + + /** + * The date part. + */ + private final ChronoLocalDate date; + /** + * The time part. + */ + private final LocalTime time; + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code ChronoLocalDateTime} from a date and time. + * + * @param date the local date, not null + * @param time the local time, not null + * @return the local date-time, not null + */ + static > ChronoLocalDateTimeImpl of(ChronoLocalDate date, LocalTime time) { + return new ChronoLocalDateTimeImpl<>(date, time); + } + + /** + * Constructor. + * + * @param date the date part of the date-time, not null + * @param time the time part of the date-time, not null + */ + private ChronoLocalDateTimeImpl(ChronoLocalDate date, LocalTime time) { + Objects.requireNonNull(date, "date"); + Objects.requireNonNull(time, "time"); + this.date = date; + this.time = time; + } + + /** + * Returns a copy of this date-time with the new date and time, checking + * to see if a new object is in fact required. + * + * @param newDate the date of the new date-time, not null + * @param newTime the time of the new date-time, not null + * @return the date-time, not null + */ + private ChronoLocalDateTimeImpl with(Temporal newDate, LocalTime newTime) { + if (date == newDate && time == newTime) { + return this; + } + // Validate that the new Temporal is a ChronoLocalDate (and not something else) + ChronoLocalDate cd = date.getChrono().ensureChronoLocalDate(newDate); + return new ChronoLocalDateTimeImpl<>(cd, newTime); + } + + //----------------------------------------------------------------------- + @Override + public ChronoLocalDate getDate() { + return date; + } + + @Override + public LocalTime getTime() { + return time; + } + + //----------------------------------------------------------------------- + @Override + public boolean isSupported(TemporalField field) { + if (field instanceof ChronoField) { + ChronoField f = (ChronoField) field; + return f.isDateField() || f.isTimeField(); + } + return field != null && field.doIsSupported(this); + } + + @Override + public ValueRange range(TemporalField field) { + if (field instanceof ChronoField) { + ChronoField f = (ChronoField) field; + return (f.isTimeField() ? time.range(field) : date.range(field)); + } + return field.doRange(this); + } + + @Override + public int get(TemporalField field) { + if (field instanceof ChronoField) { + ChronoField f = (ChronoField) field; + return (f.isTimeField() ? time.get(field) : date.get(field)); + } + return range(field).checkValidIntValue(getLong(field), field); + } + + @Override + public long getLong(TemporalField field) { + if (field instanceof ChronoField) { + ChronoField f = (ChronoField) field; + return (f.isTimeField() ? time.getLong(field) : date.getLong(field)); + } + return field.doGet(this); + } + + //----------------------------------------------------------------------- + @SuppressWarnings("unchecked") + @Override + public ChronoLocalDateTimeImpl with(TemporalAdjuster adjuster) { + if (adjuster instanceof ChronoLocalDate) { + // The Chrono is checked in with(date,time) + return with((ChronoLocalDate) adjuster, time); + } else if (adjuster instanceof LocalTime) { + return with(date, (LocalTime) adjuster); + } else if (adjuster instanceof ChronoLocalDateTimeImpl) { + return date.getChrono().ensureChronoLocalDateTime((ChronoLocalDateTimeImpl) adjuster); + } + return date.getChrono().ensureChronoLocalDateTime((ChronoLocalDateTimeImpl) adjuster.adjustInto(this)); + } + + @Override + public ChronoLocalDateTimeImpl with(TemporalField field, long newValue) { + if (field instanceof ChronoField) { + ChronoField f = (ChronoField) field; + if (f.isTimeField()) { + return with(date, time.with(field, newValue)); + } else { + return with(date.with(field, newValue), time); + } + } + return date.getChrono().ensureChronoLocalDateTime(field.doWith(this, newValue)); + } + + //----------------------------------------------------------------------- + @Override + public ChronoLocalDateTimeImpl plus(long amountToAdd, TemporalUnit unit) { + if (unit instanceof ChronoUnit) { + ChronoUnit f = (ChronoUnit) unit; + switch (f) { + case NANOS: return plusNanos(amountToAdd); + case MICROS: return plusDays(amountToAdd / MICROS_PER_DAY).plusNanos((amountToAdd % MICROS_PER_DAY) * 1000); + case MILLIS: return plusDays(amountToAdd / MILLIS_PER_DAY).plusNanos((amountToAdd % MILLIS_PER_DAY) * 1000000); + case SECONDS: return plusSeconds(amountToAdd); + case MINUTES: return plusMinutes(amountToAdd); + case HOURS: return plusHours(amountToAdd); + case HALF_DAYS: return plusDays(amountToAdd / 256).plusHours((amountToAdd % 256) * 12); // no overflow (256 is multiple of 2) + } + return with(date.plus(amountToAdd, unit), time); + } + return date.getChrono().ensureChronoLocalDateTime(unit.doPlus(this, amountToAdd)); + } + + private ChronoLocalDateTimeImpl plusDays(long days) { + return with(date.plus(days, ChronoUnit.DAYS), time); + } + + private ChronoLocalDateTimeImpl plusHours(long hours) { + return plusWithOverflow(date, hours, 0, 0, 0); + } + + private ChronoLocalDateTimeImpl plusMinutes(long minutes) { + return plusWithOverflow(date, 0, minutes, 0, 0); + } + + ChronoLocalDateTimeImpl plusSeconds(long seconds) { + return plusWithOverflow(date, 0, 0, seconds, 0); + } + + private ChronoLocalDateTimeImpl plusNanos(long nanos) { + return plusWithOverflow(date, 0, 0, 0, nanos); + } + + //----------------------------------------------------------------------- + private ChronoLocalDateTimeImpl plusWithOverflow(ChronoLocalDate newDate, long hours, long minutes, long seconds, long nanos) { + // 9223372036854775808 long, 2147483648 int + if ((hours | minutes | seconds | nanos) == 0) { + return with(newDate, time); + } + long totDays = nanos / NANOS_PER_DAY + // max/24*60*60*1B + seconds / SECONDS_PER_DAY + // max/24*60*60 + minutes / MINUTES_PER_DAY + // max/24*60 + hours / HOURS_PER_DAY; // max/24 + long totNanos = nanos % NANOS_PER_DAY + // max 86400000000000 + (seconds % SECONDS_PER_DAY) * NANOS_PER_SECOND + // max 86400000000000 + (minutes % MINUTES_PER_DAY) * NANOS_PER_MINUTE + // max 86400000000000 + (hours % HOURS_PER_DAY) * NANOS_PER_HOUR; // max 86400000000000 + long curNoD = time.toNanoOfDay(); // max 86400000000000 + totNanos = totNanos + curNoD; // total 432000000000000 + totDays += Math.floorDiv(totNanos, NANOS_PER_DAY); + long newNoD = Math.floorMod(totNanos, NANOS_PER_DAY); + LocalTime newTime = (newNoD == curNoD ? time : LocalTime.ofNanoOfDay(newNoD)); + return with(newDate.plus(totDays, ChronoUnit.DAYS), newTime); + } + + //----------------------------------------------------------------------- + @Override + public ChronoZonedDateTime atZone(ZoneId zone) { + return ChronoZonedDateTimeImpl.ofBest(this, zone, null); + } + + //----------------------------------------------------------------------- + @Override + public long periodUntil(Temporal endDateTime, TemporalUnit unit) { + if (endDateTime instanceof ChronoLocalDateTime == false) { + throw new DateTimeException("Unable to calculate period between objects of two different types"); + } + @SuppressWarnings("unchecked") + ChronoLocalDateTime end = (ChronoLocalDateTime) endDateTime; + if (getDate().getChrono().equals(end.getDate().getChrono()) == false) { + throw new DateTimeException("Unable to calculate period between two different chronologies"); + } + if (unit instanceof ChronoUnit) { + ChronoUnit f = (ChronoUnit) unit; + if (f.isTimeUnit()) { + long amount = end.getLong(EPOCH_DAY) - date.getLong(EPOCH_DAY); + switch (f) { + case NANOS: amount = Math.multiplyExact(amount, NANOS_PER_DAY); break; + case MICROS: amount = Math.multiplyExact(amount, MICROS_PER_DAY); break; + case MILLIS: amount = Math.multiplyExact(amount, MILLIS_PER_DAY); break; + case SECONDS: amount = Math.multiplyExact(amount, SECONDS_PER_DAY); break; + case MINUTES: amount = Math.multiplyExact(amount, MINUTES_PER_DAY); break; + case HOURS: amount = Math.multiplyExact(amount, HOURS_PER_DAY); break; + case HALF_DAYS: amount = Math.multiplyExact(amount, 2); break; + } + return Math.addExact(amount, time.periodUntil(end.getTime(), unit)); + } + ChronoLocalDate endDate = end.getDate(); + if (end.getTime().isBefore(time)) { + endDate = endDate.minus(1, ChronoUnit.DAYS); + } + return date.periodUntil(endDate, unit); + } + return unit.between(this, endDateTime).getAmount(); + } + + //----------------------------------------------------------------------- + private Object writeReplace() { + return new Ser(Ser.CHRONO_LOCAL_DATE_TIME_TYPE, this); + } + + /** + * Defend against malicious streams. + * @return never + * @throws InvalidObjectException always + */ + private Object readResolve() throws ObjectStreamException { + throw new InvalidObjectException("Deserialization via serialization delegate"); + } + + void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(date); + out.writeObject(time); + } + + static ChronoLocalDateTime readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + ChronoLocalDate date = (ChronoLocalDate) in.readObject(); + LocalTime time = (LocalTime) in.readObject(); + return date.atTime(time); + } + + //----------------------------------------------------------------------- + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof ChronoLocalDateTime) { + return compareTo((ChronoLocalDateTime) obj) == 0; + } + return false; + } + + @Override + public int hashCode() { + return getDate().hashCode() ^ getTime().hashCode(); + } + + @Override + public String toString() { + return getDate().toString() + 'T' + getTime().toString(); + } + +} diff --git a/jdk/src/share/classes/java/time/temporal/ChronoUnit.java b/jdk/src/share/classes/java/time/temporal/ChronoUnit.java new file mode 100644 index 00000000000..33f1318bbf6 --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/ChronoUnit.java @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import java.time.Duration; + +/** + * A standard set of date periods units. + *

    + * This set of units provide unit-based access to manipulate a date, time or date-time. + * The standard set of units can be extended by implementing {@link TemporalUnit}. + *

    + * These units are intended to be applicable in multiple calendar systems. + * For example, most non-ISO calendar systems define units of years, months and days, + * just with slightly different rules. + * The documentation of each unit explains how it operates. + * + *

    Specification for implementors

    + * This is a final, immutable and thread-safe enum. + * + * @since 1.8 + */ +public enum ChronoUnit implements TemporalUnit { + + /** + * Unit that represents the concept of a nanosecond, the smallest supported unit of time. + * For the ISO calendar system, it is equal to the 1,000,000,000th part of the second unit. + */ + NANOS("Nanos", Duration.ofNanos(1)), + /** + * Unit that represents the concept of a microsecond. + * For the ISO calendar system, it is equal to the 1,000,000th part of the second unit. + */ + MICROS("Micros", Duration.ofNanos(1000)), + /** + * Unit that represents the concept of a millisecond. + * For the ISO calendar system, it is equal to the 1000th part of the second unit. + */ + MILLIS("Millis", Duration.ofNanos(1000_000)), + /** + * Unit that represents the concept of a second. + * For the ISO calendar system, it is equal to the second in the SI system + * of units, except around a leap-second. + */ + SECONDS("Seconds", Duration.ofSeconds(1)), + /** + * Unit that represents the concept of a minute. + * For the ISO calendar system, it is equal to 60 seconds. + */ + MINUTES("Minutes", Duration.ofSeconds(60)), + /** + * Unit that represents the concept of an hour. + * For the ISO calendar system, it is equal to 60 minutes. + */ + HOURS("Hours", Duration.ofSeconds(3600)), + /** + * Unit that represents the concept of half a day, as used in AM/PM. + * For the ISO calendar system, it is equal to 12 hours. + */ + HALF_DAYS("HalfDays", Duration.ofSeconds(43200)), + /** + * Unit that represents the concept of a day. + * For the ISO calendar system, it is the standard day from midnight to midnight. + * The estimated duration of a day is {@code 24 Hours}. + *

    + * When used with other calendar systems it must correspond to the day defined by + * the rising and setting of the Sun on Earth. It is not required that days begin + * at midnight - when converting between calendar systems, the date should be + * equivalent at midday. + */ + DAYS("Days", Duration.ofSeconds(86400)), + /** + * Unit that represents the concept of a week. + * For the ISO calendar system, it is equal to 7 days. + *

    + * When used with other calendar systems it must correspond to an integral number of days. + */ + WEEKS("Weeks", Duration.ofSeconds(7 * 86400L)), + /** + * Unit that represents the concept of a month. + * For the ISO calendar system, the length of the month varies by month-of-year. + * The estimated duration of a month is one twelfth of {@code 365.2425 Days}. + *

    + * When used with other calendar systems it must correspond to an integral number of days. + */ + MONTHS("Months", Duration.ofSeconds(31556952L / 12)), + /** + * Unit that represents the concept of a year. + * For the ISO calendar system, it is equal to 12 months. + * The estimated duration of a year is {@code 365.2425 Days}. + *

    + * When used with other calendar systems it must correspond to an integral number of days + * or months roughly equal to a year defined by the passage of the Earth around the Sun. + */ + YEARS("Years", Duration.ofSeconds(31556952L)), + /** + * Unit that represents the concept of a decade. + * For the ISO calendar system, it is equal to 10 years. + *

    + * When used with other calendar systems it must correspond to an integral number of days + * and is normally an integral number of years. + */ + DECADES("Decades", Duration.ofSeconds(31556952L * 10L)), + /** + * Unit that represents the concept of a century. + * For the ISO calendar system, it is equal to 100 years. + *

    + * When used with other calendar systems it must correspond to an integral number of days + * and is normally an integral number of years. + */ + CENTURIES("Centuries", Duration.ofSeconds(31556952L * 100L)), + /** + * Unit that represents the concept of a millennium. + * For the ISO calendar system, it is equal to 1000 years. + *

    + * When used with other calendar systems it must correspond to an integral number of days + * and is normally an integral number of years. + */ + MILLENNIA("Millennia", Duration.ofSeconds(31556952L * 1000L)), + /** + * Unit that represents the concept of an era. + * The ISO calendar system doesn't have eras thus it is impossible to add + * an era to a date or date-time. + * The estimated duration of the era is artificially defined as {@code 1,000,00,000 Years}. + *

    + * When used with other calendar systems there are no restrictions on the unit. + */ + ERAS("Eras", Duration.ofSeconds(31556952L * 1000_000_000L)), + /** + * Artificial unit that represents the concept of forever. + * This is primarily used with {@link TemporalField} to represent unbounded fields + * such as the year or era. + * The estimated duration of the era is artificially defined as the largest duration + * supported by {@code Duration}. + */ + FOREVER("Forever", Duration.ofSeconds(Long.MAX_VALUE, 999_999_999)); + + private final String name; + private final Duration duration; + + private ChronoUnit(String name, Duration estimatedDuration) { + this.name = name; + this.duration = estimatedDuration; + } + + //----------------------------------------------------------------------- + @Override + public String getName() { + return name; + } + + //----------------------------------------------------------------------- + /** + * Gets the estimated duration of this unit in the ISO calendar system. + *

    + * All of the units in this class have an estimated duration. + * Days vary due to daylight saving time, while months have different lengths. + * + * @return the estimated duration of this unit, not null + */ + @Override + public Duration getDuration() { + return duration; + } + + /** + * Checks if the duration of the unit is an estimate. + *

    + * All time units in this class are considered to be accurate, while all date + * units in this class are considered to be estimated. + *

    + * This definition ignores leap seconds, but considers that Days vary due to + * daylight saving time and months have different lengths. + * + * @return true if the duration is estimated, false if accurate + */ + @Override + public boolean isDurationEstimated() { + return isDateUnit(); + } + + //----------------------------------------------------------------------- + /** + * Checks if this unit is a date unit. + * + * @return true if a date unit, false if a time unit + */ + public boolean isDateUnit() { + return this.compareTo(DAYS) >= 0; + } + + /** + * Checks if this unit is a time unit. + * + * @return true if a time unit, false if a date unit + */ + public boolean isTimeUnit() { + return this.compareTo(DAYS) < 0; + } + + //----------------------------------------------------------------------- + @Override + public boolean isSupported(Temporal temporal) { + if (this == FOREVER) { + return false; + } + if (temporal instanceof ChronoLocalDate) { + return isDateUnit(); + } + if (temporal instanceof ChronoLocalDateTime || temporal instanceof ChronoZonedDateTime) { + return true; + } + return TemporalUnit.super.isSupported(temporal); + } + + @SuppressWarnings("unchecked") + @Override + public R doPlus(R dateTime, long periodToAdd) { + return (R) dateTime.plus(periodToAdd, this); + } + + //----------------------------------------------------------------------- + @Override + public SimplePeriod between(R dateTime1, R dateTime2) { + return new SimplePeriod(dateTime1.periodUntil(dateTime2, this), this); + } + + //----------------------------------------------------------------------- + @Override + public String toString() { + return getName(); + } + +} diff --git a/jdk/src/share/classes/java/time/temporal/ChronoZonedDateTime.java b/jdk/src/share/classes/java/time/temporal/ChronoZonedDateTime.java new file mode 100644 index 00000000000..9e172ef7fda --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/ChronoZonedDateTime.java @@ -0,0 +1,564 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import static java.time.temporal.ChronoField.INSTANT_SECONDS; +import static java.time.temporal.ChronoField.OFFSET_SECONDS; +import static java.time.temporal.ChronoUnit.NANOS; + +import java.time.DateTimeException; +import java.time.Instant; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Comparator; +import java.util.Objects; + +/** + * A date-time with a time-zone in an arbitrary chronology, + * intended for advanced globalization use cases. + *

    + * Most applications should declare method signatures, fields and variables + * as {@link ZonedDateTime}, not this interface. + *

    + * A {@code ChronoZonedDateTime} is the abstract representation of an offset date-time + * where the {@code Chrono chronology}, or calendar system, is pluggable. + * The date-time is defined in terms of fields expressed by {@link TemporalField}, + * where most common implementations are defined in {@link ChronoField}. + * The chronology defines how the calendar system operates and the meaning of + * the standard fields. + * + *

    When to use this interface

    + * The design of the API encourages the use of {@code ZonedDateTime} rather than this + * interface, even in the case where the application needs to deal with multiple + * calendar systems. The rationale for this is explored in detail in {@link ChronoLocalDate}. + *

    + * Ensure that the discussion in {@code ChronoLocalDate} has been read and understood + * before using this interface. + * + *

    Specification for implementors

    + * This interface must be implemented with care to ensure other classes operate correctly. + * All implementations that can be instantiated must be final, immutable and thread-safe. + * Subclasses should be Serializable wherever possible. + * + * @param the chronology of this date-time + * @since 1.8 + */ +public interface ChronoZonedDateTime> + extends Temporal, Comparable> { + + /** + * Comparator for two {@code ChronoZonedDateTime} instances ignoring the chronology. + *

    + * This method differs from the comparison in {@link #compareTo} in that it + * only compares the underlying date and not the chronology. + * This allows dates in different calendar systems to be compared based + * on the time-line position. + * + * @see #isAfter + * @see #isBefore + * @see #isEqual + */ + Comparator> INSTANT_COMPARATOR = new Comparator>() { + @Override + public int compare(ChronoZonedDateTime datetime1, ChronoZonedDateTime datetime2) { + int cmp = Long.compare(datetime1.toEpochSecond(), datetime2.toEpochSecond()); + if (cmp == 0) { + cmp = Long.compare(datetime1.getTime().toNanoOfDay(), datetime2.getTime().toNanoOfDay()); + } + return cmp; + } + }; + + @Override + public default ValueRange range(TemporalField field) { + if (field instanceof ChronoField) { + if (field == INSTANT_SECONDS || field == OFFSET_SECONDS) { + return field.range(); + } + return getDateTime().range(field); + } + return field.doRange(this); + } + + @Override + public default int get(TemporalField field) { + if (field instanceof ChronoField) { + switch ((ChronoField) field) { + case INSTANT_SECONDS: throw new DateTimeException("Field too large for an int: " + field); + case OFFSET_SECONDS: return getOffset().getTotalSeconds(); + } + return getDateTime().get(field); + } + return Temporal.super.get(field); + } + + @Override + public default long getLong(TemporalField field) { + if (field instanceof ChronoField) { + switch ((ChronoField) field) { + case INSTANT_SECONDS: return toEpochSecond(); + case OFFSET_SECONDS: return getOffset().getTotalSeconds(); + } + return getDateTime().getLong(field); + } + return field.doGet(this); + } + + /** + * Gets the local date part of this date-time. + *

    + * This returns a local date with the same year, month and day + * as this date-time. + * + * @return the date part of this date-time, not null + */ + public default ChronoLocalDate getDate() { + return getDateTime().getDate(); + } + + /** + * Gets the local time part of this date-time. + *

    + * This returns a local time with the same hour, minute, second and + * nanosecond as this date-time. + * + * @return the time part of this date-time, not null + */ + public default LocalTime getTime() { + return getDateTime().getTime(); + } + + /** + * Gets the local date-time part of this date-time. + *

    + * This returns a local date with the same year, month and day + * as this date-time. + * + * @return the local date-time part of this date-time, not null + */ + ChronoLocalDateTime getDateTime(); + + /** + * Gets the zone offset, such as '+01:00'. + *

    + * This is the offset of the local date-time from UTC/Greenwich. + * + * @return the zone offset, not null + */ + ZoneOffset getOffset(); + + /** + * Gets the zone ID, such as 'Europe/Paris'. + *

    + * This returns the stored time-zone id used to determine the time-zone rules. + * + * @return the zone ID, not null + */ + ZoneId getZone(); + + //----------------------------------------------------------------------- + /** + * Returns a copy of this date-time changing the zone offset to the + * earlier of the two valid offsets at a local time-line overlap. + *

    + * This method only has any effect when the local time-line overlaps, such as + * at an autumn daylight savings cutover. In this scenario, there are two + * valid offsets for the local date-time. Calling this method will return + * a zoned date-time with the earlier of the two selected. + *

    + * If this method is called when it is not an overlap, {@code this} + * is returned. + *

    + * This instance is immutable and unaffected by this method call. + * + * @return a {@code ZoneChronoDateTime} based on this date-time with the earlier offset, not null + * @throws DateTimeException if no rules can be found for the zone + * @throws DateTimeException if no rules are valid for this date-time + */ + ChronoZonedDateTime withEarlierOffsetAtOverlap(); + + /** + * Returns a copy of this date-time changing the zone offset to the + * later of the two valid offsets at a local time-line overlap. + *

    + * This method only has any effect when the local time-line overlaps, such as + * at an autumn daylight savings cutover. In this scenario, there are two + * valid offsets for the local date-time. Calling this method will return + * a zoned date-time with the later of the two selected. + *

    + * If this method is called when it is not an overlap, {@code this} + * is returned. + *

    + * This instance is immutable and unaffected by this method call. + * + * @return a {@code ChronoZonedDateTime} based on this date-time with the later offset, not null + * @throws DateTimeException if no rules can be found for the zone + * @throws DateTimeException if no rules are valid for this date-time + */ + ChronoZonedDateTime withLaterOffsetAtOverlap(); + + /** + * Returns a copy of this ZonedDateTime with a different time-zone, + * retaining the local date-time if possible. + *

    + * This method changes the time-zone and retains the local date-time. + * The local date-time is only changed if it is invalid for the new zone. + *

    + * To change the zone and adjust the local date-time, + * use {@link #withZoneSameInstant(ZoneId)}. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param zone the time-zone to change to, not null + * @return a {@code ChronoZonedDateTime} based on this date-time with the requested zone, not null + */ + ChronoZonedDateTime withZoneSameLocal(ZoneId zone); + + /** + * Returns a copy of this date-time with a different time-zone, + * retaining the instant. + *

    + * This method changes the time-zone and retains the instant. + * This normally results in a change to the local date-time. + *

    + * This method is based on retaining the same instant, thus gaps and overlaps + * in the local time-line have no effect on the result. + *

    + * To change the offset while keeping the local time, + * use {@link #withZoneSameLocal(ZoneId)}. + * + * @param zone the time-zone to change to, not null + * @return a {@code ChronoZonedDateTime} based on this date-time with the requested zone, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + ChronoZonedDateTime withZoneSameInstant(ZoneId zone); + + //----------------------------------------------------------------------- + // override for covariant return type + /** + * {@inheritDoc} + * @throws DateTimeException {@inheritDoc} + * @throws ArithmeticException {@inheritDoc} + */ + @Override + public default ChronoZonedDateTime with(TemporalAdjuster adjuster) { + return getDate().getChrono().ensureChronoZonedDateTime(Temporal.super.with(adjuster)); + } + + /** + * {@inheritDoc} + * @throws DateTimeException {@inheritDoc} + * @throws ArithmeticException {@inheritDoc} + */ + @Override + ChronoZonedDateTime with(TemporalField field, long newValue); + + /** + * {@inheritDoc} + * @throws DateTimeException {@inheritDoc} + * @throws ArithmeticException {@inheritDoc} + */ + @Override + public default ChronoZonedDateTime plus(TemporalAdder adder) { + return getDate().getChrono().ensureChronoZonedDateTime(Temporal.super.plus(adder)); + } + + /** + * {@inheritDoc} + * @throws DateTimeException {@inheritDoc} + * @throws ArithmeticException {@inheritDoc} + */ + @Override + ChronoZonedDateTime plus(long amountToAdd, TemporalUnit unit); + + /** + * {@inheritDoc} + * @throws DateTimeException {@inheritDoc} + * @throws ArithmeticException {@inheritDoc} + */ + @Override + public default ChronoZonedDateTime minus(TemporalSubtractor subtractor) { + return getDate().getChrono().ensureChronoZonedDateTime(Temporal.super.minus(subtractor)); + } + + /** + * {@inheritDoc} + * @throws DateTimeException {@inheritDoc} + * @throws ArithmeticException {@inheritDoc} + */ + @Override + public default ChronoZonedDateTime minus(long amountToSubtract, TemporalUnit unit) { + return getDate().getChrono().ensureChronoZonedDateTime(Temporal.super.minus(amountToSubtract, unit)); + } + + //----------------------------------------------------------------------- + /** + * Queries this date-time using the specified query. + *

    + * This queries this date-time using the specified query strategy object. + * The {@code TemporalQuery} object defines the logic to be used to + * obtain the result. Read the documentation of the query to understand + * what the result of this method will be. + *

    + * The result of this method is obtained by invoking the + * {@link java.time.temporal.TemporalQuery#queryFrom(TemporalAccessor)} method on the + * specified query passing {@code this} as the argument. + * + * @param the type of the result + * @param query the query to invoke, not null + * @return the query result, null may be returned (defined by the query) + * @throws DateTimeException if unable to query (defined by the query) + * @throws ArithmeticException if numeric overflow occurs (defined by the query) + */ + @SuppressWarnings("unchecked") + @Override + public default R query(TemporalQuery query) { + if (query == Queries.zone() || query == Queries.zoneId()) { + return (R) getZone(); + } else if (query == Queries.chrono()) { + return (R) getDate().getChrono(); + } else if (query == Queries.precision()) { + return (R) NANOS; + } else if (query == Queries.offset()) { + return (R) getOffset(); + } + // inline TemporalAccessor.super.query(query) as an optimization + return query.queryFrom(this); + } + + //----------------------------------------------------------------------- + /** + * Converts this date-time to an {@code Instant}. + *

    + * This combines the {@linkplain #getDateTime() local date-time} and + * {@linkplain #getOffset() offset} to form an {@code Instant}. + * + * @return an {@code Instant} representing the same instant, not null + */ + public default Instant toInstant() { + return Instant.ofEpochSecond(toEpochSecond(), getTime().getNano()); + } + + /** + * Converts this date-time to the number of seconds from the epoch + * of 1970-01-01T00:00:00Z. + *

    + * This uses the {@linkplain #getDateTime() local date-time} and + * {@linkplain #getOffset() offset} to calculate the epoch-second value, + * which is the number of elapsed seconds from 1970-01-01T00:00:00Z. + * Instants on the time-line after the epoch are positive, earlier are negative. + * + * @return the number of seconds from the epoch of 1970-01-01T00:00:00Z + */ + public default long toEpochSecond() { + long epochDay = getDate().toEpochDay(); + long secs = epochDay * 86400 + getTime().toSecondOfDay(); + secs -= getOffset().getTotalSeconds(); + return secs; + } + + //----------------------------------------------------------------------- + /** + * Compares this date-time to another date-time, including the chronology. + *

    + * The comparison is based first on the instant, then on the local date-time, + * then on the zone ID, then on the chronology. + * It is "consistent with equals", as defined by {@link Comparable}. + *

    + * If all the date-time objects being compared are in the same chronology, then the + * additional chronology stage is not required. + *

    + * This default implementation performs the comparison defined above. + * + * @param other the other date-time to compare to, not null + * @return the comparator value, negative if less, positive if greater + */ + @Override + public default int compareTo(ChronoZonedDateTime other) { + int cmp = Long.compare(toEpochSecond(), other.toEpochSecond()); + if (cmp == 0) { + cmp = getTime().getNano() - other.getTime().getNano(); + if (cmp == 0) { + cmp = getDateTime().compareTo(other.getDateTime()); + if (cmp == 0) { + cmp = getZone().getId().compareTo(other.getZone().getId()); + if (cmp == 0) { + cmp = getDate().getChrono().compareTo(other.getDate().getChrono()); + } + } + } + } + return cmp; + } + + /** + * Checks if the instant of this date-time is before that of the specified date-time. + *

    + * This method differs from the comparison in {@link #compareTo} in that it + * only compares the instant of the date-time. This is equivalent to using + * {@code dateTime1.toInstant().isBefore(dateTime2.toInstant());}. + *

    + * This default implementation performs the comparison based on the epoch-second + * and nano-of-second. + * + * @param other the other date-time to compare to, not null + * @return true if this point is before the specified date-time + */ + public default boolean isBefore(ChronoZonedDateTime other) { + long thisEpochSec = toEpochSecond(); + long otherEpochSec = other.toEpochSecond(); + return thisEpochSec < otherEpochSec || + (thisEpochSec == otherEpochSec && getTime().getNano() < other.getTime().getNano()); + } + + /** + * Checks if the instant of this date-time is after that of the specified date-time. + *

    + * This method differs from the comparison in {@link #compareTo} in that it + * only compares the instant of the date-time. This is equivalent to using + * {@code dateTime1.toInstant().isAfter(dateTime2.toInstant());}. + *

    + * This default implementation performs the comparison based on the epoch-second + * and nano-of-second. + * + * @param other the other date-time to compare to, not null + * @return true if this is after the specified date-time + */ + public default boolean isAfter(ChronoZonedDateTime other) { + long thisEpochSec = toEpochSecond(); + long otherEpochSec = other.toEpochSecond(); + return thisEpochSec > otherEpochSec || + (thisEpochSec == otherEpochSec && getTime().getNano() > other.getTime().getNano()); + } + + /** + * Checks if the instant of this date-time is equal to that of the specified date-time. + *

    + * This method differs from the comparison in {@link #compareTo} and {@link #equals} + * in that it only compares the instant of the date-time. This is equivalent to using + * {@code dateTime1.toInstant().equals(dateTime2.toInstant());}. + *

    + * This default implementation performs the comparison based on the epoch-second + * and nano-of-second. + * + * @param other the other date-time to compare to, not null + * @return true if the instant equals the instant of the specified date-time + */ + public default boolean isEqual(ChronoZonedDateTime other) { + return toEpochSecond() == other.toEpochSecond() && + getTime().getNano() == other.getTime().getNano(); + } + + //----------------------------------------------------------------------- + /** + * Checks if this date-time is equal to another date-time. + *

    + * The comparison is based on the offset date-time and the zone. + * To compare for the same instant on the time-line, use {@link #compareTo}. + * Only objects of type {@code ChronoZonedDateTime} are compared, other types return false. + * + * @param obj the object to check, null returns false + * @return true if this is equal to the other date-time + */ + @Override + boolean equals(Object obj); + + /** + * A hash code for this date-time. + * + * @return a suitable hash code + */ + @Override + int hashCode(); + + //----------------------------------------------------------------------- + /** + * Outputs this date-time as a {@code String}. + *

    + * The output will include the full zoned date-time and the chronology ID. + * + * @return a string representation of this date-time, not null + */ + @Override + String toString(); + + /** + * Outputs this date-time as a {@code String} using the formatter. + *

    + * The default implementation must behave as follows: + *

    +     *  return formatter.print(this);
    +     * 
    + * + * @param formatter the formatter to use, not null + * @return the formatted date-time string, not null + * @throws DateTimeException if an error occurs during printing + */ + public default String toString(DateTimeFormatter formatter) { + Objects.requireNonNull(formatter, "formatter"); + return formatter.print(this); + } + +} diff --git a/jdk/src/share/classes/java/time/temporal/ChronoZonedDateTimeImpl.java b/jdk/src/share/classes/java/time/temporal/ChronoZonedDateTimeImpl.java new file mode 100644 index 00000000000..27efde35d06 --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/ChronoZonedDateTimeImpl.java @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import static java.time.temporal.ChronoUnit.SECONDS; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.time.DateTimeException; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.zone.ZoneOffsetTransition; +import java.time.zone.ZoneRules; +import java.util.List; +import java.util.Objects; + +/** + * A date-time with a time-zone in the calendar neutral API. + *

    + * {@code ZoneChronoDateTime} is an immutable representation of a date-time with a time-zone. + * This class stores all date and time fields, to a precision of nanoseconds, + * as well as a time-zone and zone offset. + *

    + * The purpose of storing the time-zone is to distinguish the ambiguous case where + * the local time-line overlaps, typically as a result of the end of daylight time. + * Information about the local-time can be obtained using methods on the time-zone. + * + *

    Specification for implementors

    + * This class is immutable and thread-safe. + * + * @param the chronology of this date + * @since 1.8 + */ +final class ChronoZonedDateTimeImpl> + implements ChronoZonedDateTime, Serializable { + + /** + * Serialization version. + */ + private static final long serialVersionUID = -5261813987200935591L; + + /** + * The local date-time. + */ + private final ChronoLocalDateTimeImpl dateTime; + /** + * The zone offset. + */ + private final ZoneOffset offset; + /** + * The zone ID. + */ + private final ZoneId zone; + + //----------------------------------------------------------------------- + /** + * Obtains an instance from a local date-time using the preferred offset if possible. + * + * @param localDateTime the local date-time, not null + * @param zone the zone identifier, not null + * @param preferredOffset the zone offset, null if no preference + * @return the zoned date-time, not null + */ + static > ChronoZonedDateTime ofBest( + ChronoLocalDateTimeImpl localDateTime, ZoneId zone, ZoneOffset preferredOffset) { + Objects.requireNonNull(localDateTime, "localDateTime"); + Objects.requireNonNull(zone, "zone"); + if (zone instanceof ZoneOffset) { + return new ChronoZonedDateTimeImpl(localDateTime, (ZoneOffset) zone, zone); + } + ZoneRules rules = zone.getRules(); + LocalDateTime isoLDT = LocalDateTime.from(localDateTime); + List validOffsets = rules.getValidOffsets(isoLDT); + ZoneOffset offset; + if (validOffsets.size() == 1) { + offset = validOffsets.get(0); + } else if (validOffsets.size() == 0) { + ZoneOffsetTransition trans = rules.getTransition(isoLDT); + localDateTime = localDateTime.plusSeconds(trans.getDuration().getSeconds()); + offset = trans.getOffsetAfter(); + } else { + if (preferredOffset != null && validOffsets.contains(preferredOffset)) { + offset = preferredOffset; + } else { + offset = validOffsets.get(0); + } + } + Objects.requireNonNull(offset, "offset"); // protect against bad ZoneRules + return new ChronoZonedDateTimeImpl(localDateTime, offset, zone); + } + + /** + * Obtains an instance from an instant using the specified time-zone. + * + * @param chrono the chronology, not null + * @param instant the instant, not null + * @param zone the zone identifier, not null + * @return the zoned date-time, not null + */ + static > ChronoZonedDateTimeImpl ofInstant(Chrono chrono, Instant instant, ZoneId zone) { + ZoneRules rules = zone.getRules(); + ZoneOffset offset = rules.getOffset(instant); + Objects.requireNonNull(offset, "offset"); // protect against bad ZoneRules + LocalDateTime ldt = LocalDateTime.ofEpochSecond(instant.getEpochSecond(), instant.getNano(), offset); + ChronoLocalDateTimeImpl cldt = (ChronoLocalDateTimeImpl) chrono.localDateTime(ldt); + return new ChronoZonedDateTimeImpl(cldt, offset, zone); + } + + /** + * Obtains an instance from an {@code Instant}. + * + * @param instant the instant to create the date-time from, not null + * @param zone the time-zone to use, validated not null + * @return the zoned date-time, validated not null + */ + private ChronoZonedDateTimeImpl create(Instant instant, ZoneId zone) { + return ofInstant(getDate().getChrono(), instant, zone); + } + + //----------------------------------------------------------------------- + /** + * Constructor. + * + * @param dateTime the date-time, not null + * @param offset the zone offset, not null + * @param zone the zone ID, not null + */ + private ChronoZonedDateTimeImpl(ChronoLocalDateTimeImpl dateTime, ZoneOffset offset, ZoneId zone) { + this.dateTime = Objects.requireNonNull(dateTime, "dateTime"); + this.offset = Objects.requireNonNull(offset, "offset"); + this.zone = Objects.requireNonNull(zone, "zone"); + } + + //----------------------------------------------------------------------- + public ZoneOffset getOffset() { + return offset; + } + + @Override + public ChronoZonedDateTime withEarlierOffsetAtOverlap() { + ZoneOffsetTransition trans = getZone().getRules().getTransition(LocalDateTime.from(this)); + if (trans != null && trans.isOverlap()) { + ZoneOffset earlierOffset = trans.getOffsetBefore(); + if (earlierOffset.equals(offset) == false) { + return new ChronoZonedDateTimeImpl(dateTime, earlierOffset, zone); + } + } + return this; + } + + @Override + public ChronoZonedDateTime withLaterOffsetAtOverlap() { + ZoneOffsetTransition trans = getZone().getRules().getTransition(LocalDateTime.from(this)); + if (trans != null) { + ZoneOffset offset = trans.getOffsetAfter(); + if (offset.equals(getOffset()) == false) { + return new ChronoZonedDateTimeImpl(dateTime, offset, zone); + } + } + return this; + } + + //----------------------------------------------------------------------- + @Override + public ChronoLocalDateTime getDateTime() { + return dateTime; + } + + public ZoneId getZone() { + return zone; + } + + public ChronoZonedDateTime withZoneSameLocal(ZoneId zone) { + return ofBest(dateTime, zone, offset); + } + + @Override + public ChronoZonedDateTime withZoneSameInstant(ZoneId zone) { + Objects.requireNonNull(zone, "zone"); + return this.zone.equals(zone) ? this : create(dateTime.toInstant(offset), zone); + } + + //----------------------------------------------------------------------- + @Override + public boolean isSupported(TemporalField field) { + return field instanceof ChronoField || (field != null && field.doIsSupported(this)); + } + + //----------------------------------------------------------------------- + @Override + public ChronoZonedDateTime with(TemporalField field, long newValue) { + if (field instanceof ChronoField) { + ChronoField f = (ChronoField) field; + switch (f) { + case INSTANT_SECONDS: return plus(newValue - toEpochSecond(), SECONDS); + case OFFSET_SECONDS: { + ZoneOffset offset = ZoneOffset.ofTotalSeconds(f.checkValidIntValue(newValue)); + return create(dateTime.toInstant(offset), zone); + } + } + return ofBest(dateTime.with(field, newValue), zone, offset); + } + return getDate().getChrono().ensureChronoZonedDateTime(field.doWith(this, newValue)); + } + + //----------------------------------------------------------------------- + @Override + public ChronoZonedDateTime plus(long amountToAdd, TemporalUnit unit) { + if (unit instanceof ChronoUnit) { + return with(dateTime.plus(amountToAdd, unit)); + } + return getDate().getChrono().ensureChronoZonedDateTime(unit.doPlus(this, amountToAdd)); /// TODO: Generics replacement Risk! + } + + //----------------------------------------------------------------------- + @Override + public long periodUntil(Temporal endDateTime, TemporalUnit unit) { + if (endDateTime instanceof ChronoZonedDateTime == false) { + throw new DateTimeException("Unable to calculate period between objects of two different types"); + } + @SuppressWarnings("unchecked") + ChronoZonedDateTime end = (ChronoZonedDateTime) endDateTime; + if (getDate().getChrono().equals(end.getDate().getChrono()) == false) { + throw new DateTimeException("Unable to calculate period between two different chronologies"); + } + if (unit instanceof ChronoUnit) { + end = end.withZoneSameInstant(offset); + return dateTime.periodUntil(end.getDateTime(), unit); + } + return unit.between(this, endDateTime).getAmount(); + } + + //----------------------------------------------------------------------- + private Object writeReplace() { + return new Ser(Ser.CHRONO_ZONE_DATE_TIME_TYPE, this); + } + + /** + * Defend against malicious streams. + * @return never + * @throws InvalidObjectException always + */ + private Object readResolve() throws ObjectStreamException { + throw new InvalidObjectException("Deserialization via serialization delegate"); + } + + void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(dateTime); + out.writeObject(offset); + out.writeObject(zone); + } + + static ChronoZonedDateTime readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + ChronoLocalDateTime dateTime = (ChronoLocalDateTime) in.readObject(); + ZoneOffset offset = (ZoneOffset) in.readObject(); + ZoneId zone = (ZoneId) in.readObject(); + return dateTime.atZone(offset).withZoneSameLocal(zone); + // TODO: ZDT uses ofLenient() + } + + //------------------------------------------------------------------------- + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof ChronoZonedDateTime) { + return compareTo((ChronoZonedDateTime) obj) == 0; + } + return false; + } + + @Override + public int hashCode() { + return getDateTime().hashCode() ^ getOffset().hashCode() ^ Integer.rotateLeft(getZone().hashCode(), 3); + } + + @Override + public String toString() { + String str = getDateTime().toString() + getOffset().toString(); + if (getOffset() != getZone()) { + str += '[' + getZone().toString() + ']'; + } + return str; + } + + +} diff --git a/jdk/src/share/classes/java/time/temporal/Era.java b/jdk/src/share/classes/java/time/temporal/Era.java new file mode 100644 index 00000000000..e32de7473eb --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/Era.java @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import static java.time.temporal.ChronoField.ERA; +import static java.time.temporal.ChronoUnit.ERAS; + +import java.time.DateTimeException; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.TextStyle; +import java.util.Locale; + +/** + * An era of the time-line. + *

    + * Most calendar systems have a single epoch dividing the time-line into two eras. + * However, some calendar systems, have multiple eras, such as one for the reign + * of each leader. + * In all cases, the era is conceptually the largest division of the time-line. + * Each chronology defines the Era's that are known Eras and a + * {@link Chrono#eras Chrono.eras} to get the valid eras. + *

    + * For example, the Thai Buddhist calendar system divides time into two eras, + * before and after a single date. By contrast, the Japanese calendar system + * has one era for the reign of each Emperor. + *

    + * Instances of {@code Era} may be compared using the {@code ==} operator. + * + *

    Specification for implementors

    + * This interface must be implemented with care to ensure other classes operate correctly. + * All implementations must be singletons - final, immutable and thread-safe. + * It is recommended to use an enum whenever possible. + * + * @param the chronology of the era + * @since 1.8 + */ +public interface Era> extends TemporalAccessor, TemporalAdjuster { + + /** + * Gets the numeric value associated with the era as defined by the chronology. + * Each chronology defines the predefined Eras and methods to list the Eras + * of the chronology. + *

    + * All fields, including eras, have an associated numeric value. + * The meaning of the numeric value for era is determined by the chronology + * according to these principles: + *

      + *
    • The era in use at the epoch 1970-01-01 (ISO) has the value 1. + *
    • Later eras have sequentially higher values. + *
    • Earlier eras have sequentially lower values, which may be negative. + *

    + * + * @return the numeric era value + */ + int getValue(); + + /** + * Gets the chronology of this era. + *

    + * The {@code Chrono} represents the calendar system in use. + * This always returns the standard form of the chronology. + * + * @return the chronology, not null + */ + C getChrono(); + + //----------------------------------------------------------------------- + /** + * Obtains a date in this era given the year-of-era, month, and day. + *

    + * This era is combined with the given date fields to form a date. + * The year specified must be the year-of-era. + * Methods to create a date from the proleptic-year are on {@code Chrono}. + * This always uses the standard form of the chronology. + *

    + * This default implementation invokes the factory method on {@link Chrono}. + * + * @param yearOfEra the calendar system year-of-era + * @param month the calendar system month-of-year + * @param day the calendar system day-of-month + * @return a local date based on this era and the specified year-of-era, month and day + */ + public default ChronoLocalDate date(int yearOfEra, int month, int day) { + return getChrono().date(this, yearOfEra, month, day); + } + + + /** + * Obtains a date in this era given year-of-era and day-of-year fields. + *

    + * This era is combined with the given date fields to form a date. + * The year specified must be the year-of-era. + * Methods to create a date from the proleptic-year are on {@code Chrono}. + * This always uses the standard form of the chronology. + *

    + * This default implementation invokes the factory method on {@link Chrono}. + * + * @param yearOfEra the calendar system year-of-era + * @param dayOfYear the calendar system day-of-year + * @return a local date based on this era and the specified year-of-era and day-of-year + */ + public default ChronoLocalDate dateYearDay(int yearOfEra, int dayOfYear) { + return getChrono().dateYearDay(this, yearOfEra, dayOfYear); + } + + //----------------------------------------------------------------------- + /** + * Checks if the specified field is supported. + *

    + * This checks if this era can be queried for the specified field. + * If false, then calling the {@link #range(TemporalField) range} and + * {@link #get(TemporalField) get} methods will throw an exception. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@code ERA} field returns true. + * All other {@code ChronoField} instances will return false. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doIsSupported(TemporalAccessor)} + * passing {@code this} as the argument. + * Whether the field is supported is determined by the field. + * + * @param field the field to check, null returns false + * @return true if the field is supported on this era, false if not + */ + @Override + public default boolean isSupported(TemporalField field) { + if (field instanceof ChronoField) { + return field == ERA; + } + return field != null && field.doIsSupported(this); + } + + /** + * Gets the range of valid values for the specified field. + *

    + * The range object expresses the minimum and maximum valid values for a field. + * This era is used to enhance the accuracy of the returned range. + * If it is not possible to return the range, because the field is not supported + * or for some other reason, an exception is thrown. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@code ERA} field returns the range. + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doRange(TemporalAccessor)} + * passing {@code this} as the argument. + * Whether the range can be obtained is determined by the field. + * + * @param field the field to query the range for, not null + * @return the range of valid values for the field, not null + * @throws DateTimeException if the range for the field cannot be obtained + */ + @Override // override for Javadoc + public default ValueRange range(TemporalField field) { + return TemporalAccessor.super.range(field); + } + + /** + * Gets the value of the specified field from this era as an {@code int}. + *

    + * This queries this era for the value for the specified field. + * The returned value will always be within the valid range of values for the field. + * If it is not possible to return the value, because the field is not supported + * or for some other reason, an exception is thrown. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@code ERA} field returns the value of the era. + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doGet(TemporalAccessor)} + * passing {@code this} as the argument. Whether the value can be obtained, + * and what the value represents, is determined by the field. + * + * @param field the field to get, not null + * @return the value for the field + * @throws DateTimeException if a value for the field cannot be obtained + * @throws ArithmeticException if numeric overflow occurs + */ + @Override // override for Javadoc and performance + public default int get(TemporalField field) { + if (field == ERA) { + return getValue(); + } + return range(field).checkValidIntValue(getLong(field), field); + } + + /** + * Gets the value of the specified field from this era as a {@code long}. + *

    + * This queries this era for the value for the specified field. + * If it is not possible to return the value, because the field is not supported + * or for some other reason, an exception is thrown. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@code ERA} field returns the value of the era. + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doGet(TemporalAccessor)} + * passing {@code this} as the argument. Whether the value can be obtained, + * and what the value represents, is determined by the field. + * + * @param field the field to get, not null + * @return the value for the field + * @throws DateTimeException if a value for the field cannot be obtained + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public default long getLong(TemporalField field) { + if (field == ERA) { + return getValue(); + } else if (field instanceof ChronoField) { + throw new DateTimeException("Unsupported field: " + field.getName()); + } + return field.doGet(this); + } + + //----------------------------------------------------------------------- + /** + * Queries this era using the specified query. + *

    + * This queries this era using the specified query strategy object. + * The {@code TemporalQuery} object defines the logic to be used to + * obtain the result. Read the documentation of the query to understand + * what the result of this method will be. + *

    + * The result of this method is obtained by invoking the + * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the + * specified query passing {@code this} as the argument. + * + * @param the type of the result + * @param query the query to invoke, not null + * @return the query result, null may be returned (defined by the query) + * @throws DateTimeException if unable to query (defined by the query) + * @throws ArithmeticException if numeric overflow occurs (defined by the query) + */ + @SuppressWarnings("unchecked") + @Override + public default R query(TemporalQuery query) { + if (query == Queries.chrono()) { + return (R) getChrono(); + } else if (query == Queries.precision()) { + return (R) ERAS; + } + return TemporalAccessor.super.query(query); + } + + /** + * Adjusts the specified temporal object to have the same era as this object. + *

    + * This returns a temporal object of the same observable type as the input + * with the era changed to be the same as this. + *

    + * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)} + * passing {@link ChronoField#ERA} as the field. + *

    + * In most cases, it is clearer to reverse the calling pattern by using + * {@link Temporal#with(TemporalAdjuster)}: + *

    +     *   // these two lines are equivalent, but the second approach is recommended
    +     *   temporal = thisEra.adjustInto(temporal);
    +     *   temporal = temporal.with(thisEra);
    +     * 
    + *

    + * This instance is immutable and unaffected by this method call. + * + * @param temporal the target object to be adjusted, not null + * @return the adjusted object, not null + * @throws DateTimeException if unable to make the adjustment + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public default Temporal adjustInto(Temporal temporal) { + return temporal.with(ERA, getValue()); + } + + //----------------------------------------------------------------------- + /** + * Gets the textual representation of this era. + *

    + * This returns the textual name used to identify the era. + * The parameters control the style of the returned text and the locale. + *

    + * If no textual mapping is found then the {@link #getValue() numeric value} is returned. + *

    + * This default implementation is suitable for all implementations. + * + * @param style the style of the text required, not null + * @param locale the locale to use, not null + * @return the text value of the era, not null + */ + public default String getText(TextStyle style, Locale locale) { + return new DateTimeFormatterBuilder().appendText(ERA, style).toFormatter(locale).print(this); + } + + // NOTE: methods to convert year-of-era/proleptic-year cannot be here as they may depend on month/day (Japanese) +} diff --git a/jdk/src/share/classes/java/time/temporal/ISOChrono.java b/jdk/src/share/classes/java/time/temporal/ISOChrono.java new file mode 100644 index 00000000000..c290e4e1dbb --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/ISOChrono.java @@ -0,0 +1,399 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import java.io.Serializable; +import java.time.Clock; +import java.time.DateTimeException; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.Objects; + +/** + * The ISO calendar system. + *

    + * This chronology defines the rules of the ISO calendar system. + * This calendar system is based on the ISO-8601 standard, which is the + * de facto world calendar. + *

    + * The fields are defined as follows: + *

      + *
    • era - There are two eras, 'Current Era' (CE) and 'Before Current Era' (BCE). + *
    • year-of-era - The year-of-era is the same as the proleptic-year for the current CE era. + * For the BCE era before the ISO epoch the year increases from 1 upwards as time goes backwards. + *
    • proleptic-year - The proleptic year is the same as the year-of-era for the + * current era. For the previous era, years have zero, then negative values. + *
    • month-of-year - There are 12 months in an ISO year, numbered from 1 to 12. + *
    • day-of-month - There are between 28 and 31 days in each of the ISO month, numbered from 1 to 31. + * Months 4, 6, 9 and 11 have 30 days, Months 1, 3, 5, 7, 8, 10 and 12 have 31 days. + * Month 2 has 28 days, or 29 in a leap year. + *
    • day-of-year - There are 365 days in a standard ISO year and 366 in a leap year. + * The days are numbered from 1 to 365 or 1 to 366. + *
    • leap-year - Leap years occur every 4 years, except where the year is divisble by 100 and not divisble by 400. + *

    + * + *

    Specification for implementors

    + * This class is immutable and thread-safe. + * + * @since 1.8 + */ +public final class ISOChrono extends Chrono implements Serializable { + + /** + * Singleton instance of the ISO chronology. + */ + public static final ISOChrono INSTANCE = new ISOChrono(); + /** + * The singleton instance for the era BCE - 'Before Current Era'. + * The 'ISO' part of the name emphasizes that this differs from the BCE + * era in the Gregorian calendar system. + * This has the numeric value of {@code 0}. + */ + public static final Era ERA_BCE = ISOEra.BCE; + /** + * The singleton instance for the era CE - 'Current Era'. + * The 'ISO' part of the name emphasizes that this differs from the CE + * era in the Gregorian calendar system. + * This has the numeric value of {@code 1}. + */ + public static final Era ERA_CE = ISOEra.CE; + + /** + * Serialization version. + */ + private static final long serialVersionUID = -1440403870442975015L; + + /** + * Restricted constructor. + */ + private ISOChrono() { + } + + /** + * Resolve singleton. + * + * @return the singleton instance, not null + */ + private Object readResolve() { + return INSTANCE; + } + + //----------------------------------------------------------------------- + /** + * Gets the ID of the chronology - 'ISO'. + *

    + * The ID uniquely identifies the {@code Chrono}. + * It can be used to lookup the {@code Chrono} using {@link #of(String)}. + * + * @return the chronology ID - 'ISO' + * @see #getCalendarType() + */ + @Override + public String getId() { + return "ISO"; + } + + /** + * Gets the calendar type of the underlying calendar system - 'iso8601'. + *

    + * The calendar type is an identifier defined by the + * Unicode Locale Data Markup Language (LDML) specification. + * It can be used to lookup the {@code Chrono} using {@link #of(String)}. + * It can also be used as part of a locale, accessible via + * {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'. + * + * @return the calendar system type - 'iso8601' + * @see #getId() + */ + @Override + public String getCalendarType() { + return "iso8601"; + } + + //----------------------------------------------------------------------- + /** + * Obtains an ISO local date from the era, year-of-era, month-of-year + * and day-of-month fields. + * + * @param era the ISO era, not null + * @param yearOfEra the ISO year-of-era + * @param month the ISO month-of-year + * @param dayOfMonth the ISO day-of-month + * @return the ISO local date, not null + * @throws DateTimeException if unable to create the date + */ + @Override // override with covariant return type + public LocalDate date(Era era, int yearOfEra, int month, int dayOfMonth) { + return date(prolepticYear(era, yearOfEra), month, dayOfMonth); + } + + /** + * Obtains an ISO local date from the proleptic-year, month-of-year + * and day-of-month fields. + *

    + * This is equivalent to {@link LocalDate#of(int, int, int)}. + * + * @param prolepticYear the ISO proleptic-year + * @param month the ISO month-of-year + * @param dayOfMonth the ISO day-of-month + * @return the ISO local date, not null + * @throws DateTimeException if unable to create the date + */ + @Override // override with covariant return type + public LocalDate date(int prolepticYear, int month, int dayOfMonth) { + return LocalDate.of(prolepticYear, month, dayOfMonth); + } + + /** + * Obtains an ISO local date from the era, year-of-era and day-of-year fields. + * + * @param era the ISO era, not null + * @param yearOfEra the ISO year-of-era + * @param dayOfYear the ISO day-of-year + * @return the ISO local date, not null + * @throws DateTimeException if unable to create the date + */ + @Override // override with covariant return type + public LocalDate dateYearDay(Era era, int yearOfEra, int dayOfYear) { + return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear); + } + + /** + * Obtains an ISO local date from the proleptic-year and day-of-year fields. + *

    + * This is equivalent to {@link LocalDate#ofYearDay(int, int)}. + * + * @param prolepticYear the ISO proleptic-year + * @param dayOfYear the ISO day-of-year + * @return the ISO local date, not null + * @throws DateTimeException if unable to create the date + */ + @Override // override with covariant return type + public LocalDate dateYearDay(int prolepticYear, int dayOfYear) { + return LocalDate.ofYearDay(prolepticYear, dayOfYear); + } + + //----------------------------------------------------------------------- + /** + * Obtains an ISO local date from another date-time object. + *

    + * This is equivalent to {@link LocalDate#from(TemporalAccessor)}. + * + * @param temporal the date-time object to convert, not null + * @return the ISO local date, not null + * @throws DateTimeException if unable to create the date + */ + @Override // override with covariant return type + public LocalDate date(TemporalAccessor temporal) { + return LocalDate.from(temporal); + } + + /** + * Obtains an ISO local date-time from another date-time object. + *

    + * This is equivalent to {@link LocalDateTime#from(TemporalAccessor)}. + * + * @param temporal the date-time object to convert, not null + * @return the ISO local date-time, not null + * @throws DateTimeException if unable to create the date-time + */ + @Override // override with covariant return type + public LocalDateTime localDateTime(TemporalAccessor temporal) { + return LocalDateTime.from(temporal); + } + + /** + * Obtains an ISO zoned date-time from another date-time object. + *

    + * This is equivalent to {@link ZonedDateTime#from(TemporalAccessor)}. + * + * @param temporal the date-time object to convert, not null + * @return the ISO zoned date-time, not null + * @throws DateTimeException if unable to create the date-time + */ + @Override // override with covariant return type + public ZonedDateTime zonedDateTime(TemporalAccessor temporal) { + return ZonedDateTime.from(temporal); + } + + /** + * Obtains an ISO zoned date-time in this chronology from an {@code Instant}. + *

    + * This is equivalent to {@link ZonedDateTime#ofInstant(Instant, ZoneId)}. + * + * @param instant the instant to create the date-time from, not null + * @param zone the time-zone, not null + * @return the zoned date-time, not null + * @throws DateTimeException if the result exceeds the supported range + */ + public ZonedDateTime zonedDateTime(Instant instant, ZoneId zone) { + return ZonedDateTime.ofInstant(instant, zone); + } + + //----------------------------------------------------------------------- + /** + * Obtains the current ISO local date from the system clock in the default time-zone. + *

    + * This will query the {@link Clock#systemDefaultZone() system clock} in the default + * time-zone to obtain the current date. + *

    + * Using this method will prevent the ability to use an alternate clock for testing + * because the clock is hard-coded. + * + * @return the current ISO local date using the system clock and default time-zone, not null + * @throws DateTimeException if unable to create the date + */ + @Override // override with covariant return type + public LocalDate dateNow() { + return dateNow(Clock.systemDefaultZone()); + } + + /** + * Obtains the current ISO local date from the system clock in the specified time-zone. + *

    + * This will query the {@link Clock#system(ZoneId) system clock} to obtain the current date. + * Specifying the time-zone avoids dependence on the default time-zone. + *

    + * Using this method will prevent the ability to use an alternate clock for testing + * because the clock is hard-coded. + * + * @return the current ISO local date using the system clock, not null + * @throws DateTimeException if unable to create the date + */ + @Override // override with covariant return type + public LocalDate dateNow(ZoneId zone) { + return dateNow(Clock.system(zone)); + } + + /** + * Obtains the current ISO local date from the specified clock. + *

    + * This will query the specified clock to obtain the current date - today. + * Using this method allows the use of an alternate clock for testing. + * The alternate clock may be introduced using {@link Clock dependency injection}. + * + * @param clock the clock to use, not null + * @return the current ISO local date, not null + * @throws DateTimeException if unable to create the date + */ + @Override // override with covariant return type + public LocalDate dateNow(Clock clock) { + Objects.requireNonNull(clock, "clock"); + return date(LocalDate.now(clock)); + } + + //----------------------------------------------------------------------- + /** + * Checks if the year is a leap year, according to the ISO proleptic + * calendar system rules. + *

    + * This method applies the current rules for leap years across the whole time-line. + * In general, a year is a leap year if it is divisible by four without + * remainder. However, years divisible by 100, are not leap years, with + * the exception of years divisible by 400 which are. + *

    + * For example, 1904 is a leap year it is divisible by 4. + * 1900 was not a leap year as it is divisible by 100, however 2000 was a + * leap year as it is divisible by 400. + *

    + * The calculation is proleptic - applying the same rules into the far future and far past. + * This is historically inaccurate, but is correct for the ISO-8601 standard. + * + * @param prolepticYear the ISO proleptic year to check + * @return true if the year is leap, false otherwise + */ + @Override + public boolean isLeapYear(long prolepticYear) { + return ((prolepticYear & 3) == 0) && ((prolepticYear % 100) != 0 || (prolepticYear % 400) == 0); + } + + @Override + public int prolepticYear(Era era, int yearOfEra) { + if (era instanceof ISOEra == false) { + throw new DateTimeException("Era must be ISOEra"); + } + return (era == ISOEra.CE ? yearOfEra : 1 - yearOfEra); + } + + @Override + public Era eraOf(int eraValue) { + return ISOEra.of(eraValue); + } + + @Override + public List> eras() { + return Arrays.>asList(ISOEra.values()); + } + + //----------------------------------------------------------------------- + @Override + public ValueRange range(ChronoField field) { + return field.range(); + } + +} diff --git a/jdk/src/share/classes/java/time/temporal/ISOEra.java b/jdk/src/share/classes/java/time/temporal/ISOEra.java new file mode 100644 index 00000000000..21009f6b183 --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/ISOEra.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import static java.time.temporal.ChronoField.ERA; + +import java.time.DateTimeException; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.TextStyle; +import java.util.Locale; + +/** + * An era in the ISO calendar system. + *

    + * The ISO-8601 standard does not define eras. + * A definition has therefore been created with two eras - 'Current era' (CE) for + * years from 0001-01-01 (ISO) and 'Before current era' (BCE) for years before that. + *

    + * Do not use {@code ordinal()} to obtain the numeric representation of {@code ISOEra}. + * Use {@code getValue()} instead. + * + *

    Specification for implementors

    + * This is an immutable and thread-safe enum. + * + * @since 1.8 + */ +enum ISOEra implements Era { + + /** + * The singleton instance for the era BCE, 'Before Current Era'. + * The 'ISO' part of the name emphasizes that this differs from the BCE + * era in the Gregorian calendar system. + * This has the numeric value of {@code 0}. + */ + BCE, + /** + * The singleton instance for the era CE, 'Current Era'. + * The 'ISO' part of the name emphasizes that this differs from the CE + * era in the Gregorian calendar system. + * This has the numeric value of {@code 1}. + */ + CE; + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code ISOEra} from an {@code int} value. + *

    + * {@code ISOEra} is an enum representing the ISO eras of BCE/CE. + * This factory allows the enum to be obtained from the {@code int} value. + * + * @param era the BCE/CE value to represent, from 0 (BCE) to 1 (CE) + * @return the era singleton, not null + * @throws DateTimeException if the value is invalid + */ + public static ISOEra of(int era) { + switch (era) { + case 0: + return BCE; + case 1: + return CE; + default: + throw new DateTimeException("Invalid era: " + era); + } + } + + //----------------------------------------------------------------------- + /** + * Gets the numeric era {@code int} value. + *

    + * The era BCE has the value 0, while the era CE has the value 1. + * + * @return the era value, from 0 (BCE) to 1 (CE) + */ + @Override + public int getValue() { + return ordinal(); + } + + @Override + public ISOChrono getChrono() { + return ISOChrono.INSTANCE; + } + + // JDK8 default methods: + //----------------------------------------------------------------------- + @Override + public ChronoLocalDate date(int year, int month, int day) { + return getChrono().date(this, year, month, day); + } + + @Override + public ChronoLocalDate dateYearDay(int year, int dayOfYear) { + return getChrono().dateYearDay(this, year, dayOfYear); + } + + //----------------------------------------------------------------------- + @Override + public boolean isSupported(TemporalField field) { + if (field instanceof ChronoField) { + return field == ERA; + } + return field != null && field.doIsSupported(this); + } + + @Override + public ValueRange range(TemporalField field) { + if (field == ERA) { + return field.range(); + } else if (field instanceof ChronoField) { + throw new DateTimeException("Unsupported field: " + field.getName()); + } + return field.doRange(this); + } + + @Override + public int get(TemporalField field) { + if (field == ERA) { + return getValue(); + } + return range(field).checkValidIntValue(getLong(field), field); + } + + @Override + public long getLong(TemporalField field) { + if (field == ERA) { + return getValue(); + } else if (field instanceof ChronoField) { + throw new DateTimeException("Unsupported field: " + field.getName()); + } + return field.doGet(this); + } + + //------------------------------------------------------------------------- + @Override + public Temporal adjustInto(Temporal temporal) { + return temporal.with(ERA, getValue()); + } + + //----------------------------------------------------------------------- + @Override + public String getText(TextStyle style, Locale locale) { + return new DateTimeFormatterBuilder().appendText(ERA, style).toFormatter(locale).print(this); + } + +} diff --git a/jdk/src/share/classes/java/time/temporal/ISOFields.java b/jdk/src/share/classes/java/time/temporal/ISOFields.java new file mode 100644 index 00000000000..548d1d5e31b --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/ISOFields.java @@ -0,0 +1,564 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2011-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import static java.time.DayOfWeek.THURSDAY; +import static java.time.DayOfWeek.WEDNESDAY; +import static java.time.temporal.ChronoField.DAY_OF_WEEK; +import static java.time.temporal.ChronoField.DAY_OF_YEAR; +import static java.time.temporal.ChronoField.EPOCH_DAY; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.YEAR; +import static java.time.temporal.ChronoUnit.DAYS; +import static java.time.temporal.ChronoUnit.FOREVER; +import static java.time.temporal.ChronoUnit.MONTHS; +import static java.time.temporal.ChronoUnit.WEEKS; +import static java.time.temporal.ChronoUnit.YEARS; + +import java.time.DateTimeException; +import java.time.Duration; +import java.time.LocalDate; +import java.time.format.DateTimeBuilder; + +/** + * Fields and units specific to the ISO-8601 calendar system, + * including quarter-of-year and week-based-year. + *

    + * This class defines fields and units that are specific to the ISO calendar system. + * + *

    Quarter of year

    + * The ISO-8601 standard is based on the standard civic 12 month year. + * This is commonly divided into four quarters, often abbreviated as Q1, Q2, Q3 and Q4. + *

    + * January, February and March are in Q1. + * April, May and June are in Q2. + * July, August and September are in Q3. + * October, November and December are in Q4. + *

    + * The complete date is expressed using three fields: + *

      + *
    • {@link #DAY_OF_QUARTER DAY_OF_QUARTER} - the day within the quarter, from 1 to 90, 91 or 92 + *
    • {@link #QUARTER_OF_YEAR QUARTER_OF_YEAR} - the week within the week-based-year + *
    • {@link ChronoField#YEAR YEAR} - the standard ISO year + *

    + * + *

    Week based years

    + * The ISO-8601 standard was originally intended as a data interchange format, + * defining a string format for dates and times. However, it also defines an + * alternate way of expressing the date, based on the concept of week-based-year. + *

    + * The date is expressed using three fields: + *

      + *
    • {@link ChronoField#DAY_OF_WEEK DAY_OF_WEEK} - the standard field defining the + * day-of-week from Monday (1) to Sunday (7) + *
    • {@link #WEEK_OF_WEEK_BASED_YEAR} - the week within the week-based-year + *
    • {@link #WEEK_BASED_YEAR WEEK_BASED_YEAR} - the week-based-year + *

    + * The week-based-year itself is defined relative to the standard ISO proleptic year. + * It differs from the standard year in that it always starts on a Monday. + *

    + * The first week of a week-based-year is the first Monday-based week of the standard + * ISO year that has at least 4 days in the new year. + *

      + *
    • If January 1st is Monday then week 1 starts on January 1st + *
    • If January 1st is Tuesday then week 1 starts on December 31st of the previous standard year + *
    • If January 1st is Wednesday then week 1 starts on December 30th of the previous standard year + *
    • If January 1st is Thursday then week 1 starts on December 29th of the previous standard year + *
    • If January 1st is Friday then week 1 starts on January 4th + *
    • If January 1st is Saturday then week 1 starts on January 3rd + *
    • If January 1st is Sunday then week 1 starts on January 2nd + *

    + * There are 52 weeks in most week-based years, however on occasion there are 53 weeks. + *

    + * For example: + *

    + * + * + * + * + * + * + * + * + * + *
    Examples of Week based Years
    DateDay-of-weekField values
    2008-12-28SundayWeek 52 of week-based-year 2008
    2008-12-29MondayWeek 1 of week-based-year 2009
    2008-12-31WednesdayWeek 1 of week-based-year 2009
    2009-01-01ThursdayWeek 1 of week-based-year 2009
    2009-01-04SundayWeek 1 of week-based-year 2009
    2009-01-05MondayWeek 2 of week-based-year 2009
    + * + *

    Specification for implementors

    + *

    + * This class is immutable and thread-safe. + * + * @since 1.8 + */ +public final class ISOFields { + + /** + * The field that represents the day-of-quarter. + *

    + * This field allows the day-of-quarter value to be queried and set. + * The day-of-quarter has values from 1 to 90 in Q1 of a standard year, from 1 to 91 + * in Q1 of a leap year, from 1 to 91 in Q2 and from 1 to 92 in Q3 and Q4. + *

    + * The day-of-quarter can only be calculated if the day-of-year, month-of-year and year + * are available. + *

    + * When setting this field, the value is allowed to be partially lenient, taking any + * value from 1 to 92. If the quarter has less than 92 days, then day 92, and + * potentially day 91, is in the following quarter. + *

    + * This unit is an immutable and thread-safe singleton. + */ + public static final TemporalField DAY_OF_QUARTER = Field.DAY_OF_QUARTER; + /** + * The field that represents the quarter-of-year. + *

    + * This field allows the quarter-of-year value to be queried and set. + * The quarter-of-year has values from 1 to 4. + *

    + * The day-of-quarter can only be calculated if the month-of-year is available. + *

    + * This unit is an immutable and thread-safe singleton. + */ + public static final TemporalField QUARTER_OF_YEAR = Field.QUARTER_OF_YEAR; + /** + * The field that represents the week-of-week-based-year. + *

    + * This field allows the week of the week-based-year value to be queried and set. + *

    + * This unit is an immutable and thread-safe singleton. + */ + public static final TemporalField WEEK_OF_WEEK_BASED_YEAR = Field.WEEK_OF_WEEK_BASED_YEAR; + /** + * The field that represents the week-based-year. + *

    + * This field allows the week-based-year value to be queried and set. + *

    + * This unit is an immutable and thread-safe singleton. + */ + public static final TemporalField WEEK_BASED_YEAR = Field.WEEK_BASED_YEAR; + /** + * The unit that represents week-based-years for the purpose of addition and subtraction. + *

    + * This allows a number of week-based-years to be added to, or subtracted from, a date. + * The unit is equal to either 52 or 53 weeks. + * The estimated duration of a week-based-year is the same as that of a standard ISO + * year at {@code 365.2425 Days}. + *

    + * The rules for addition add the number of week-based-years to the existing value + * for the week-based-year field. If the resulting week-based-year only has 52 weeks, + * then the date will be in week 1 of the following week-based-year. + *

    + * This unit is an immutable and thread-safe singleton. + */ + public static final TemporalUnit WEEK_BASED_YEARS = Unit.WEEK_BASED_YEARS; + /** + * Unit that represents the concept of a quarter-year. + * For the ISO calendar system, it is equal to 3 months. + * The estimated duration of a quarter-year is one quarter of {@code 365.2425 Days}. + *

    + * This unit is an immutable and thread-safe singleton. + */ + public static final TemporalUnit QUARTER_YEARS = Unit.QUARTER_YEARS; + + /** + * Restricted constructor. + */ + private ISOFields() { + throw new AssertionError("Not instantiable"); + } + + //----------------------------------------------------------------------- + /** + * Implementation of the field. + */ + private static enum Field implements TemporalField { + DAY_OF_QUARTER { + @Override + public String getName() { + return "DayOfQuarter"; + } + @Override + public TemporalUnit getBaseUnit() { + return DAYS; + } + @Override + public TemporalUnit getRangeUnit() { + return QUARTER_YEARS; + } + @Override + public ValueRange range() { + return ValueRange.of(1, 90, 92); + } + @Override + public boolean doIsSupported(TemporalAccessor temporal) { + return temporal.isSupported(DAY_OF_YEAR) && temporal.isSupported(MONTH_OF_YEAR) && + temporal.isSupported(YEAR) && Chrono.from(temporal).equals(ISOChrono.INSTANCE); + } + @Override + public ValueRange doRange(TemporalAccessor temporal) { + if (doIsSupported(temporal) == false) { + throw new DateTimeException("Unsupported field: DayOfQuarter"); + } + long qoy = temporal.getLong(QUARTER_OF_YEAR); + if (qoy == 1) { + long year = temporal.getLong(YEAR); + return (ISOChrono.INSTANCE.isLeapYear(year) ? ValueRange.of(1, 91) : ValueRange.of(1, 90)); + } else if (qoy == 2) { + return ValueRange.of(1, 91); + } else if (qoy == 3 || qoy == 4) { + return ValueRange.of(1, 92); + } // else value not from 1 to 4, so drop through + return range(); + } + @Override + public long doGet(TemporalAccessor temporal) { + if (doIsSupported(temporal) == false) { + throw new DateTimeException("Unsupported field: DayOfQuarter"); + } + int doy = temporal.get(DAY_OF_YEAR); + int moy = temporal.get(MONTH_OF_YEAR); + long year = temporal.getLong(YEAR); + return doy - QUARTER_DAYS[((moy - 1) / 3) + (ISOChrono.INSTANCE.isLeapYear(year) ? 4 : 0)]; + } + @Override + public R doWith(R temporal, long newValue) { + long curValue = doGet(temporal); + range().checkValidValue(newValue, this); + return (R) temporal.with(DAY_OF_YEAR, temporal.getLong(DAY_OF_YEAR) + (newValue - curValue)); + } + }, + QUARTER_OF_YEAR { + @Override + public String getName() { + return "QuarterOfYear"; + } + @Override + public TemporalUnit getBaseUnit() { + return QUARTER_YEARS; + } + @Override + public TemporalUnit getRangeUnit() { + return YEARS; + } + @Override + public ValueRange range() { + return ValueRange.of(1, 4); + } + @Override + public boolean doIsSupported(TemporalAccessor temporal) { + return temporal.isSupported(MONTH_OF_YEAR) && Chrono.from(temporal).equals(ISOChrono.INSTANCE); + } + @Override + public long doGet(TemporalAccessor temporal) { + if (doIsSupported(temporal) == false) { + throw new DateTimeException("Unsupported field: DayOfQuarter"); + } + long moy = temporal.getLong(MONTH_OF_YEAR); + return ((moy + 2) / 3); + } + @Override + public R doWith(R temporal, long newValue) { + long curValue = doGet(temporal); + range().checkValidValue(newValue, this); + return (R) temporal.with(MONTH_OF_YEAR, temporal.getLong(MONTH_OF_YEAR) + (newValue - curValue) * 3); + } + @Override + public boolean resolve(DateTimeBuilder builder, long value) { + Long[] values = builder.queryFieldValues(YEAR, QUARTER_OF_YEAR, DAY_OF_QUARTER); + if (values[0] != null && values[1] != null && values[2] != null) { + int y = YEAR.range().checkValidIntValue(values[0], YEAR); + int qoy = QUARTER_OF_YEAR.range().checkValidIntValue(values[1], QUARTER_OF_YEAR); + int doq = DAY_OF_QUARTER.range().checkValidIntValue(values[2], DAY_OF_QUARTER); + LocalDate date = LocalDate.of(y, ((qoy - 1) * 3) + 1, 1).plusDays(doq - 1); + builder.addFieldValue(EPOCH_DAY, date.toEpochDay()); + builder.removeFieldValues(QUARTER_OF_YEAR, DAY_OF_QUARTER); + } + return false; + } + }, + WEEK_OF_WEEK_BASED_YEAR { + @Override + public String getName() { + return "WeekOfWeekBasedYear"; + } + @Override + public TemporalUnit getBaseUnit() { + return WEEKS; + } + @Override + public TemporalUnit getRangeUnit() { + return WEEK_BASED_YEARS; + } + @Override + public ValueRange range() { + return ValueRange.of(1, 52, 53); + } + @Override + public boolean doIsSupported(TemporalAccessor temporal) { + return temporal.isSupported(EPOCH_DAY); + } + @Override + public ValueRange doRange(TemporalAccessor temporal) { + return getWeekRange(LocalDate.from(temporal)); + } + @Override + public long doGet(TemporalAccessor temporal) { + return getWeek(LocalDate.from(temporal)); + } + @Override + public R doWith(R temporal, long newValue) { + ValueRange.of(1, 53).checkValidValue(newValue, this); + return (R) temporal.plus(Math.subtractExact(newValue, doGet(temporal)), WEEKS); + } + }, + WEEK_BASED_YEAR { + @Override + public String getName() { + return "WeekBasedYear"; + } + @Override + public TemporalUnit getBaseUnit() { + return WEEK_BASED_YEARS; + } + @Override + public TemporalUnit getRangeUnit() { + return FOREVER; + } + @Override + public ValueRange range() { + return YEAR.range(); + } + @Override + public boolean doIsSupported(TemporalAccessor temporal) { + return temporal.isSupported(EPOCH_DAY); + } + @Override + public long doGet(TemporalAccessor temporal) { + return getWeekBasedYear(LocalDate.from(temporal)); + } + @Override + public R doWith(R temporal, long newValue) { + int newVal = range().checkValidIntValue(newValue, WEEK_BASED_YEAR); + LocalDate date = LocalDate.from(temporal); + int week = getWeek(date); + date = date.withDayOfYear(180).withYear(newVal).with(WEEK_OF_WEEK_BASED_YEAR, week); + return (R) date.with(date); + } + @Override + public boolean resolve(DateTimeBuilder builder, long value) { + Long[] values = builder.queryFieldValues(WEEK_BASED_YEAR, WEEK_OF_WEEK_BASED_YEAR, DAY_OF_WEEK); + if (values[0] != null && values[1] != null && values[2] != null) { + int wby = WEEK_BASED_YEAR.range().checkValidIntValue(values[0], WEEK_BASED_YEAR); + int week = WEEK_OF_WEEK_BASED_YEAR.range().checkValidIntValue(values[1], WEEK_OF_WEEK_BASED_YEAR); + int dow = DAY_OF_WEEK.range().checkValidIntValue(values[2], DAY_OF_WEEK); + LocalDate date = LocalDate.of(wby, 2, 1).with(WEEK_OF_WEEK_BASED_YEAR, week).with(DAY_OF_WEEK, dow); + builder.addFieldValue(EPOCH_DAY, date.toEpochDay()); + builder.removeFieldValues(WEEK_BASED_YEAR, WEEK_OF_WEEK_BASED_YEAR, DAY_OF_WEEK); + } + return false; + } + }; + + @Override + public ValueRange doRange(TemporalAccessor temporal) { + return range(); + } + + @Override + public boolean resolve(DateTimeBuilder builder, long value) { + return false; + } + + @Override + public String toString() { + return getName(); + } + + //------------------------------------------------------------------------- + private static final int[] QUARTER_DAYS = {0, 90, 181, 273, 0, 91, 182, 274}; + + private static ValueRange getWeekRange(LocalDate date) { + int wby = getWeekBasedYear(date); + date = date.withDayOfYear(1).withYear(wby); + // 53 weeks if standard year starts on Thursday, or Wed in a leap year + if (date.getDayOfWeek() == THURSDAY || (date.getDayOfWeek() == WEDNESDAY && date.isLeapYear())) { + return ValueRange.of(1, 53); + } + return ValueRange.of(1, 52); + } + + private static int getWeek(LocalDate date) { + int dow0 = date.getDayOfWeek().ordinal(); + int doy0 = date.getDayOfYear() - 1; + int doyThu0 = doy0 + (3 - dow0); // adjust to mid-week Thursday (which is 3 indexed from zero) + int alignedWeek = doyThu0 / 7; + int firstThuDoy0 = doyThu0 - (alignedWeek * 7); + int firstMonDoy0 = firstThuDoy0 - 3; + if (firstMonDoy0 < -3) { + firstMonDoy0 += 7; + } + if (doy0 < firstMonDoy0) { + return (int) getWeekRange(date.withDayOfYear(180).minusYears(1)).getMaximum(); + } + int week = ((doy0 - firstMonDoy0) / 7) + 1; + if (week == 53) { + if ((firstMonDoy0 == -3 || (firstMonDoy0 == -2 && date.isLeapYear())) == false) { + week = 1; + } + } + return week; + } + + private static int getWeekBasedYear(LocalDate date) { + int year = date.getYear(); + int doy = date.getDayOfYear(); + if (doy <= 3) { + int dow = date.getDayOfWeek().ordinal(); + if (doy - dow < -2) { + year--; + } + } else if (doy >= 363) { + int dow = date.getDayOfWeek().ordinal(); + doy = doy - 363 - (date.isLeapYear() ? 1 : 0); + if (doy - dow >= 0) { + year++; + } + } + return year; + } + } + + //----------------------------------------------------------------------- + /** + * Implementation of the period unit. + */ + private static enum Unit implements TemporalUnit { + + /** + * Unit that represents the concept of a week-based-year. + */ + WEEK_BASED_YEARS("WeekBasedYears", Duration.ofSeconds(31556952L)), + /** + * Unit that represents the concept of a quarter-year. + */ + QUARTER_YEARS("QuarterYears", Duration.ofSeconds(31556952L / 4)); + + private final String name; + private final Duration duration; + + private Unit(String name, Duration estimatedDuration) { + this.name = name; + this.duration = estimatedDuration; + } + + @Override + public String getName() { + return name; + } + + @Override + public Duration getDuration() { + return duration; + } + + @Override + public boolean isDurationEstimated() { + return true; + } + + @Override + public boolean isSupported(Temporal temporal) { + return temporal.isSupported(EPOCH_DAY); + } + + @Override + public R doPlus(R dateTime, long periodToAdd) { + switch(this) { + case WEEK_BASED_YEARS: + return (R) dateTime.with(WEEK_BASED_YEAR, + Math.addExact(dateTime.get(WEEK_BASED_YEAR), periodToAdd)); + case QUARTER_YEARS: + // no overflow (256 is multiple of 4) + return (R) dateTime.plus(periodToAdd / 256, YEARS) + .plus((periodToAdd % 256) * 3, MONTHS); + default: + throw new IllegalStateException("Unreachable"); + } + } + + @Override + public SimplePeriod between(R dateTime1, R dateTime2) { + switch(this) { + case WEEK_BASED_YEARS: + long period = Math.subtractExact(dateTime2.getLong(WEEK_BASED_YEAR), + dateTime1.getLong(WEEK_BASED_YEAR)); + return new SimplePeriod(period, WEEK_BASED_YEARS); + case QUARTER_YEARS: + long period2 = Math.subtractExact(dateTime2.getLong(QUARTER_OF_YEAR), + dateTime1.getLong(QUARTER_OF_YEAR)); + return new SimplePeriod(period2, QUARTER_YEARS); + default: + throw new IllegalStateException("Unreachable"); + } + } + + @Override + public String toString() { + return getName(); + + } + } +} diff --git a/jdk/src/share/classes/java/time/temporal/JulianFields.java b/jdk/src/share/classes/java/time/temporal/JulianFields.java new file mode 100644 index 00000000000..79a9e96bac7 --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/JulianFields.java @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import static java.time.temporal.ChronoField.EPOCH_DAY; +import static java.time.temporal.ChronoUnit.DAYS; +import static java.time.temporal.ChronoUnit.FOREVER; + +import java.io.InvalidObjectException; +import java.io.Serializable; +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.format.DateTimeBuilder; + +/** + * A set of date fields that provide access to Julian Days. + *

    + * The Julian Day is a standard way of expressing date and time commonly used in the scientific community. + * It is expressed as a decimal number of whole days where days start at midday. + * This class represents variations on Julian Days that count whole days from midnight. + * + *

    Specification for implementors

    + * This is an immutable and thread-safe class. + * + * @since 1.8 + */ +public final class JulianFields { + + /** + * The offset from Julian to EPOCH DAY. + */ + private static final long JULIAN_DAY_OFFSET = 2440588L; + + /** + * Julian Day field. + *

    + * This is an integer-based version of the Julian Day Number. + * Julian Day is a well-known system that represents the count of whole days since day 0, + * which is defined to be January 1, 4713 BCE in the Julian calendar, and -4713-11-24 Gregorian. + * The field has "JulianDay" as 'name', and 'DAYS' as 'baseUnit'. + * The field always refers to the local date-time, ignoring the offset or zone. + *

    + * For date-times, 'JULIAN_DAY.doGet()' assumes the same value from + * midnight until just before the next midnight. + * When 'JULIAN_DAY.doWith()' is applied to a date-time, the time of day portion remains unaltered. + * 'JULIAN_DAY.doWith()' and 'JULIAN_DAY.doGet()' only apply to {@code Temporal} objects that + * can be converted into {@link ChronoField#EPOCH_DAY}. + * A {@link DateTimeException} is thrown for any other type of object. + *

    + *

    Astronomical and Scientific Notes

    + * The standard astronomical definition uses a fraction to indicate the time-of-day, + * thus 3.25 would represent the time 18:00, since days start at midday. + * This implementation uses an integer and days starting at midnight. + * The integer value for the Julian Day Number is the astronomical Julian Day value at midday + * of the date in question. + * This amounts to the astronomical Julian Day, rounded to an integer {@code JDN = floor(JD + 0.5)}. + *

    + *

    +     *  | ISO date          |  Julian Day Number | Astronomical Julian Day |
    +     *  | 1970-01-01T00:00  |         2,440,588  |         2,440,587.5     |
    +     *  | 1970-01-01T06:00  |         2,440,588  |         2,440,587.75    |
    +     *  | 1970-01-01T12:00  |         2,440,588  |         2,440,588.0     |
    +     *  | 1970-01-01T18:00  |         2,440,588  |         2,440,588.25    |
    +     *  | 1970-01-02T00:00  |         2,440,589  |         2,440,588.5     |
    +     *  | 1970-01-02T06:00  |         2,440,589  |         2,440,588.75    |
    +     *  | 1970-01-02T12:00  |         2,440,589  |         2,440,589.0     |
    +     * 
    + *

    + * Julian Days are sometimes taken to imply Universal Time or UTC, but this + * implementation always uses the Julian Day number for the local date, + * regardless of the offset or time-zone. + */ + public static final TemporalField JULIAN_DAY = new Field("JulianDay", DAYS, FOREVER, JULIAN_DAY_OFFSET); + + /** + * Modified Julian Day field. + *

    + * This is an integer-based version of the Modified Julian Day Number. + * Modified Julian Day (MJD) is a well-known system that counts days continuously. + * It is defined relative to astronomical Julian Day as {@code MJD = JD - 2400000.5}. + * Each Modified Julian Day runs from midnight to midnight. + * The field always refers to the local date-time, ignoring the offset or zone. + *

    + * For date-times, 'MODIFIED_JULIAN_DAY.doGet()' assumes the same value from + * midnight until just before the next midnight. + * When 'MODIFIED_JULIAN_DAY.doWith()' is applied to a date-time, the time of day portion remains unaltered. + * 'MODIFIED_JULIAN_DAY.doWith()' and 'MODIFIED_JULIAN_DAY.doGet()' only apply to {@code Temporal} objects + * that can be converted into {@link ChronoField#EPOCH_DAY}. + * A {@link DateTimeException} is thrown for any other type of object. + *

    + * This implementation is an integer version of MJD with the decimal part rounded to floor. + *

    + *

    Astronomical and Scientific Notes

    + *
    +     *  | ISO date          | Modified Julian Day |      Decimal MJD |
    +     *  | 1970-01-01T00:00  |             40,587  |       40,587.0   |
    +     *  | 1970-01-01T06:00  |             40,587  |       40,587.25  |
    +     *  | 1970-01-01T12:00  |             40,587  |       40,587.5   |
    +     *  | 1970-01-01T18:00  |             40,587  |       40,587.75  |
    +     *  | 1970-01-02T00:00  |             40,588  |       40,588.0   |
    +     *  | 1970-01-02T06:00  |             40,588  |       40,588.25  |
    +     *  | 1970-01-02T12:00  |             40,588  |       40,588.5   |
    +     * 
    + *

    + * Modified Julian Days are sometimes taken to imply Universal Time or UTC, but this + * implementation always uses the Modified Julian Day for the local date, + * regardless of the offset or time-zone. + */ + public static final TemporalField MODIFIED_JULIAN_DAY = new Field("ModifiedJulianDay", DAYS, FOREVER, 40587L); + + /** + * Rata Die field. + *

    + * Rata Die counts whole days continuously starting day 1 at midnight at the beginning of 0001-01-01 (ISO). + * The field always refers to the local date-time, ignoring the offset or zone. + *

    + * For date-times, 'RATA_DIE.doGet()' assumes the same value from + * midnight until just before the next midnight. + * When 'RATA_DIE.doWith()' is applied to a date-time, the time of day portion remains unaltered. + * 'MODIFIED_JULIAN_DAY.doWith()' and 'RATA_DIE.doGet()' only apply to {@code Temporal} objects + * that can be converted into {@link ChronoField#EPOCH_DAY}. + * A {@link DateTimeException} is thrown for any other type of object. + */ + public static final TemporalField RATA_DIE = new Field("RataDie", DAYS, FOREVER, 719163L); + + /** + * Restricted constructor. + */ + private JulianFields() { + throw new AssertionError("Not instantiable"); + } + + /** + * implementation of JulianFields. Each instance is a singleton. + */ + private static class Field implements TemporalField, Serializable { + + private static final long serialVersionUID = -7501623920830201812L; + + private final String name; + private final transient TemporalUnit baseUnit; + private final transient TemporalUnit rangeUnit; + private final transient ValueRange range; + private final transient long offset; + + private Field(String name, TemporalUnit baseUnit, TemporalUnit rangeUnit, long offset) { + this.name = name; + this.baseUnit = baseUnit; + this.rangeUnit = rangeUnit; + this.range = ValueRange.of(-365243219162L + offset, 365241780471L + offset); + this.offset = offset; + } + + + /** + * Resolve the object from the stream to the appropriate singleton. + * @return one of the singleton objects {@link #JULIAN_DAY}, + * {@link #MODIFIED_JULIAN_DAY}, or {@link #RATA_DIE}. + * @throws InvalidObjectException if the object in the stream is not one of the singletons. + */ + private Object readResolve() throws InvalidObjectException { + if (JULIAN_DAY.getName().equals(name)) { + return JULIAN_DAY; + } else if (MODIFIED_JULIAN_DAY.getName().equals(name)) { + return MODIFIED_JULIAN_DAY; + } else if (RATA_DIE.getName().equals(name)) { + return RATA_DIE; + } else { + throw new InvalidObjectException("Not one of the singletons"); + } + } + + //----------------------------------------------------------------------- + @Override + public String getName() { + return name; + } + + @Override + public TemporalUnit getBaseUnit() { + return baseUnit; + } + + @Override + public TemporalUnit getRangeUnit() { + return rangeUnit; + } + + @Override + public ValueRange range() { + return range; + } + + //----------------------------------------------------------------------- + @Override + public boolean doIsSupported(TemporalAccessor temporal) { + return temporal.isSupported(EPOCH_DAY); + } + + @Override + public ValueRange doRange(TemporalAccessor temporal) { + if (doIsSupported(temporal) == false) { + throw new DateTimeException("Unsupported field: " + this); + } + return range(); + } + + @Override + public long doGet(TemporalAccessor temporal) { + return temporal.getLong(EPOCH_DAY) + offset; + } + + @Override + public R doWith(R temporal, long newValue) { + if (range().isValidValue(newValue) == false) { + throw new DateTimeException("Invalid value: " + name + " " + newValue); + } + return (R) temporal.with(EPOCH_DAY, Math.subtractExact(newValue, offset)); + } + + //----------------------------------------------------------------------- + @Override + public boolean resolve(DateTimeBuilder builder, long value) { + boolean changed = false; + changed = resolve0(JULIAN_DAY, builder, changed); + changed = resolve0(MODIFIED_JULIAN_DAY, builder, changed); + changed = resolve0(RATA_DIE, builder, changed); + return changed; + } + + private boolean resolve0(TemporalField field, DateTimeBuilder builder, boolean changed) { + if (builder.containsFieldValue(field)) { + builder.addCalendrical(LocalDate.ofEpochDay(Math.subtractExact(builder.getFieldValue(JULIAN_DAY), JULIAN_DAY_OFFSET))); + builder.removeFieldValue(JULIAN_DAY); + changed = true; + } + return changed; + } + + //----------------------------------------------------------------------- + @Override + public String toString() { + return getName(); + } + } +} diff --git a/jdk/src/share/classes/java/time/temporal/MonthDay.java b/jdk/src/share/classes/java/time/temporal/MonthDay.java new file mode 100644 index 00000000000..7707cd099a5 --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/MonthDay.java @@ -0,0 +1,755 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.time.Clock; +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.Month; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.DateTimeParseException; +import java.util.Objects; + +/** + * A month-day in the ISO-8601 calendar system, such as {@code --12-03}. + *

    + * {@code MonthDay} is an immutable date-time object that represents the combination + * of a year and month. Any field that can be derived from a month and day, such as + * quarter-of-year, can be obtained. + *

    + * This class does not store or represent a year, time or time-zone. + * For example, the value "December 3rd" can be stored in a {@code MonthDay}. + *

    + * Since a {@code MonthDay} does not possess a year, the leap day of + * February 29th is considered valid. + *

    + * This class implements {@link TemporalAccessor} rather than {@link Temporal}. + * This is because it is not possible to define whether February 29th is valid or not + * without external information, preventing the implementation of plus/minus. + * Related to this, {@code MonthDay} only provides access to query and set the fields + * {@code MONTH_OF_YEAR} and {@code DAY_OF_MONTH}. + *

    + * The ISO-8601 calendar system is the modern civil calendar system used today + * in most of the world. It is equivalent to the proleptic Gregorian calendar + * system, in which today's rules for leap years are applied for all time. + * For most applications written today, the ISO-8601 rules are entirely suitable. + * However, any application that makes use of historical dates, and requires them + * to be accurate will find the ISO-8601 approach unsuitable. + * + *

    Specification for implementors

    + * This class is immutable and thread-safe. + * + * @since 1.8 + */ +public final class MonthDay + implements TemporalAccessor, TemporalAdjuster, Comparable, Serializable { + + /** + * Serialization version. + */ + private static final long serialVersionUID = -939150713474957432L; + /** + * Parser. + */ + private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder() + .appendLiteral("--") + .appendValue(MONTH_OF_YEAR, 2) + .appendLiteral('-') + .appendValue(DAY_OF_MONTH, 2) + .toFormatter(); + + /** + * The month-of-year, not null. + */ + private final int month; + /** + * The day-of-month. + */ + private final int day; + + //----------------------------------------------------------------------- + /** + * Obtains the current month-day from the system clock in the default time-zone. + *

    + * This will query the {@link java.time.Clock#systemDefaultZone() system clock} in the default + * time-zone to obtain the current month-day. + *

    + * Using this method will prevent the ability to use an alternate clock for testing + * because the clock is hard-coded. + * + * @return the current month-day using the system clock and default time-zone, not null + */ + public static MonthDay now() { + return now(Clock.systemDefaultZone()); + } + + /** + * Obtains the current month-day from the system clock in the specified time-zone. + *

    + * This will query the {@link Clock#system(java.time.ZoneId) system clock} to obtain the current month-day. + * Specifying the time-zone avoids dependence on the default time-zone. + *

    + * Using this method will prevent the ability to use an alternate clock for testing + * because the clock is hard-coded. + * + * @param zone the zone ID to use, not null + * @return the current month-day using the system clock, not null + */ + public static MonthDay now(ZoneId zone) { + return now(Clock.system(zone)); + } + + /** + * Obtains the current month-day from the specified clock. + *

    + * This will query the specified clock to obtain the current month-day. + * Using this method allows the use of an alternate clock for testing. + * The alternate clock may be introduced using {@link Clock dependency injection}. + * + * @param clock the clock to use, not null + * @return the current month-day, not null + */ + public static MonthDay now(Clock clock) { + final LocalDate now = LocalDate.now(clock); // called once + return MonthDay.of(now.getMonth(), now.getDayOfMonth()); + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code MonthDay}. + *

    + * The day-of-month must be valid for the month within a leap year. + * Hence, for February, day 29 is valid. + *

    + * For example, passing in April and day 31 will throw an exception, as + * there can never be April 31st in any year. By contrast, passing in + * February 29th is permitted, as that month-day can sometimes be valid. + * + * @param month the month-of-year to represent, not null + * @param dayOfMonth the day-of-month to represent, from 1 to 31 + * @return the month-day, not null + * @throws DateTimeException if the value of any field is out of range + * @throws DateTimeException if the day-of-month is invalid for the month + */ + public static MonthDay of(Month month, int dayOfMonth) { + Objects.requireNonNull(month, "month"); + DAY_OF_MONTH.checkValidValue(dayOfMonth); + if (dayOfMonth > month.maxLength()) { + throw new DateTimeException("Illegal value for DayOfMonth field, value " + dayOfMonth + + " is not valid for month " + month.name()); + } + return new MonthDay(month.getValue(), dayOfMonth); + } + + /** + * Obtains an instance of {@code MonthDay}. + *

    + * The day-of-month must be valid for the month within a leap year. + * Hence, for month 2 (February), day 29 is valid. + *

    + * For example, passing in month 4 (April) and day 31 will throw an exception, as + * there can never be April 31st in any year. By contrast, passing in + * February 29th is permitted, as that month-day can sometimes be valid. + * + * @param month the month-of-year to represent, from 1 (January) to 12 (December) + * @param dayOfMonth the day-of-month to represent, from 1 to 31 + * @return the month-day, not null + * @throws DateTimeException if the value of any field is out of range + * @throws DateTimeException if the day-of-month is invalid for the month + */ + public static MonthDay of(int month, int dayOfMonth) { + return of(Month.of(month), dayOfMonth); + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code MonthDay} from a temporal object. + *

    + * A {@code TemporalAccessor} represents some form of date and time information. + * This factory converts the arbitrary temporal object to an instance of {@code MonthDay}. + *

    + * The conversion extracts the {@link ChronoField#MONTH_OF_YEAR MONTH_OF_YEAR} and + * {@link ChronoField#DAY_OF_MONTH DAY_OF_MONTH} fields. + * The extraction is only permitted if the date-time has an ISO chronology. + *

    + * This method matches the signature of the functional interface {@link TemporalQuery} + * allowing it to be used in queries via method reference, {@code MonthDay::from}. + * + * @param temporal the temporal object to convert, not null + * @return the month-day, not null + * @throws DateTimeException if unable to convert to a {@code MonthDay} + */ + public static MonthDay from(TemporalAccessor temporal) { + if (temporal instanceof MonthDay) { + return (MonthDay) temporal; + } + try { + if (ISOChrono.INSTANCE.equals(Chrono.from(temporal)) == false) { + temporal = LocalDate.from(temporal); + } + return of(temporal.get(MONTH_OF_YEAR), temporal.get(DAY_OF_MONTH)); + } catch (DateTimeException ex) { + throw new DateTimeException("Unable to obtain MonthDay from TemporalAccessor: " + temporal.getClass(), ex); + } + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code MonthDay} from a text string such as {@code --12-03}. + *

    + * The string must represent a valid month-day. + * The format is {@code --MM-dd}. + * + * @param text the text to parse such as "--12-03", not null + * @return the parsed month-day, not null + * @throws DateTimeParseException if the text cannot be parsed + */ + public static MonthDay parse(CharSequence text) { + return parse(text, PARSER); + } + + /** + * Obtains an instance of {@code MonthDay} from a text string using a specific formatter. + *

    + * The text is parsed using the formatter, returning a month-day. + * + * @param text the text to parse, not null + * @param formatter the formatter to use, not null + * @return the parsed month-day, not null + * @throws DateTimeParseException if the text cannot be parsed + */ + public static MonthDay parse(CharSequence text, DateTimeFormatter formatter) { + Objects.requireNonNull(formatter, "formatter"); + return formatter.parse(text, MonthDay::from); + } + + //----------------------------------------------------------------------- + /** + * Constructor, previously validated. + * + * @param month the month-of-year to represent, validated from 1 to 12 + * @param dayOfMonth the day-of-month to represent, validated from 1 to 29-31 + */ + private MonthDay(int month, int dayOfMonth) { + this.month = month; + this.day = dayOfMonth; + } + + //----------------------------------------------------------------------- + /** + * Checks if the specified field is supported. + *

    + * This checks if this month-day can be queried for the specified field. + * If false, then calling the {@link #range(TemporalField) range} and + * {@link #get(TemporalField) get} methods will throw an exception. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@link #isSupported(TemporalField) supported fields} will return valid + * values based on this date-time. + * The supported fields are: + *

      + *
    • {@code MONTH_OF_YEAR} + *
    • {@code YEAR} + *
    + * All other {@code ChronoField} instances will return false. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doIsSupported(TemporalAccessor)} + * passing {@code this} as the argument. + * Whether the field is supported is determined by the field. + * + * @param field the field to check, null returns false + * @return true if the field is supported on this month-day, false if not + */ + @Override + public boolean isSupported(TemporalField field) { + if (field instanceof ChronoField) { + return field == MONTH_OF_YEAR || field == DAY_OF_MONTH; + } + return field != null && field.doIsSupported(this); + } + + /** + * Gets the range of valid values for the specified field. + *

    + * The range object expresses the minimum and maximum valid values for a field. + * This month-day is used to enhance the accuracy of the returned range. + * If it is not possible to return the range, because the field is not supported + * or for some other reason, an exception is thrown. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@link #isSupported(TemporalField) supported fields} will return + * appropriate range instances. + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doRange(TemporalAccessor)} + * passing {@code this} as the argument. + * Whether the range can be obtained is determined by the field. + * + * @param field the field to query the range for, not null + * @return the range of valid values for the field, not null + * @throws DateTimeException if the range for the field cannot be obtained + */ + @Override + public ValueRange range(TemporalField field) { + if (field == MONTH_OF_YEAR) { + return field.range(); + } else if (field == DAY_OF_MONTH) { + return ValueRange.of(1, getMonth().minLength(), getMonth().maxLength()); + } + return TemporalAccessor.super.range(field); + } + + /** + * Gets the value of the specified field from this month-day as an {@code int}. + *

    + * This queries this month-day for the value for the specified field. + * The returned value will always be within the valid range of values for the field. + * If it is not possible to return the value, because the field is not supported + * or for some other reason, an exception is thrown. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@link #isSupported(TemporalField) supported fields} will return valid + * values based on this month-day. + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doGet(TemporalAccessor)} + * passing {@code this} as the argument. Whether the value can be obtained, + * and what the value represents, is determined by the field. + * + * @param field the field to get, not null + * @return the value for the field + * @throws DateTimeException if a value for the field cannot be obtained + * @throws ArithmeticException if numeric overflow occurs + */ + @Override // override for Javadoc + public int get(TemporalField field) { + return range(field).checkValidIntValue(getLong(field), field); + } + + /** + * Gets the value of the specified field from this month-day as a {@code long}. + *

    + * This queries this month-day for the value for the specified field. + * If it is not possible to return the value, because the field is not supported + * or for some other reason, an exception is thrown. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@link #isSupported(TemporalField) supported fields} will return valid + * values based on this month-day. + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doGet(TemporalAccessor)} + * passing {@code this} as the argument. Whether the value can be obtained, + * and what the value represents, is determined by the field. + * + * @param field the field to get, not null + * @return the value for the field + * @throws DateTimeException if a value for the field cannot be obtained + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public long getLong(TemporalField field) { + if (field instanceof ChronoField) { + switch ((ChronoField) field) { + // alignedDOW and alignedWOM not supported because they cannot be set in with() + case DAY_OF_MONTH: return day; + case MONTH_OF_YEAR: return month; + } + throw new DateTimeException("Unsupported field: " + field.getName()); + } + return field.doGet(this); + } + + //----------------------------------------------------------------------- + /** + * Gets the month-of-year field using the {@code Month} enum. + *

    + * This method returns the enum {@link Month} for the month. + * This avoids confusion as to what {@code int} values mean. + * If you need access to the primitive {@code int} value then the enum + * provides the {@link Month#getValue() int value}. + * + * @return the month-of-year, not null + */ + public Month getMonth() { + return Month.of(month); + } + + /** + * Gets the day-of-month field. + *

    + * This method returns the primitive {@code int} value for the day-of-month. + * + * @return the day-of-month, from 1 to 31 + */ + public int getDayOfMonth() { + return day; + } + + //----------------------------------------------------------------------- + /** + * Checks if the year is valid for this month-day. + *

    + * This method checks whether this month and day and the input year form + * a valid date. This can only return false for February 29th. + * + * @param year the year to validate, an out of range value returns false + * @return true if the year is valid for this month-day + * @see Year#isValidMonthDay(MonthDay) + */ + public boolean isValidYear(int year) { + return (day == 29 && month == 2 && Year.isLeap(year) == false) == false; + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this {@code MonthDay} with the month-of-year altered. + *

    + * This returns a month-day with the specified month. + * If the day-of-month is invalid for the specified month, the day will + * be adjusted to the last valid day-of-month. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param month the month-of-year to set in the returned month-day, from 1 (January) to 12 (December) + * @return a {@code MonthDay} based on this month-day with the requested month, not null + * @throws DateTimeException if the month-of-year value is invalid + */ + public MonthDay withMonth(int month) { + return with(Month.of(month)); + } + + /** + * Returns a copy of this {@code MonthDay} with the month-of-year altered. + *

    + * This returns a month-day with the specified month. + * If the day-of-month is invalid for the specified month, the day will + * be adjusted to the last valid day-of-month. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param month the month-of-year to set in the returned month-day, not null + * @return a {@code MonthDay} based on this month-day with the requested month, not null + */ + public MonthDay with(Month month) { + Objects.requireNonNull(month, "month"); + if (month.getValue() == this.month) { + return this; + } + int day = Math.min(this.day, month.maxLength()); + return new MonthDay(month.getValue(), day); + } + + /** + * Returns a copy of this {@code MonthDay} with the day-of-month altered. + *

    + * This returns a month-day with the specified day-of-month. + * If the day-of-month is invalid for the month, an exception is thrown. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param dayOfMonth the day-of-month to set in the return month-day, from 1 to 31 + * @return a {@code MonthDay} based on this month-day with the requested day, not null + * @throws DateTimeException if the day-of-month value is invalid + * @throws DateTimeException if the day-of-month is invalid for the month + */ + public MonthDay withDayOfMonth(int dayOfMonth) { + if (dayOfMonth == this.day) { + return this; + } + return of(month, dayOfMonth); + } + + //----------------------------------------------------------------------- + /** + * Queries this month-day using the specified query. + *

    + * This queries this month-day using the specified query strategy object. + * The {@code TemporalQuery} object defines the logic to be used to + * obtain the result. Read the documentation of the query to understand + * what the result of this method will be. + *

    + * The result of this method is obtained by invoking the + * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the + * specified query passing {@code this} as the argument. + * + * @param the type of the result + * @param query the query to invoke, not null + * @return the query result, null may be returned (defined by the query) + * @throws DateTimeException if unable to query (defined by the query) + * @throws ArithmeticException if numeric overflow occurs (defined by the query) + */ + @SuppressWarnings("unchecked") + @Override + public R query(TemporalQuery query) { + if (query == Queries.chrono()) { + return (R) ISOChrono.INSTANCE; + } + return TemporalAccessor.super.query(query); + } + + /** + * Adjusts the specified temporal object to have this month-day. + *

    + * This returns a temporal object of the same observable type as the input + * with the month and day-of-month changed to be the same as this. + *

    + * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)} + * twice, passing {@link ChronoField#MONTH_OF_YEAR} and + * {@link ChronoField#DAY_OF_MONTH} as the fields. + * If the specified temporal object does not use the ISO calendar system then + * a {@code DateTimeException} is thrown. + *

    + * In most cases, it is clearer to reverse the calling pattern by using + * {@link Temporal#with(TemporalAdjuster)}: + *

    +     *   // these two lines are equivalent, but the second approach is recommended
    +     *   temporal = thisMonthDay.adjustInto(temporal);
    +     *   temporal = temporal.with(thisMonthDay);
    +     * 
    + *

    + * This instance is immutable and unaffected by this method call. + * + * @param temporal the target object to be adjusted, not null + * @return the adjusted object, not null + * @throws DateTimeException if unable to make the adjustment + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public Temporal adjustInto(Temporal temporal) { + if (Chrono.from(temporal).equals(ISOChrono.INSTANCE) == false) { + throw new DateTimeException("Adjustment only supported on ISO date-time"); + } + temporal = temporal.with(MONTH_OF_YEAR, month); + return temporal.with(DAY_OF_MONTH, Math.min(temporal.range(DAY_OF_MONTH).getMaximum(), day)); + } + + //----------------------------------------------------------------------- + /** + * Returns a date formed from this month-day at the specified year. + *

    + * This combines this month-day and the specified year to form a {@code LocalDate}. + * A month-day of February 29th will be adjusted to February 28th in the resulting + * date if the year is not a leap year. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param year the year to use, from MIN_YEAR to MAX_YEAR + * @return the local date formed from this month-day and the specified year, not null + * @see Year#atMonthDay(MonthDay) + */ + public LocalDate atYear(int year) { + return LocalDate.of(year, month, isValidYear(year) ? day : 28); + } + + //----------------------------------------------------------------------- + /** + * Compares this month-day to another month-day. + *

    + * The comparison is based first on value of the month, then on the value of the day. + * It is "consistent with equals", as defined by {@link Comparable}. + * + * @param other the other month-day to compare to, not null + * @return the comparator value, negative if less, positive if greater + */ + public int compareTo(MonthDay other) { + int cmp = (month - other.month); + if (cmp == 0) { + cmp = (day - other.day); + } + return cmp; + } + + /** + * Is this month-day after the specified month-day. + * + * @param other the other month-day to compare to, not null + * @return true if this is after the specified month-day + */ + public boolean isAfter(MonthDay other) { + return compareTo(other) > 0; + } + + /** + * Is this month-day before the specified month-day. + * + * @param other the other month-day to compare to, not null + * @return true if this point is before the specified month-day + */ + public boolean isBefore(MonthDay other) { + return compareTo(other) < 0; + } + + //----------------------------------------------------------------------- + /** + * Checks if this month-day is equal to another month-day. + *

    + * The comparison is based on the time-line position of the month-day within a year. + * + * @param obj the object to check, null returns false + * @return true if this is equal to the other month-day + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof MonthDay) { + MonthDay other = (MonthDay) obj; + return month == other.month && day == other.day; + } + return false; + } + + /** + * A hash code for this month-day. + * + * @return a suitable hash code + */ + @Override + public int hashCode() { + return (month << 6) + day; + } + + //----------------------------------------------------------------------- + /** + * Outputs this month-day as a {@code String}, such as {@code --12-03}. + *

    + * The output will be in the format {@code --MM-dd}: + * + * @return a string representation of this month-day, not null + */ + @Override + public String toString() { + return new StringBuilder(10).append("--") + .append(month < 10 ? "0" : "").append(month) + .append(day < 10 ? "-0" : "-").append(day) + .toString(); + } + + /** + * Outputs this month-day as a {@code String} using the formatter. + *

    + * This month-day will be passed to the formatter + * {@link DateTimeFormatter#print(TemporalAccessor) print method}. + * + * @param formatter the formatter to use, not null + * @return the formatted month-day string, not null + * @throws DateTimeException if an error occurs during printing + */ + public String toString(DateTimeFormatter formatter) { + Objects.requireNonNull(formatter, "formatter"); + return formatter.print(this); + } + + //----------------------------------------------------------------------- + /** + * Writes the object using a + * dedicated serialized form. + *

    +     *  out.writeByte(6);  // identifies this as a Year
    +     *  out.writeByte(month);
    +     *  out.writeByte(day);
    +     * 
    + * + * @return the instance of {@code Ser}, not null + */ + private Object writeReplace() { + return new Ser(Ser.MONTH_DAY_TYPE, this); + } + + /** + * Defend against malicious streams. + * @return never + * @throws InvalidObjectException always + */ + private Object readResolve() throws ObjectStreamException { + throw new InvalidObjectException("Deserialization via serialization delegate"); + } + + void writeExternal(DataOutput out) throws IOException { + out.writeByte(month); + out.writeByte(day); + } + + static MonthDay readExternal(DataInput in) throws IOException { + byte month = in.readByte(); + byte day = in.readByte(); + return MonthDay.of(month, day); + } + +} diff --git a/jdk/src/share/classes/java/time/temporal/OffsetDate.java b/jdk/src/share/classes/java/time/temporal/OffsetDate.java new file mode 100644 index 00000000000..2503de927bd --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/OffsetDate.java @@ -0,0 +1,1351 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import static java.time.temporal.ChronoField.EPOCH_DAY; +import static java.time.temporal.ChronoField.OFFSET_SECONDS; +import static java.time.temporal.ChronoLocalDateTimeImpl.SECONDS_PER_DAY; +import static java.time.temporal.ChronoUnit.DAYS; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.time.Clock; +import java.time.DateTimeException; +import java.time.DayOfWeek; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.Month; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatters; +import java.time.format.DateTimeParseException; +import java.time.zone.ZoneRules; +import java.util.Objects; + +/** + * A date with an offset from UTC/Greenwich in the ISO-8601 calendar system, + * such as {@code 2007-12-03+01:00}. + *

    + * {@code OffsetDate} is an immutable date-time object that represents a date, often viewed + * as year-month-day-offset. This object can also access other date fields such as + * day-of-year, day-of-week and week-of-year. + *

    + * This class does not store or represent a time. + * For example, the value "2nd October 2007 +02:00" can be stored + * in an {@code OffsetDate}. + * + *

    Specification for implementors

    + * This class is immutable and thread-safe. + * + * @since 1.8 + */ +public final class OffsetDate + implements Temporal, TemporalAdjuster, Comparable, Serializable { + + /** + * The minimum supported {@code OffsetDate}, '-999999999-01-01+18:00'. + * This is the minimum local date in the maximum offset + * (larger offsets are earlier on the time-line). + * This combines {@link LocalDate#MIN} and {@link ZoneOffset#MAX}. + * This could be used by an application as a "far past" date. + */ + public static final OffsetDate MIN = LocalDate.MIN.atOffset(ZoneOffset.MAX); + /** + * The maximum supported {@code OffsetDate}, '+999999999-12-31-18:00'. + * This is the maximum local date in the minimum offset + * (larger negative offsets are later on the time-line). + * This combines {@link LocalDate#MAX} and {@link ZoneOffset#MIN}. + * This could be used by an application as a "far future" date. + */ + public static final OffsetDate MAX = LocalDate.MAX.atOffset(ZoneOffset.MIN); + + /** + * Serialization version. + */ + private static final long serialVersionUID = -4382054179074397774L; + + /** + * The local date. + */ + private final LocalDate date; + /** + * The offset from UTC/Greenwich. + */ + private final ZoneOffset offset; + + //----------------------------------------------------------------------- + /** + * Obtains the current date from the system clock in the default time-zone. + *

    + * This will query the {@link java.time.Clock#systemDefaultZone() system clock} in the default + * time-zone to obtain the current date. + * The offset will be calculated from the time-zone in the clock. + *

    + * Using this method will prevent the ability to use an alternate clock for testing + * because the clock is hard-coded. + * + * @return the current date using the system clock, not null + */ + public static OffsetDate now() { + return now(Clock.systemDefaultZone()); + } + + /** + * Obtains the current date from the system clock in the specified time-zone. + *

    + * This will query the {@link Clock#system(java.time.ZoneId) system clock} to obtain the current date. + * Specifying the time-zone avoids dependence on the default time-zone. + * The offset will be calculated from the specified time-zone. + *

    + * Using this method will prevent the ability to use an alternate clock for testing + * because the clock is hard-coded. + * + * @param zone the zone ID to use, not null + * @return the current date using the system clock, not null + */ + public static OffsetDate now(ZoneId zone) { + return now(Clock.system(zone)); + } + + /** + * Obtains the current date from the specified clock. + *

    + * This will query the specified clock to obtain the current date - today. + * The offset will be calculated from the time-zone in the clock. + *

    + * Using this method allows the use of an alternate clock for testing. + * The alternate clock may be introduced using {@link Clock dependency injection}. + * + * @param clock the clock to use, not null + * @return the current date, not null + */ + public static OffsetDate now(Clock clock) { + Objects.requireNonNull(clock, "clock"); + final Instant now = clock.instant(); // called once + return ofInstant(now, clock.getZone().getRules().getOffset(now)); + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code OffsetDate} from a local date and an offset. + * + * @param date the local date, not null + * @param offset the zone offset, not null + * @return the offset date, not null + */ + public static OffsetDate of(LocalDate date, ZoneOffset offset) { + return new OffsetDate(date, offset); + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code OffsetDate} from an {@code Instant} and zone ID. + *

    + * This creates an offset date with the same instant as midnight at the + * start of day of the instant specified. + * Finding the offset from UTC/Greenwich is simple as there is only one valid + * offset for each instant. + * + * @param instant the instant to create the time from, not null + * @param zone the time-zone, which may be an offset, not null + * @return the offset time, not null + */ + public static OffsetDate ofInstant(Instant instant, ZoneId zone) { + Objects.requireNonNull(instant, "instant"); + Objects.requireNonNull(zone, "zone"); + ZoneRules rules = zone.getRules(); + ZoneOffset offset = rules.getOffset(instant); + long epochSec = instant.getEpochSecond() + offset.getTotalSeconds(); // overflow caught later + long epochDay = Math.floorDiv(epochSec, SECONDS_PER_DAY); + LocalDate date = LocalDate.ofEpochDay(epochDay); + return new OffsetDate(date, offset); + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code OffsetDate} from a temporal object. + *

    + * A {@code TemporalAccessor} represents some form of date and time information. + * This factory converts the arbitrary temporal object to an instance of {@code OffsetDate}. + *

    + * The conversion extracts and combines {@code LocalDate} and {@code ZoneOffset}. + *

    + * This method matches the signature of the functional interface {@link TemporalQuery} + * allowing it to be used in queries via method reference, {@code OffsetDate::from}. + * + * @param temporal the temporal object to convert, not null + * @return the offset date, not null + * @throws DateTimeException if unable to convert to an {@code OffsetDate} + */ + public static OffsetDate from(TemporalAccessor temporal) { + if (temporal instanceof OffsetDate) { + return (OffsetDate) temporal; + } + try { + LocalDate date = LocalDate.from(temporal); + ZoneOffset offset = ZoneOffset.from(temporal); + return new OffsetDate(date, offset); + } catch (DateTimeException ex) { + throw new DateTimeException("Unable to obtain OffsetDate from TemporalAccessor: " + temporal.getClass(), ex); + } + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code OffsetDate} from a text string such as {@code 2007-12-03+01:00}. + *

    + * The string must represent a valid date and is parsed using + * {@link java.time.format.DateTimeFormatters#isoOffsetDate()}. + * + * @param text the text to parse such as "2007-12-03+01:00", not null + * @return the parsed offset date, not null + * @throws DateTimeParseException if the text cannot be parsed + */ + public static OffsetDate parse(CharSequence text) { + return parse(text, DateTimeFormatters.isoOffsetDate()); + } + + /** + * Obtains an instance of {@code OffsetDate} from a text string using a specific formatter. + *

    + * The text is parsed using the formatter, returning a date. + * + * @param text the text to parse, not null + * @param formatter the formatter to use, not null + * @return the parsed offset date, not null + * @throws DateTimeParseException if the text cannot be parsed + */ + public static OffsetDate parse(CharSequence text, DateTimeFormatter formatter) { + Objects.requireNonNull(formatter, "formatter"); + return formatter.parse(text, OffsetDate::from); + } + + //----------------------------------------------------------------------- + /** + * Constructor. + * + * @param date the local date, not null + * @param offset the zone offset, not null + */ + private OffsetDate(LocalDate date, ZoneOffset offset) { + this.date = Objects.requireNonNull(date, "date"); + this.offset = Objects.requireNonNull(offset, "offset"); + } + + /** + * Returns a new date based on this one, returning {@code this} where possible. + * + * @param date the date to create with, not null + * @param offset the zone offset to create with, not null + */ + private OffsetDate with(LocalDate date, ZoneOffset offset) { + if (this.date == date && this.offset.equals(offset)) { + return this; + } + return new OffsetDate(date, offset); + } + + //----------------------------------------------------------------------- + /** + * Checks if the specified field is supported. + *

    + * This checks if this date can be queried for the specified field. + * If false, then calling the {@link #range(TemporalField) range} and + * {@link #get(TemporalField) get} methods will throw an exception. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@link #isSupported(TemporalField) supported fields} will return valid + * values based on this date-time. + * The supported fields are: + *

      + *
    • {@code DAY_OF_WEEK} + *
    • {@code ALIGNED_DAY_OF_WEEK_IN_MONTH} + *
    • {@code ALIGNED_DAY_OF_WEEK_IN_YEAR} + *
    • {@code DAY_OF_MONTH} + *
    • {@code DAY_OF_YEAR} + *
    • {@code EPOCH_DAY} + *
    • {@code ALIGNED_WEEK_OF_MONTH} + *
    • {@code ALIGNED_WEEK_OF_YEAR} + *
    • {@code MONTH_OF_YEAR} + *
    • {@code EPOCH_MONTH} + *
    • {@code YEAR_OF_ERA} + *
    • {@code YEAR} + *
    • {@code ERA} + *
    • {@code OFFSET_SECONDS} + *
    + * All other {@code ChronoField} instances will return false. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doIsSupported(TemporalAccessor)} + * passing {@code this} as the argument. + * Whether the field is supported is determined by the field. + * + * @param field the field to check, null returns false + * @return true if the field is supported on this date, false if not + */ + @Override + public boolean isSupported(TemporalField field) { + if (field instanceof ChronoField) { + return ((ChronoField) field).isDateField() || field == OFFSET_SECONDS; + } + return field != null && field.doIsSupported(this); + } + + /** + * Gets the range of valid values for the specified field. + *

    + * The range object expresses the minimum and maximum valid values for a field. + * This date is used to enhance the accuracy of the returned range. + * If it is not possible to return the range, because the field is not supported + * or for some other reason, an exception is thrown. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@link #isSupported(TemporalField) supported fields} will return + * appropriate range instances. + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doRange(TemporalAccessor)} + * passing {@code this} as the argument. + * Whether the range can be obtained is determined by the field. + * + * @param field the field to query the range for, not null + * @return the range of valid values for the field, not null + * @throws DateTimeException if the range for the field cannot be obtained + */ + @Override + public ValueRange range(TemporalField field) { + if (field instanceof ChronoField) { + if (field == OFFSET_SECONDS) { + return field.range(); + } + return date.range(field); + } + return field.doRange(this); + } + + /** + * Gets the value of the specified field from this date as an {@code int}. + *

    + * This queries this date for the value for the specified field. + * The returned value will always be within the valid range of values for the field. + * If it is not possible to return the value, because the field is not supported + * or for some other reason, an exception is thrown. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@link #isSupported(TemporalField) supported fields} will return valid + * values based on this date, except {@code EPOCH_DAY} and {@code EPOCH_MONTH} + * which are too large to fit in an {@code int} and throw a {@code DateTimeException}. + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doGet(TemporalAccessor)} + * passing {@code this} as the argument. Whether the value can be obtained, + * and what the value represents, is determined by the field. + * + * @param field the field to get, not null + * @return the value for the field + * @throws DateTimeException if a value for the field cannot be obtained + * @throws ArithmeticException if numeric overflow occurs + */ + @Override // override for Javadoc + public int get(TemporalField field) { + return Temporal.super.get(field); + } + + /** + * Gets the value of the specified field from this date as a {@code long}. + *

    + * This queries this date for the value for the specified field. + * If it is not possible to return the value, because the field is not supported + * or for some other reason, an exception is thrown. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@link #isSupported(TemporalField) supported fields} will return valid + * values based on this date. + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doGet(TemporalAccessor)} + * passing {@code this} as the argument. Whether the value can be obtained, + * and what the value represents, is determined by the field. + * + * @param field the field to get, not null + * @return the value for the field + * @throws DateTimeException if a value for the field cannot be obtained + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public long getLong(TemporalField field) { + if (field instanceof ChronoField) { + if (field == OFFSET_SECONDS) { + return getOffset().getTotalSeconds(); + } + return date.getLong(field); + } + return field.doGet(this); + } + + //----------------------------------------------------------------------- + /** + * Gets the zone offset, such as '+01:00'. + *

    + * This is the offset of the local date from UTC/Greenwich. + * + * @return the zone offset, not null + */ + public ZoneOffset getOffset() { + return offset; + } + + /** + * Returns a copy of this {@code OffsetDate} with the specified offset. + *

    + * This method returns an object with the same {@code LocalDate} and the specified {@code ZoneOffset}. + * No calculation is needed or performed. + * For example, if this time represents {@code 2007-12-03+02:00} and the offset specified is + * {@code +03:00}, then this method will return {@code 2007-12-03+03:00}. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param offset the zone offset to change to, not null + * @return an {@code OffsetDate} based on this date with the requested offset, not null + */ + public OffsetDate withOffset(ZoneOffset offset) { + Objects.requireNonNull(offset, "offset"); + return with(date, offset); + } + + //----------------------------------------------------------------------- + /** + * Gets the {@code LocalDate} part of this date-time. + *

    + * This returns a {@code LocalDate} with the same year, month and day + * as this date-time. + * + * @return the date part of this date-time, not null + */ + public LocalDate getDate() { + return date; + } + + //----------------------------------------------------------------------- + /** + * Gets the year field. + *

    + * This method returns the primitive {@code int} value for the year. + *

    + * The year returned by this method is proleptic as per {@code get(YEAR)}. + * To obtain the year-of-era, use {@code get(YEAR_OF_ERA}. + * + * @return the year, from MIN_YEAR to MAX_YEAR + */ + public int getYear() { + return date.getYear(); + } + + /** + * Gets the month-of-year field from 1 to 12. + *

    + * This method returns the month as an {@code int} from 1 to 12. + * Application code is frequently clearer if the enum {@link Month} + * is used by calling {@link #getMonth()}. + * + * @return the month-of-year, from 1 to 12 + * @see #getMonth() + */ + public int getMonthValue() { + return date.getMonthValue(); + } + + /** + * Gets the month-of-year field using the {@code Month} enum. + *

    + * This method returns the enum {@link Month} for the month. + * This avoids confusion as to what {@code int} values mean. + * If you need access to the primitive {@code int} value then the enum + * provides the {@link Month#getValue() int value}. + * + * @return the month-of-year, not null + * @see #getMonthValue() + */ + public Month getMonth() { + return date.getMonth(); + } + + /** + * Gets the day-of-month field. + *

    + * This method returns the primitive {@code int} value for the day-of-month. + * + * @return the day-of-month, from 1 to 31 + */ + public int getDayOfMonth() { + return date.getDayOfMonth(); + } + + /** + * Gets the day-of-year field. + *

    + * This method returns the primitive {@code int} value for the day-of-year. + * + * @return the day-of-year, from 1 to 365, or 366 in a leap year + */ + public int getDayOfYear() { + return date.getDayOfYear(); + } + + /** + * Gets the day-of-week field, which is an enum {@code DayOfWeek}. + *

    + * This method returns the enum {@link java.time.DayOfWeek} for the day-of-week. + * This avoids confusion as to what {@code int} values mean. + * If you need access to the primitive {@code int} value then the enum + * provides the {@link java.time.DayOfWeek#getValue() int value}. + *

    + * Additional information can be obtained from the {@code DayOfWeek}. + * This includes textual names of the values. + * + * @return the day-of-week, not null + */ + public DayOfWeek getDayOfWeek() { + return date.getDayOfWeek(); + } + + //----------------------------------------------------------------------- + /** + * Returns an adjusted copy of this date. + *

    + * This returns a new {@code OffsetDate}, based on this one, with the date adjusted. + * The adjustment takes place using the specified adjuster strategy object. + * Read the documentation of the adjuster to understand what adjustment will be made. + *

    + * A simple adjuster might simply set the one of the fields, such as the year field. + * A more complex adjuster might set the date to the last day of the month. + * A selection of common adjustments is provided in {@link java.time.temporal.Adjusters}. + * These include finding the "last day of the month" and "next Wednesday". + * Key date-time classes also implement the {@code TemporalAdjuster} interface, + * such as {@link Month} and {@link java.time.temporal.MonthDay MonthDay}. + * The adjuster is responsible for handling special cases, such as the varying + * lengths of month and leap years. + *

    + * For example this code returns a date on the last day of July: + *

    +     *  import static java.time.Month.*;
    +     *  import static java.time.temporal.Adjusters.*;
    +     *
    +     *  result = offsetDate.with(JULY).with(lastDayOfMonth());
    +     * 
    + *

    + * The classes {@link LocalDate} and {@link ZoneOffset} implement {@code TemporalAdjuster}, + * thus this method can be used to change the date or offset: + *

    +     *  result = offsetDate.with(date);
    +     *  result = offsetDate.with(offset);
    +     * 
    + *

    + * The result of this method is obtained by invoking the + * {@link TemporalAdjuster#adjustInto(Temporal)} method on the + * specified adjuster passing {@code this} as the argument. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param adjuster the adjuster to use, not null + * @return an {@code OffsetDate} based on {@code this} with the adjustment made, not null + * @throws DateTimeException if the adjustment cannot be made + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public OffsetDate with(TemporalAdjuster adjuster) { + // optimizations + if (adjuster instanceof LocalDate) { + return with((LocalDate) adjuster, offset); + } else if (adjuster instanceof ZoneOffset) { + return with(date, (ZoneOffset) adjuster); + } else if (adjuster instanceof OffsetDate) { + return (OffsetDate) adjuster; + } + return (OffsetDate) adjuster.adjustInto(this); + } + + /** + * Returns a copy of this date with the specified field set to a new value. + *

    + * This returns a new {@code OffsetDate}, based on this one, with the value + * for the specified field changed. + * This can be used to change any supported field, such as the year, month or day-of-month. + * If it is not possible to set the value, because the field is not supported or for + * some other reason, an exception is thrown. + *

    + * In some cases, changing the specified field can cause the resulting date to become invalid, + * such as changing the month from 31st January to February would make the day-of-month invalid. + * In cases like this, the field is responsible for resolving the date. Typically it will choose + * the previous valid date, which would be the last valid day of February in this example. + *

    + * If the field is a {@link ChronoField} then the adjustment is implemented here. + *

    + * The {@code OFFSET_SECONDS} field will return a date with the specified offset. + * The local date is unaltered. If the new offset value is outside the valid range + * then a {@code DateTimeException} will be thrown. + *

    + * The other {@link #isSupported(TemporalField) supported fields} will behave as per + * the matching method on {@link LocalDate#with(TemporalField, long)} LocalDate}. + * In this case, the offset is not part of the calculation and will be unchanged. + *

    + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doWith(Temporal, long)} + * passing {@code this} as the argument. In this case, the field determines + * whether and how to adjust the instant. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param field the field to set in the result, not null + * @param newValue the new value of the field in the result + * @return an {@code OffsetDate} based on {@code this} with the specified field set, not null + * @throws DateTimeException if the field cannot be set + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public OffsetDate with(TemporalField field, long newValue) { + if (field instanceof ChronoField) { + if (field == OFFSET_SECONDS) { + ChronoField f = (ChronoField) field; + return with(date, ZoneOffset.ofTotalSeconds(f.checkValidIntValue(newValue))); + } + return with(date.with(field, newValue), offset); + } + return field.doWith(this, newValue); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this {@code OffsetDate} with the year altered. + * The offset does not affect the calculation and will be the same in the result. + * If the day-of-month is invalid for the year, it will be changed to the last valid day of the month. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param year the year to set in the result, from MIN_YEAR to MAX_YEAR + * @return an {@code OffsetDate} based on this date with the requested year, not null + * @throws DateTimeException if the year value is invalid + */ + public OffsetDate withYear(int year) { + return with(date.withYear(year), offset); + } + + /** + * Returns a copy of this {@code OffsetDate} with the month-of-year altered. + * The offset does not affect the calculation and will be the same in the result. + * If the day-of-month is invalid for the year, it will be changed to the last valid day of the month. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param month the month-of-year to set in the result, from 1 (January) to 12 (December) + * @return an {@code OffsetDate} based on this date with the requested month, not null + * @throws DateTimeException if the month-of-year value is invalid + */ + public OffsetDate withMonth(int month) { + return with(date.withMonth(month), offset); + } + + /** + * Returns a copy of this {@code OffsetDate} with the day-of-month altered. + * If the resulting date is invalid, an exception is thrown. + * The offset does not affect the calculation and will be the same in the result. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param dayOfMonth the day-of-month to set in the result, from 1 to 28-31 + * @return an {@code OffsetDate} based on this date with the requested day, not null + * @throws DateTimeException if the day-of-month value is invalid + * @throws DateTimeException if the day-of-month is invalid for the month-year + */ + public OffsetDate withDayOfMonth(int dayOfMonth) { + return with(date.withDayOfMonth(dayOfMonth), offset); + } + + /** + * Returns a copy of this {@code OffsetDate} with the day-of-year altered. + * If the resulting date is invalid, an exception is thrown. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param dayOfYear the day-of-year to set in the result, from 1 to 365-366 + * @return an {@code OffsetDate} based on this date with the requested day, not null + * @throws DateTimeException if the day-of-year value is invalid + * @throws DateTimeException if the day-of-year is invalid for the year + */ + public OffsetDate withDayOfYear(int dayOfYear) { + return with(date.withDayOfYear(dayOfYear), offset); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this date with the specified period added. + *

    + * This method returns a new date based on this date with the specified period added. + * The adder is typically {@link java.time.Period} but may be any other type implementing + * the {@link TemporalAdder} interface. + * The calculation is delegated to the specified adjuster, which typically calls + * back to {@link #plus(long, TemporalUnit)}. + * The offset is not part of the calculation and will be unchanged in the result. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param adder the adder to use, not null + * @return an {@code OffsetDate} based on this date with the addition made, not null + * @throws DateTimeException if the addition cannot be made + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public OffsetDate plus(TemporalAdder adder) { + return (OffsetDate) adder.addTo(this); + } + + /** + * Returns a copy of this date with the specified period added. + *

    + * This method returns a new date based on this date with the specified period added. + * This can be used to add any period that is defined by a unit, for example to add years, months or days. + * The unit is responsible for the details of the calculation, including the resolution + * of any edge cases in the calculation. + * The offset is not part of the calculation and will be unchanged in the result. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param amountToAdd the amount of the unit to add to the result, may be negative + * @param unit the unit of the period to add, not null + * @return an {@code OffsetDate} based on this date with the specified period added, not null + * @throws DateTimeException if the unit cannot be added to this type + */ + @Override + public OffsetDate plus(long amountToAdd, TemporalUnit unit) { + if (unit instanceof ChronoUnit) { + return with(date.plus(amountToAdd, unit), offset); + } + return unit.doPlus(this, amountToAdd); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this {@code OffsetDate} with the specified period in years added. + *

    + * This method adds the specified amount to the years field in three steps: + *

      + *
    1. Add the input years to the year field
    2. + *
    3. Check if the resulting date would be invalid
    4. + *
    5. Adjust the day-of-month to the last valid day if necessary
    6. + *
    + *

    + * For example, 2008-02-29 (leap year) plus one year would result in the + * invalid date 2009-02-29 (standard year). Instead of returning an invalid + * result, the last valid day of the month, 2009-02-28, is selected instead. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param years the years to add, may be negative + * @return an {@code OffsetDate} based on this date with the years added, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + public OffsetDate plusYears(long years) { + return with(date.plusYears(years), offset); + } + + /** + * Returns a copy of this {@code OffsetDate} with the specified period in months added. + *

    + * This method adds the specified amount to the months field in three steps: + *

      + *
    1. Add the input months to the month-of-year field
    2. + *
    3. Check if the resulting date would be invalid
    4. + *
    5. Adjust the day-of-month to the last valid day if necessary
    6. + *
    + *

    + * For example, 2007-03-31 plus one month would result in the invalid date + * 2007-04-31. Instead of returning an invalid result, the last valid day + * of the month, 2007-04-30, is selected instead. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param months the months to add, may be negative + * @return an {@code OffsetDate} based on this date with the months added, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + public OffsetDate plusMonths(long months) { + return with(date.plusMonths(months), offset); + } + + /** + * Returns a copy of this {@code OffsetDate} with the specified period in weeks added. + *

    + * This method adds the specified amount in weeks to the days field incrementing + * the month and year fields as necessary to ensure the result remains valid. + * The result is only invalid if the maximum/minimum year is exceeded. + *

    + * For example, 2008-12-31 plus one week would result in 2009-01-07. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param weeks the weeks to add, may be negative + * @return an {@code OffsetDate} based on this date with the weeks added, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + public OffsetDate plusWeeks(long weeks) { + return with(date.plusWeeks(weeks), offset); + } + + /** + * Returns a copy of this {@code OffsetDate} with the specified period in days added. + *

    + * This method adds the specified amount to the days field incrementing the + * month and year fields as necessary to ensure the result remains valid. + * The result is only invalid if the maximum/minimum year is exceeded. + *

    + * For example, 2008-12-31 plus one day would result in 2009-01-01. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param days the days to add, may be negative + * @return an {@code OffsetDate} based on this date with the days added, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + public OffsetDate plusDays(long days) { + return with(date.plusDays(days), offset); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this date with the specified period subtracted. + *

    + * This method returns a new date based on this date with the specified period subtracted. + * The subtractor is typically {@link java.time.Period} but may be any other type implementing + * the {@link TemporalSubtractor} interface. + * The calculation is delegated to the specified adjuster, which typically calls + * back to {@link #minus(long, TemporalUnit)}. + * The offset is not part of the calculation and will be unchanged in the result. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param subtractor the subtractor to use, not null + * @return an {@code OffsetDate} based on this date with the subtraction made, not null + * @throws DateTimeException if the subtraction cannot be made + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public OffsetDate minus(TemporalSubtractor subtractor) { + return (OffsetDate) subtractor.subtractFrom(this); + } + + /** + * Returns a copy of this date with the specified period subtracted. + *

    + * This method returns a new date based on this date with the specified period subtracted. + * This can be used to subtract any period that is defined by a unit, for example to subtract years, months or days. + * The unit is responsible for the details of the calculation, including the resolution + * of any edge cases in the calculation. + * The offset is not part of the calculation and will be unchanged in the result. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param amountToSubtract the amount of the unit to subtract from the result, may be negative + * @param unit the unit of the period to subtract, not null + * @return an {@code OffsetDate} based on this date with the specified period subtracted, not null + * @throws DateTimeException if the unit cannot be added to this type + */ + @Override + public OffsetDate minus(long amountToSubtract, TemporalUnit unit) { + return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit)); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this {@code OffsetDate} with the specified period in years subtracted. + *

    + * This method subtracts the specified amount from the years field in three steps: + *

      + *
    1. Subtract the input years to the year field
    2. + *
    3. Check if the resulting date would be invalid
    4. + *
    5. Adjust the day-of-month to the last valid day if necessary
    6. + *
    + *

    + * For example, 2008-02-29 (leap year) minus one year would result in the + * invalid date 2007-02-29 (standard year). Instead of returning an invalid + * result, the last valid day of the month, 2007-02-28, is selected instead. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param years the years to subtract, may be negative + * @return an {@code OffsetDate} based on this date with the years subtracted, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + public OffsetDate minusYears(long years) { + return with(date.minusYears(years), offset); + } + + /** + * Returns a copy of this {@code OffsetDate} with the specified period in months subtracted. + *

    + * This method subtracts the specified amount from the months field in three steps: + *

      + *
    1. Subtract the input months to the month-of-year field
    2. + *
    3. Check if the resulting date would be invalid
    4. + *
    5. Adjust the day-of-month to the last valid day if necessary
    6. + *
    + *

    + * For example, 2007-03-31 minus one month would result in the invalid date + * 2007-02-31. Instead of returning an invalid result, the last valid day + * of the month, 2007-02-28, is selected instead. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param months the months to subtract, may be negative + * @return an {@code OffsetDate} based on this date with the months subtracted, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + public OffsetDate minusMonths(long months) { + return with(date.minusMonths(months), offset); + } + + /** + * Returns a copy of this {@code OffsetDate} with the specified period in weeks subtracted. + *

    + * This method subtracts the specified amount in weeks from the days field decrementing + * the month and year fields as necessary to ensure the result remains valid. + * The result is only invalid if the maximum/minimum year is exceeded. + *

    + * For example, 2009-01-07 minus one week would result in 2008-12-31. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param weeks the weeks to subtract, may be negative + * @return an {@code OffsetDate} based on this date with the weeks subtracted, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + public OffsetDate minusWeeks(long weeks) { + return with(date.minusWeeks(weeks), offset); + } + + /** + * Returns a copy of this {@code OffsetDate} with the specified number of days subtracted. + *

    + * This method subtracts the specified amount from the days field decrementing the + * month and year fields as necessary to ensure the result remains valid. + * The result is only invalid if the maximum/minimum year is exceeded. + *

    + * For example, 2009-01-01 minus one day would result in 2008-12-31. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param days the days to subtract, may be negative + * @return an {@code OffsetDate} based on this date with the days subtracted, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + public OffsetDate minusDays(long days) { + return with(date.minusDays(days), offset); + } + + //----------------------------------------------------------------------- + /** + * Queries this date using the specified query. + *

    + * This queries this date using the specified query strategy object. + * The {@code TemporalQuery} object defines the logic to be used to + * obtain the result. Read the documentation of the query to understand + * what the result of this method will be. + *

    + * The result of this method is obtained by invoking the + * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the + * specified query passing {@code this} as the argument. + * + * @param the type of the result + * @param query the query to invoke, not null + * @return the query result, null may be returned (defined by the query) + * @throws DateTimeException if unable to query (defined by the query) + * @throws ArithmeticException if numeric overflow occurs (defined by the query) + */ + @SuppressWarnings("unchecked") + @Override + public R query(TemporalQuery query) { + if (query == Queries.chrono()) { + return (R) ISOChrono.INSTANCE; + } else if (query == Queries.precision()) { + return (R) DAYS; + } else if (query == Queries.offset() || query == Queries.zone()) { + return (R) getOffset(); + } + return Temporal.super.query(query); + } + + /** + * Adjusts the specified temporal object to have the same offset and date + * as this object. + *

    + * This returns a temporal object of the same observable type as the input + * with the offset and date changed to be the same as this. + *

    + * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)} + * twice, passing {@link ChronoField#OFFSET_SECONDS} and + * {@link ChronoField#EPOCH_DAY} as the fields. + *

    + * In most cases, it is clearer to reverse the calling pattern by using + * {@link Temporal#with(TemporalAdjuster)}: + *

    +     *   // these two lines are equivalent, but the second approach is recommended
    +     *   temporal = thisOffsetDate.adjustInto(temporal);
    +     *   temporal = temporal.with(thisOffsetDate);
    +     * 
    + *

    + * This instance is immutable and unaffected by this method call. + * + * @param temporal the target object to be adjusted, not null + * @return the adjusted object, not null + * @throws DateTimeException if unable to make the adjustment + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public Temporal adjustInto(Temporal temporal) { + return temporal + .with(OFFSET_SECONDS, getOffset().getTotalSeconds()) + .with(EPOCH_DAY, getDate().toEpochDay()); + } + + /** + * Calculates the period between this date and another date in + * terms of the specified unit. + *

    + * This calculates the period between two dates in terms of a single unit. + * The start and end points are {@code this} and the specified date. + * The result will be negative if the end is before the start. + * For example, the period in days between two dates can be calculated + * using {@code startDate.periodUntil(endDate, DAYS)}. + *

    + * The {@code Temporal} passed to this method must be an {@code OffsetDate}. + * If the offset differs between the two times, then the specified + * end time is normalized to have the same offset as this time. + *

    + * The calculation returns a whole number, representing the number of + * complete units between the two dates. + * For example, the period in months between 2012-06-15Z and 2012-08-14Z + * will only be one month as it is one day short of two months. + *

    + * This method operates in association with {@link TemporalUnit#between}. + * The result of this method is a {@code long} representing the amount of + * the specified unit. By contrast, the result of {@code between} is an + * object that can be used directly in addition/subtraction: + *

    +     *   long period = start.periodUntil(end, MONTHS);   // this method
    +     *   dateTime.plus(MONTHS.between(start, end));      // use in plus/minus
    +     * 
    + *

    + * The calculation is implemented in this method for {@link ChronoUnit}. + * The units {@code DAYS}, {@code WEEKS}, {@code MONTHS}, {@code YEARS}, + * {@code DECADES}, {@code CENTURIES}, {@code MILLENNIA} and {@code ERAS} + * are supported. Other {@code ChronoUnit} values will throw an exception. + *

    + * If the unit is not a {@code ChronoUnit}, then the result of this method + * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)} + * passing {@code this} as the first argument and the input temporal as + * the second argument. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param endDate the end date, which must be an {@code OffsetDate}, not null + * @param unit the unit to measure the period in, not null + * @return the amount of the period between this date and the end date + * @throws DateTimeException if the period cannot be calculated + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public long periodUntil(Temporal endDate, TemporalUnit unit) { + if (endDate instanceof OffsetDate == false) { + Objects.requireNonNull(endDate, "endDate"); + throw new DateTimeException("Unable to calculate period between objects of two different types"); + } + if (unit instanceof ChronoUnit) { + OffsetDate end = (OffsetDate) endDate; + long offsetDiff = end.offset.getTotalSeconds() - offset.getTotalSeconds(); + LocalDate endLocal = end.date.plusDays(Math.floorDiv(-offsetDiff, SECONDS_PER_DAY)); + return date.periodUntil(endLocal, unit); + } + return unit.between(this, endDate).getAmount(); + } + + //----------------------------------------------------------------------- + /** + * Returns an offset date-time formed from this date at the specified time. + *

    + * This combines this date with the specified time to form an {@code OffsetDateTime}. + * All possible combinations of date and time are valid. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param time the time to combine with, not null + * @return the offset date-time formed from this date and the specified time, not null + */ + public OffsetDateTime atTime(LocalTime time) { + return OffsetDateTime.of(date, time, offset); + } + + //----------------------------------------------------------------------- + /** + * Converts this date to midnight at the start of day in epoch seconds. + * + * @return the epoch seconds value + */ + private long toEpochSecond() { + long epochDay = date.toEpochDay(); + long secs = epochDay * SECONDS_PER_DAY; + return secs - offset.getTotalSeconds(); + } + + //----------------------------------------------------------------------- + /** + * Compares this {@code OffsetDate} to another date. + *

    + * The comparison is based first on the UTC equivalent instant, then on the local date. + * It is "consistent with equals", as defined by {@link Comparable}. + *

    + * For example, the following is the comparator order: + *

      + *
    1. 2008-06-29-11:00
    2. + *
    3. 2008-06-29-12:00
    4. + *
    5. 2008-06-30+12:00
    6. + *
    7. 2008-06-29-13:00
    8. + *
    + * Values #2 and #3 represent the same instant on the time-line. + * When two values represent the same instant, the local date is compared + * to distinguish them. This step is needed to make the ordering + * consistent with {@code equals()}. + *

    + * To compare the underlying local date of two {@code TemporalAccessor} instances, + * use {@link ChronoField#EPOCH_DAY} as a comparator. + * + * @param other the other date to compare to, not null + * @return the comparator value, negative if less, positive if greater + */ + @Override + public int compareTo(OffsetDate other) { + if (offset.equals(other.offset)) { + return date.compareTo(other.date); + } + int compare = Long.compare(toEpochSecond(), other.toEpochSecond()); + if (compare == 0) { + compare = date.compareTo(other.date); + } + return compare; + } + + //----------------------------------------------------------------------- + /** + * Checks if the instant of midnight at the start of this {@code OffsetDate} + * is after midnight at the start of the specified date. + *

    + * This method differs from the comparison in {@link #compareTo} in that it + * only compares the instant of the date. This is equivalent to using + * {@code date1.toEpochSecond().isAfter(date2.toEpochSecond())}. + * + * @param other the other date to compare to, not null + * @return true if this is after the instant of the specified date + */ + public boolean isAfter(OffsetDate other) { + return toEpochSecond() > other.toEpochSecond(); + } + + /** + * Checks if the instant of midnight at the start of this {@code OffsetDate} + * is before midnight at the start of the specified date. + *

    + * This method differs from the comparison in {@link #compareTo} in that it + * only compares the instant of the date. This is equivalent to using + * {@code date1.toEpochSecond().isBefore(date2.toEpochSecond())}. + * + * @param other the other date to compare to, not null + * @return true if this is before the instant of the specified date + */ + public boolean isBefore(OffsetDate other) { + return toEpochSecond() < other.toEpochSecond(); + } + + /** + * Checks if the instant of midnight at the start of this {@code OffsetDate} + * equals midnight at the start of the specified date. + *

    + * This method differs from the comparison in {@link #compareTo} and {@link #equals} + * in that it only compares the instant of the date. This is equivalent to using + * {@code date1.toEpochSecond().equals(date2.toEpochSecond())}. + * + * @param other the other date to compare to, not null + * @return true if the instant equals the instant of the specified date + */ + public boolean isEqual(OffsetDate other) { + return toEpochSecond() == other.toEpochSecond(); + } + + //----------------------------------------------------------------------- + /** + * Checks if this date is equal to another date. + *

    + * The comparison is based on the local-date and the offset. + * To compare for the same instant on the time-line, use {@link #isEqual(OffsetDate)}. + *

    + * Only objects of type {@code OffsetDate} are compared, other types return false. + * To compare the underlying local date of two {@code TemporalAccessor} instances, + * use {@link ChronoField#EPOCH_DAY} as a comparator. + * + * @param obj the object to check, null returns false + * @return true if this is equal to the other date + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof OffsetDate) { + OffsetDate other = (OffsetDate) obj; + return date.equals(other.date) && offset.equals(other.offset); + } + return false; + } + + /** + * A hash code for this date. + * + * @return a suitable hash code + */ + @Override + public int hashCode() { + return date.hashCode() ^ offset.hashCode(); + } + + //----------------------------------------------------------------------- + /** + * Outputs this date as a {@code String}, such as {@code 2007-12-03+01:00}. + *

    + * The output will be in the ISO-8601 format {@code yyyy-MM-ddXXXXX}. + * + * @return a string representation of this date, not null + */ + @Override + public String toString() { + return date.toString() + offset.toString(); + } + + /** + * Outputs this date as a {@code String} using the formatter. + *

    + * This date will be passed to the formatter + * {@link DateTimeFormatter#print(TemporalAccessor) print method}. + * + * @param formatter the formatter to use, not null + * @return the formatted date string, not null + * @throws DateTimeException if an error occurs during printing + */ + public String toString(DateTimeFormatter formatter) { + Objects.requireNonNull(formatter, "formatter"); + return formatter.print(this); + } + + //----------------------------------------------------------------------- + /** + * Writes the object using a + * dedicated serialized form. + *

    +     *  out.writeByte(1);  // identifies this as a OffsetDateTime
    +     *  out.writeObject(date);
    +     *  out.writeObject(offset);
    +     * 
    + * + * @return the instance of {@code Ser}, not null + */ + private Object writeReplace() { + return new Ser(Ser.OFFSET_DATE_TYPE, this); + } + + /** + * Defend against malicious streams. + * @return never + * @throws InvalidObjectException always + */ + private Object readResolve() throws ObjectStreamException { + throw new InvalidObjectException("Deserialization via serialization delegate"); + } + + void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(date); + out.writeObject(offset); + } + + static OffsetDate readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + LocalDate date = (LocalDate) in.readObject(); + ZoneOffset offset = (ZoneOffset) in.readObject(); + return OffsetDate.of(date, offset); + } + +} diff --git a/jdk/src/share/classes/java/time/temporal/OffsetDateTime.java b/jdk/src/share/classes/java/time/temporal/OffsetDateTime.java new file mode 100644 index 00000000000..c7c2295a748 --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/OffsetDateTime.java @@ -0,0 +1,1824 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import static java.time.temporal.ChronoField.EPOCH_DAY; +import static java.time.temporal.ChronoField.INSTANT_SECONDS; +import static java.time.temporal.ChronoField.NANO_OF_DAY; +import static java.time.temporal.ChronoField.OFFSET_SECONDS; +import static java.time.temporal.ChronoUnit.NANOS; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.time.Clock; +import java.time.DateTimeException; +import java.time.DayOfWeek; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Month; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatters; +import java.time.format.DateTimeParseException; +import java.time.zone.ZoneRules; +import java.util.Comparator; +import java.util.Objects; + +/** + * A date-time with an offset from UTC/Greenwich in the ISO-8601 calendar system, + * such as {@code 2007-12-03T10:15:30+01:00}. + *

    + * {@code OffsetDateTime} is an immutable representation of a date-time with an offset. + * This class stores all date and time fields, to a precision of nanoseconds, + * as well as the offset from UTC/Greenwich. For example, the value + * "2nd October 2007 at 13:45.30.123456789 +02:00" can be stored in an {@code OffsetDateTime}. + *

    + * {@code OffsetDateTime}, {@link java.time.ZonedDateTime} and {@link java.time.Instant} all store an instant + * on the time-line to nanosecond precision. + * {@code Instant} is the simplest, simply representing the instant. + * {@code OffsetDateTime} adds to the instant the offset from UTC/Greenwich, which allows + * the local date-time to be obtained. + * {@code ZonedDateTime} adds full time-zone rules. + *

    + * It is intended that {@code ZonedDateTime} or {@code Instant} is used to model data + * in simpler applications. This class may be used when modeling date-time concepts in + * more detail, or when communicating to a database or in a network protocol. + * + *

    Specification for implementors

    + * This class is immutable and thread-safe. + * + * @since 1.8 + */ +public final class OffsetDateTime + implements Temporal, TemporalAdjuster, Comparable, Serializable { + + /** + * The minimum supported {@code OffsetDateTime}, '-999999999-01-01T00:00:00+18:00'. + * This is the local date-time of midnight at the start of the minimum date + * in the maximum offset (larger offsets are earlier on the time-line). + * This combines {@link LocalDateTime#MIN} and {@link ZoneOffset#MAX}. + * This could be used by an application as a "far past" date-time. + */ + public static final OffsetDateTime MIN = LocalDateTime.MIN.atOffset(ZoneOffset.MAX); + /** + * The maximum supported {@code OffsetDateTime}, '+999999999-12-31T23:59:59.999999999-18:00'. + * This is the local date-time just before midnight at the end of the maximum date + * in the minimum offset (larger negative offsets are later on the time-line). + * This combines {@link LocalDateTime#MAX} and {@link ZoneOffset#MIN}. + * This could be used by an application as a "far future" date-time. + */ + public static final OffsetDateTime MAX = LocalDateTime.MAX.atOffset(ZoneOffset.MIN); + + /** + * Comparator for two {@code OffsetDateTime} instances based solely on the instant. + *

    + * This method differs from the comparison in {@link #compareTo} in that it + * only compares the underlying instant. + * + * @see #isAfter + * @see #isBefore + * @see #isEqual + */ + public static final Comparator INSTANT_COMPARATOR = new Comparator() { + @Override + public int compare(OffsetDateTime datetime1, OffsetDateTime datetime2) { + int cmp = Long.compare(datetime1.toEpochSecond(), datetime2.toEpochSecond()); + if (cmp == 0) { + cmp = Long.compare(datetime1.getTime().toNanoOfDay(), datetime2.getTime().toNanoOfDay()); + } + return cmp; + } + }; + + /** + * Serialization version. + */ + private static final long serialVersionUID = 2287754244819255394L; + + /** + * The local date-time. + */ + private final LocalDateTime dateTime; + /** + * The offset from UTC/Greenwich. + */ + private final ZoneOffset offset; + + //----------------------------------------------------------------------- + /** + * Obtains the current date-time from the system clock in the default time-zone. + *

    + * This will query the {@link java.time.Clock#systemDefaultZone() system clock} in the default + * time-zone to obtain the current date-time. + * The offset will be calculated from the time-zone in the clock. + *

    + * Using this method will prevent the ability to use an alternate clock for testing + * because the clock is hard-coded. + * + * @return the current date-time using the system clock, not null + */ + public static OffsetDateTime now() { + return now(Clock.systemDefaultZone()); + } + + /** + * Obtains the current date-time from the system clock in the specified time-zone. + *

    + * This will query the {@link Clock#system(java.time.ZoneId) system clock} to obtain the current date-time. + * Specifying the time-zone avoids dependence on the default time-zone. + * The offset will be calculated from the specified time-zone. + *

    + * Using this method will prevent the ability to use an alternate clock for testing + * because the clock is hard-coded. + * + * @param zone the zone ID to use, not null + * @return the current date-time using the system clock, not null + */ + public static OffsetDateTime now(ZoneId zone) { + return now(Clock.system(zone)); + } + + /** + * Obtains the current date-time from the specified clock. + *

    + * This will query the specified clock to obtain the current date-time. + * The offset will be calculated from the time-zone in the clock. + *

    + * Using this method allows the use of an alternate clock for testing. + * The alternate clock may be introduced using {@link Clock dependency injection}. + * + * @param clock the clock to use, not null + * @return the current date-time, not null + */ + public static OffsetDateTime now(Clock clock) { + Objects.requireNonNull(clock, "clock"); + final Instant now = clock.instant(); // called once + return ofInstant(now, clock.getZone().getRules().getOffset(now)); + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code OffsetDateTime} from a date, time and offset. + *

    + * This creates an offset date-time with the specified local date, time and offset. + * + * @param date the local date, not null + * @param time the local time, not null + * @param offset the zone offset, not null + * @return the offset date-time, not null + */ + public static OffsetDateTime of(LocalDate date, LocalTime time, ZoneOffset offset) { + LocalDateTime dt = LocalDateTime.of(date, time); + return new OffsetDateTime(dt, offset); + } + + /** + * Obtains an instance of {@code OffsetDateTime} from a date-time and offset. + *

    + * This creates an offset date-time with the specified local date-time and offset. + * + * @param dateTime the local date-time, not null + * @param offset the zone offset, not null + * @return the offset date-time, not null + */ + public static OffsetDateTime of(LocalDateTime dateTime, ZoneOffset offset) { + return new OffsetDateTime(dateTime, offset); + } + + /** + * Obtains an instance of {@code OffsetDateTime} from a {@code ZonedDateTime}. + *

    + * This creates an offset date-time with the same local date-time and offset as + * the zoned date-time. The result will have the same instant as the input. + * + * @param zonedDateTime the zoned date-time to convert from, not null + * @return the offset date-time, not null + * @throws DateTimeException if the result exceeds the supported range + */ + public static OffsetDateTime of(ZonedDateTime zonedDateTime) { + Objects.requireNonNull(zonedDateTime, "zonedDateTime"); + return new OffsetDateTime(zonedDateTime.getDateTime(), zonedDateTime.getOffset()); + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code OffsetDateTime} from an {@code Instant} and zone ID. + *

    + * This creates an offset date-time with the same instant as that specified. + * Finding the offset from UTC/Greenwich is simple as there is only one valid + * offset for each instant. + * + * @param instant the instant to create the date-time from, not null + * @param zone the time-zone, which may be an offset, not null + * @return the offset date-time, not null + * @throws DateTimeException if the result exceeds the supported range + */ + public static OffsetDateTime ofInstant(Instant instant, ZoneId zone) { + Objects.requireNonNull(instant, "instant"); + Objects.requireNonNull(zone, "zone"); + ZoneRules rules = zone.getRules(); + ZoneOffset offset = rules.getOffset(instant); + LocalDateTime ldt = LocalDateTime.ofEpochSecond(instant.getEpochSecond(), instant.getNano(), offset); + return new OffsetDateTime(ldt, offset); + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code OffsetDateTime} from a temporal object. + *

    + * A {@code TemporalAccessor} represents some form of date and time information. + * This factory converts the arbitrary temporal object to an instance of {@code OffsetDateTime}. + *

    + * The conversion extracts and combines {@code LocalDateTime} and {@code ZoneOffset}. + * If that fails it will try to extract and combine {@code Instant} and {@code ZoneOffset}. + *

    + * This method matches the signature of the functional interface {@link TemporalQuery} + * allowing it to be used in queries via method reference, {@code OffsetDateTime::from}. + * + * @param temporal the temporal object to convert, not null + * @return the offset date-time, not null + * @throws DateTimeException if unable to convert to an {@code OffsetDateTime} + */ + public static OffsetDateTime from(TemporalAccessor temporal) { + if (temporal instanceof OffsetDateTime) { + return (OffsetDateTime) temporal; + } + ZoneOffset offset = ZoneOffset.from(temporal); + try { + try { + LocalDateTime ldt = LocalDateTime.from(temporal); + return OffsetDateTime.of(ldt, offset); + } catch (DateTimeException ignore) { + Instant instant = Instant.from(temporal); + return OffsetDateTime.ofInstant(instant, offset); + } + } catch (DateTimeException ex) { + throw new DateTimeException("Unable to obtain OffsetDateTime from TemporalAccessor: " + temporal.getClass(), ex); + } + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code OffsetDateTime} from a text string + * such as {@code 2007-12-03T10:15:30+01:00}. + *

    + * The string must represent a valid date-time and is parsed using + * {@link java.time.format.DateTimeFormatters#isoOffsetDateTime()}. + * + * @param text the text to parse such as "2007-12-03T10:15:30+01:00", not null + * @return the parsed offset date-time, not null + * @throws DateTimeParseException if the text cannot be parsed + */ + public static OffsetDateTime parse(CharSequence text) { + return parse(text, DateTimeFormatters.isoOffsetDateTime()); + } + + /** + * Obtains an instance of {@code OffsetDateTime} from a text string using a specific formatter. + *

    + * The text is parsed using the formatter, returning a date-time. + * + * @param text the text to parse, not null + * @param formatter the formatter to use, not null + * @return the parsed offset date-time, not null + * @throws DateTimeParseException if the text cannot be parsed + */ + public static OffsetDateTime parse(CharSequence text, DateTimeFormatter formatter) { + Objects.requireNonNull(formatter, "formatter"); + return formatter.parse(text, OffsetDateTime::from); + } + + //----------------------------------------------------------------------- + /** + * Constructor. + * + * @param dateTime the local date-time, not null + * @param offset the zone offset, not null + */ + private OffsetDateTime(LocalDateTime dateTime, ZoneOffset offset) { + this.dateTime = Objects.requireNonNull(dateTime, "dateTime"); + this.offset = Objects.requireNonNull(offset, "offset"); + } + + /** + * Returns a new date-time based on this one, returning {@code this} where possible. + * + * @param dateTime the date-time to create with, not null + * @param offset the zone offset to create with, not null + */ + private OffsetDateTime with(LocalDateTime dateTime, ZoneOffset offset) { + if (this.dateTime == dateTime && this.offset.equals(offset)) { + return this; + } + return new OffsetDateTime(dateTime, offset); + } + + //----------------------------------------------------------------------- + /** + * Checks if the specified field is supported. + *

    + * This checks if this date-time can be queried for the specified field. + * If false, then calling the {@link #range(TemporalField) range} and + * {@link #get(TemporalField) get} methods will throw an exception. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The supported fields are: + *

      + *
    • {@code NANO_OF_SECOND} + *
    • {@code NANO_OF_DAY} + *
    • {@code MICRO_OF_SECOND} + *
    • {@code MICRO_OF_DAY} + *
    • {@code MILLI_OF_SECOND} + *
    • {@code MILLI_OF_DAY} + *
    • {@code SECOND_OF_MINUTE} + *
    • {@code SECOND_OF_DAY} + *
    • {@code MINUTE_OF_HOUR} + *
    • {@code MINUTE_OF_DAY} + *
    • {@code HOUR_OF_AMPM} + *
    • {@code CLOCK_HOUR_OF_AMPM} + *
    • {@code HOUR_OF_DAY} + *
    • {@code CLOCK_HOUR_OF_DAY} + *
    • {@code AMPM_OF_DAY} + *
    • {@code DAY_OF_WEEK} + *
    • {@code ALIGNED_DAY_OF_WEEK_IN_MONTH} + *
    • {@code ALIGNED_DAY_OF_WEEK_IN_YEAR} + *
    • {@code DAY_OF_MONTH} + *
    • {@code DAY_OF_YEAR} + *
    • {@code EPOCH_DAY} + *
    • {@code ALIGNED_WEEK_OF_MONTH} + *
    • {@code ALIGNED_WEEK_OF_YEAR} + *
    • {@code MONTH_OF_YEAR} + *
    • {@code EPOCH_MONTH} + *
    • {@code YEAR_OF_ERA} + *
    • {@code YEAR} + *
    • {@code ERA} + *
    • {@code INSTANT_SECONDS} + *
    • {@code OFFSET_SECONDS} + *
    + * All other {@code ChronoField} instances will return false. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doIsSupported(TemporalAccessor)} + * passing {@code this} as the argument. + * Whether the field is supported is determined by the field. + * + * @param field the field to check, null returns false + * @return true if the field is supported on this date-time, false if not + */ + @Override + public boolean isSupported(TemporalField field) { + return field instanceof ChronoField || (field != null && field.doIsSupported(this)); + } + + /** + * Gets the range of valid values for the specified field. + *

    + * The range object expresses the minimum and maximum valid values for a field. + * This date-time is used to enhance the accuracy of the returned range. + * If it is not possible to return the range, because the field is not supported + * or for some other reason, an exception is thrown. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@link #isSupported(TemporalField) supported fields} will return + * appropriate range instances. + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doRange(TemporalAccessor)} + * passing {@code this} as the argument. + * Whether the range can be obtained is determined by the field. + * + * @param field the field to query the range for, not null + * @return the range of valid values for the field, not null + * @throws DateTimeException if the range for the field cannot be obtained + */ + @Override + public ValueRange range(TemporalField field) { + if (field instanceof ChronoField) { + if (field == INSTANT_SECONDS || field == OFFSET_SECONDS) { + return field.range(); + } + return dateTime.range(field); + } + return field.doRange(this); + } + + /** + * Gets the value of the specified field from this date-time as an {@code int}. + *

    + * This queries this date-time for the value for the specified field. + * The returned value will always be within the valid range of values for the field. + * If it is not possible to return the value, because the field is not supported + * or for some other reason, an exception is thrown. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@link #isSupported(TemporalField) supported fields} will return valid + * values based on this date-time, except {@code NANO_OF_DAY}, {@code MICRO_OF_DAY}, + * {@code EPOCH_DAY}, {@code EPOCH_MONTH} and {@code INSTANT_SECONDS} which are too + * large to fit in an {@code int} and throw a {@code DateTimeException}. + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doGet(TemporalAccessor)} + * passing {@code this} as the argument. Whether the value can be obtained, + * and what the value represents, is determined by the field. + * + * @param field the field to get, not null + * @return the value for the field + * @throws DateTimeException if a value for the field cannot be obtained + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public int get(TemporalField field) { + if (field instanceof ChronoField) { + switch ((ChronoField) field) { + case INSTANT_SECONDS: throw new DateTimeException("Field too large for an int: " + field); + case OFFSET_SECONDS: return getOffset().getTotalSeconds(); + } + return dateTime.get(field); + } + return Temporal.super.get(field); + } + + /** + * Gets the value of the specified field from this date-time as a {@code long}. + *

    + * This queries this date-time for the value for the specified field. + * If it is not possible to return the value, because the field is not supported + * or for some other reason, an exception is thrown. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@link #isSupported(TemporalField) supported fields} will return valid + * values based on this date-time. + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doGet(TemporalAccessor)} + * passing {@code this} as the argument. Whether the value can be obtained, + * and what the value represents, is determined by the field. + * + * @param field the field to get, not null + * @return the value for the field + * @throws DateTimeException if a value for the field cannot be obtained + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public long getLong(TemporalField field) { + if (field instanceof ChronoField) { + switch ((ChronoField) field) { + case INSTANT_SECONDS: return toEpochSecond(); + case OFFSET_SECONDS: return getOffset().getTotalSeconds(); + } + return dateTime.getLong(field); + } + return field.doGet(this); + } + + //----------------------------------------------------------------------- + /** + * Gets the zone offset, such as '+01:00'. + *

    + * This is the offset of the local date-time from UTC/Greenwich. + * + * @return the zone offset, not null + */ + public ZoneOffset getOffset() { + return offset; + } + + /** + * Returns a copy of this {@code OffsetDateTime} with the specified offset ensuring + * that the result has the same local date-time. + *

    + * This method returns an object with the same {@code LocalDateTime} and the specified {@code ZoneOffset}. + * No calculation is needed or performed. + * For example, if this time represents {@code 2007-12-03T10:30+02:00} and the offset specified is + * {@code +03:00}, then this method will return {@code 2007-12-03T10:30+03:00}. + *

    + * To take into account the difference between the offsets, and adjust the time fields, + * use {@link #withOffsetSameInstant}. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param offset the zone offset to change to, not null + * @return an {@code OffsetDateTime} based on this date-time with the requested offset, not null + */ + public OffsetDateTime withOffsetSameLocal(ZoneOffset offset) { + return with(dateTime, offset); + } + + /** + * Returns a copy of this {@code OffsetDateTime} with the specified offset ensuring + * that the result is at the same instant. + *

    + * This method returns an object with the specified {@code ZoneOffset} and a {@code LocalDateTime} + * adjusted by the difference between the two offsets. + * This will result in the old and new objects representing the same instant. + * This is useful for finding the local time in a different offset. + * For example, if this time represents {@code 2007-12-03T10:30+02:00} and the offset specified is + * {@code +03:00}, then this method will return {@code 2007-12-03T11:30+03:00}. + *

    + * To change the offset without adjusting the local time use {@link #withOffsetSameLocal}. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param offset the zone offset to change to, not null + * @return an {@code OffsetDateTime} based on this date-time with the requested offset, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + public OffsetDateTime withOffsetSameInstant(ZoneOffset offset) { + if (offset.equals(this.offset)) { + return this; + } + int difference = offset.getTotalSeconds() - this.offset.getTotalSeconds(); + LocalDateTime adjusted = dateTime.plusSeconds(difference); + return new OffsetDateTime(adjusted, offset); + } + + //----------------------------------------------------------------------- + /** + * Gets the {@code LocalDateTime} part of this offset date-time. + *

    + * This returns a {@code LocalDateTime} with the same year, month, day and time + * as this date-time. + * + * @return the local date-time part of this date-time, not null + */ + public LocalDateTime getDateTime() { + return dateTime; + } + + //----------------------------------------------------------------------- + /** + * Gets the {@code LocalDate} part of this date-time. + *

    + * This returns a {@code LocalDate} with the same year, month and day + * as this date-time. + * + * @return the date part of this date-time, not null + */ + public LocalDate getDate() { + return dateTime.getDate(); + } + + /** + * Gets the year field. + *

    + * This method returns the primitive {@code int} value for the year. + *

    + * The year returned by this method is proleptic as per {@code get(YEAR)}. + * To obtain the year-of-era, use {@code get(YEAR_OF_ERA}. + * + * @return the year, from MIN_YEAR to MAX_YEAR + */ + public int getYear() { + return dateTime.getYear(); + } + + /** + * Gets the month-of-year field from 1 to 12. + *

    + * This method returns the month as an {@code int} from 1 to 12. + * Application code is frequently clearer if the enum {@link Month} + * is used by calling {@link #getMonth()}. + * + * @return the month-of-year, from 1 to 12 + * @see #getMonth() + */ + public int getMonthValue() { + return dateTime.getMonthValue(); + } + + /** + * Gets the month-of-year field using the {@code Month} enum. + *

    + * This method returns the enum {@link Month} for the month. + * This avoids confusion as to what {@code int} values mean. + * If you need access to the primitive {@code int} value then the enum + * provides the {@link Month#getValue() int value}. + * + * @return the month-of-year, not null + * @see #getMonthValue() + */ + public Month getMonth() { + return dateTime.getMonth(); + } + + /** + * Gets the day-of-month field. + *

    + * This method returns the primitive {@code int} value for the day-of-month. + * + * @return the day-of-month, from 1 to 31 + */ + public int getDayOfMonth() { + return dateTime.getDayOfMonth(); + } + + /** + * Gets the day-of-year field. + *

    + * This method returns the primitive {@code int} value for the day-of-year. + * + * @return the day-of-year, from 1 to 365, or 366 in a leap year + */ + public int getDayOfYear() { + return dateTime.getDayOfYear(); + } + + /** + * Gets the day-of-week field, which is an enum {@code DayOfWeek}. + *

    + * This method returns the enum {@link java.time.DayOfWeek} for the day-of-week. + * This avoids confusion as to what {@code int} values mean. + * If you need access to the primitive {@code int} value then the enum + * provides the {@link java.time.DayOfWeek#getValue() int value}. + *

    + * Additional information can be obtained from the {@code DayOfWeek}. + * This includes textual names of the values. + * + * @return the day-of-week, not null + */ + public DayOfWeek getDayOfWeek() { + return dateTime.getDayOfWeek(); + } + + //----------------------------------------------------------------------- + /** + * Gets the {@code LocalTime} part of this date-time. + *

    + * This returns a {@code LocalTime} with the same hour, minute, second and + * nanosecond as this date-time. + * + * @return the time part of this date-time, not null + */ + public LocalTime getTime() { + return dateTime.getTime(); + } + + /** + * Gets the hour-of-day field. + * + * @return the hour-of-day, from 0 to 23 + */ + public int getHour() { + return dateTime.getHour(); + } + + /** + * Gets the minute-of-hour field. + * + * @return the minute-of-hour, from 0 to 59 + */ + public int getMinute() { + return dateTime.getMinute(); + } + + /** + * Gets the second-of-minute field. + * + * @return the second-of-minute, from 0 to 59 + */ + public int getSecond() { + return dateTime.getSecond(); + } + + /** + * Gets the nano-of-second field. + * + * @return the nano-of-second, from 0 to 999,999,999 + */ + public int getNano() { + return dateTime.getNano(); + } + + //----------------------------------------------------------------------- + /** + * Returns an adjusted copy of this date-time. + *

    + * This returns a new {@code OffsetDateTime}, based on this one, with the date-time adjusted. + * The adjustment takes place using the specified adjuster strategy object. + * Read the documentation of the adjuster to understand what adjustment will be made. + *

    + * A simple adjuster might simply set the one of the fields, such as the year field. + * A more complex adjuster might set the date to the last day of the month. + * A selection of common adjustments is provided in {@link java.time.temporal.Adjusters}. + * These include finding the "last day of the month" and "next Wednesday". + * Key date-time classes also implement the {@code TemporalAdjuster} interface, + * such as {@link Month} and {@link java.time.temporal.MonthDay MonthDay}. + * The adjuster is responsible for handling special cases, such as the varying + * lengths of month and leap years. + *

    + * For example this code returns a date on the last day of July: + *

    +     *  import static java.time.Month.*;
    +     *  import static java.time.temporal.Adjusters.*;
    +     *
    +     *  result = offsetDateTime.with(JULY).with(lastDayOfMonth());
    +     * 
    + *

    + * The classes {@link LocalDate}, {@link LocalTime} and {@link ZoneOffset} implement + * {@code TemporalAdjuster}, thus this method can be used to change the date, time or offset: + *

    +     *  result = offsetDateTime.with(date);
    +     *  result = offsetDateTime.with(time);
    +     *  result = offsetDateTime.with(offset);
    +     * 
    + *

    + * The result of this method is obtained by invoking the + * {@link TemporalAdjuster#adjustInto(Temporal)} method on the + * specified adjuster passing {@code this} as the argument. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param adjuster the adjuster to use, not null + * @return an {@code OffsetDateTime} based on {@code this} with the adjustment made, not null + * @throws DateTimeException if the adjustment cannot be made + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public OffsetDateTime with(TemporalAdjuster adjuster) { + // optimizations + if (adjuster instanceof LocalDate || adjuster instanceof LocalTime || adjuster instanceof LocalDateTime) { + return with(dateTime.with(adjuster), offset); + } else if (adjuster instanceof Instant) { + return ofInstant((Instant) adjuster, offset); + } else if (adjuster instanceof ZoneOffset) { + return with(dateTime, (ZoneOffset) adjuster); + } else if (adjuster instanceof OffsetDateTime) { + return (OffsetDateTime) adjuster; + } + return (OffsetDateTime) adjuster.adjustInto(this); + } + + /** + * Returns a copy of this date-time with the specified field set to a new value. + *

    + * This returns a new {@code OffsetDateTime}, based on this one, with the value + * for the specified field changed. + * This can be used to change any supported field, such as the year, month or day-of-month. + * If it is not possible to set the value, because the field is not supported or for + * some other reason, an exception is thrown. + *

    + * In some cases, changing the specified field can cause the resulting date-time to become invalid, + * such as changing the month from 31st January to February would make the day-of-month invalid. + * In cases like this, the field is responsible for resolving the date. Typically it will choose + * the previous valid date, which would be the last valid day of February in this example. + *

    + * If the field is a {@link ChronoField} then the adjustment is implemented here. + *

    + * The {@code INSTANT_SECONDS} field will return a date-time with the specified instant. + * The offset and nano-of-second are unchanged. + * If the new instant value is outside the valid range then a {@code DateTimeException} will be thrown. + *

    + * The {@code OFFSET_SECONDS} field will return a date-time with the specified offset. + * The local date-time is unaltered. If the new offset value is outside the valid range + * then a {@code DateTimeException} will be thrown. + *

    + * The other {@link #isSupported(TemporalField) supported fields} will behave as per + * the matching method on {@link LocalDateTime#with(TemporalField, long) LocalDateTime}. + * In this case, the offset is not part of the calculation and will be unchanged. + *

    + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doWith(Temporal, long)} + * passing {@code this} as the argument. In this case, the field determines + * whether and how to adjust the instant. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param field the field to set in the result, not null + * @param newValue the new value of the field in the result + * @return an {@code OffsetDateTime} based on {@code this} with the specified field set, not null + * @throws DateTimeException if the field cannot be set + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public OffsetDateTime with(TemporalField field, long newValue) { + if (field instanceof ChronoField) { + ChronoField f = (ChronoField) field; + switch (f) { + case INSTANT_SECONDS: return ofInstant(Instant.ofEpochSecond(newValue, getNano()), offset); + case OFFSET_SECONDS: { + return with(dateTime, ZoneOffset.ofTotalSeconds(f.checkValidIntValue(newValue))); + } + } + return with(dateTime.with(field, newValue), offset); + } + return field.doWith(this, newValue); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this {@code OffsetDateTime} with the year altered. + * The offset does not affect the calculation and will be the same in the result. + * If the day-of-month is invalid for the year, it will be changed to the last valid day of the month. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param year the year to set in the result, from MIN_YEAR to MAX_YEAR + * @return an {@code OffsetDateTime} based on this date-time with the requested year, not null + * @throws DateTimeException if the year value is invalid + */ + public OffsetDateTime withYear(int year) { + return with(dateTime.withYear(year), offset); + } + + /** + * Returns a copy of this {@code OffsetDateTime} with the month-of-year altered. + * The offset does not affect the calculation and will be the same in the result. + * If the day-of-month is invalid for the year, it will be changed to the last valid day of the month. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param month the month-of-year to set in the result, from 1 (January) to 12 (December) + * @return an {@code OffsetDateTime} based on this date-time with the requested month, not null + * @throws DateTimeException if the month-of-year value is invalid + */ + public OffsetDateTime withMonth(int month) { + return with(dateTime.withMonth(month), offset); + } + + /** + * Returns a copy of this {@code OffsetDateTime} with the day-of-month altered. + * If the resulting {@code OffsetDateTime} is invalid, an exception is thrown. + * The offset does not affect the calculation and will be the same in the result. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param dayOfMonth the day-of-month to set in the result, from 1 to 28-31 + * @return an {@code OffsetDateTime} based on this date-time with the requested day, not null + * @throws DateTimeException if the day-of-month value is invalid + * @throws DateTimeException if the day-of-month is invalid for the month-year + */ + public OffsetDateTime withDayOfMonth(int dayOfMonth) { + return with(dateTime.withDayOfMonth(dayOfMonth), offset); + } + + /** + * Returns a copy of this {@code OffsetDateTime} with the day-of-year altered. + * If the resulting {@code OffsetDateTime} is invalid, an exception is thrown. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param dayOfYear the day-of-year to set in the result, from 1 to 365-366 + * @return an {@code OffsetDateTime} based on this date with the requested day, not null + * @throws DateTimeException if the day-of-year value is invalid + * @throws DateTimeException if the day-of-year is invalid for the year + */ + public OffsetDateTime withDayOfYear(int dayOfYear) { + return with(dateTime.withDayOfYear(dayOfYear), offset); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this {@code OffsetDateTime} with the hour-of-day value altered. + *

    + * The offset does not affect the calculation and will be the same in the result. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param hour the hour-of-day to set in the result, from 0 to 23 + * @return an {@code OffsetDateTime} based on this date-time with the requested hour, not null + * @throws DateTimeException if the hour value is invalid + */ + public OffsetDateTime withHour(int hour) { + return with(dateTime.withHour(hour), offset); + } + + /** + * Returns a copy of this {@code OffsetDateTime} with the minute-of-hour value altered. + *

    + * The offset does not affect the calculation and will be the same in the result. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param minute the minute-of-hour to set in the result, from 0 to 59 + * @return an {@code OffsetDateTime} based on this date-time with the requested minute, not null + * @throws DateTimeException if the minute value is invalid + */ + public OffsetDateTime withMinute(int minute) { + return with(dateTime.withMinute(minute), offset); + } + + /** + * Returns a copy of this {@code OffsetDateTime} with the second-of-minute value altered. + *

    + * The offset does not affect the calculation and will be the same in the result. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param second the second-of-minute to set in the result, from 0 to 59 + * @return an {@code OffsetDateTime} based on this date-time with the requested second, not null + * @throws DateTimeException if the second value is invalid + */ + public OffsetDateTime withSecond(int second) { + return with(dateTime.withSecond(second), offset); + } + + /** + * Returns a copy of this {@code OffsetDateTime} with the nano-of-second value altered. + *

    + * The offset does not affect the calculation and will be the same in the result. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param nanoOfSecond the nano-of-second to set in the result, from 0 to 999,999,999 + * @return an {@code OffsetDateTime} based on this date-time with the requested nanosecond, not null + * @throws DateTimeException if the nanos value is invalid + */ + public OffsetDateTime withNano(int nanoOfSecond) { + return with(dateTime.withNano(nanoOfSecond), offset); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this {@code OffsetDateTime} with the time truncated. + *

    + * Truncation returns a copy of the original date-time with fields + * smaller than the specified unit set to zero. + * For example, truncating with the {@link ChronoUnit#MINUTES minutes} unit + * will set the second-of-minute and nano-of-second field to zero. + *

    + * Not all units are accepted. The {@link ChronoUnit#DAYS days} unit and time + * units with an exact duration can be used, other units throw an exception. + *

    + * The offset does not affect the calculation and will be the same in the result. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param unit the unit to truncate to, not null + * @return an {@code OffsetDateTime} based on this date-time with the time truncated, not null + * @throws DateTimeException if unable to truncate + */ + public OffsetDateTime truncatedTo(TemporalUnit unit) { + return with(dateTime.truncatedTo(unit), offset); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this date-time with the specified period added. + *

    + * This method returns a new date-time based on this time with the specified period added. + * The adder is typically {@link java.time.Period} but may be any other type implementing + * the {@link TemporalAdder} interface. + * The calculation is delegated to the specified adjuster, which typically calls + * back to {@link #plus(long, TemporalUnit)}. + * The offset is not part of the calculation and will be unchanged in the result. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param adder the adder to use, not null + * @return an {@code OffsetDateTime} based on this date-time with the addition made, not null + * @throws DateTimeException if the addition cannot be made + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public OffsetDateTime plus(TemporalAdder adder) { + return (OffsetDateTime) adder.addTo(this); + } + + /** + * Returns a copy of this date-time with the specified period added. + *

    + * This method returns a new date-time based on this date-time with the specified period added. + * This can be used to add any period that is defined by a unit, for example to add years, months or days. + * The unit is responsible for the details of the calculation, including the resolution + * of any edge cases in the calculation. + * The offset is not part of the calculation and will be unchanged in the result. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param amountToAdd the amount of the unit to add to the result, may be negative + * @param unit the unit of the period to add, not null + * @return an {@code OffsetDateTime} based on this date-time with the specified period added, not null + * @throws DateTimeException if the unit cannot be added to this type + */ + @Override + public OffsetDateTime plus(long amountToAdd, TemporalUnit unit) { + if (unit instanceof ChronoUnit) { + return with(dateTime.plus(amountToAdd, unit), offset); + } + return unit.doPlus(this, amountToAdd); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this {@code OffsetDateTime} with the specified period in years added. + *

    + * This method adds the specified amount to the years field in three steps: + *

      + *
    1. Add the input years to the year field
    2. + *
    3. Check if the resulting date would be invalid
    4. + *
    5. Adjust the day-of-month to the last valid day if necessary
    6. + *
    + *

    + * For example, 2008-02-29 (leap year) plus one year would result in the + * invalid date 2009-02-29 (standard year). Instead of returning an invalid + * result, the last valid day of the month, 2009-02-28, is selected instead. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param years the years to add, may be negative + * @return an {@code OffsetDateTime} based on this date-time with the years added, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + public OffsetDateTime plusYears(long years) { + return with(dateTime.plusYears(years), offset); + } + + /** + * Returns a copy of this {@code OffsetDateTime} with the specified period in months added. + *

    + * This method adds the specified amount to the months field in three steps: + *

      + *
    1. Add the input months to the month-of-year field
    2. + *
    3. Check if the resulting date would be invalid
    4. + *
    5. Adjust the day-of-month to the last valid day if necessary
    6. + *
    + *

    + * For example, 2007-03-31 plus one month would result in the invalid date + * 2007-04-31. Instead of returning an invalid result, the last valid day + * of the month, 2007-04-30, is selected instead. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param months the months to add, may be negative + * @return an {@code OffsetDateTime} based on this date-time with the months added, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + public OffsetDateTime plusMonths(long months) { + return with(dateTime.plusMonths(months), offset); + } + + /** + * Returns a copy of this OffsetDateTime with the specified period in weeks added. + *

    + * This method adds the specified amount in weeks to the days field incrementing + * the month and year fields as necessary to ensure the result remains valid. + * The result is only invalid if the maximum/minimum year is exceeded. + *

    + * For example, 2008-12-31 plus one week would result in the 2009-01-07. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param weeks the weeks to add, may be negative + * @return an {@code OffsetDateTime} based on this date-time with the weeks added, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + public OffsetDateTime plusWeeks(long weeks) { + return with(dateTime.plusWeeks(weeks), offset); + } + + /** + * Returns a copy of this OffsetDateTime with the specified period in days added. + *

    + * This method adds the specified amount to the days field incrementing the + * month and year fields as necessary to ensure the result remains valid. + * The result is only invalid if the maximum/minimum year is exceeded. + *

    + * For example, 2008-12-31 plus one day would result in the 2009-01-01. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param days the days to add, may be negative + * @return an {@code OffsetDateTime} based on this date-time with the days added, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + public OffsetDateTime plusDays(long days) { + return with(dateTime.plusDays(days), offset); + } + + /** + * Returns a copy of this {@code OffsetDateTime} with the specified period in hours added. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param hours the hours to add, may be negative + * @return an {@code OffsetDateTime} based on this date-time with the hours added, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + public OffsetDateTime plusHours(long hours) { + return with(dateTime.plusHours(hours), offset); + } + + /** + * Returns a copy of this {@code OffsetDateTime} with the specified period in minutes added. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param minutes the minutes to add, may be negative + * @return an {@code OffsetDateTime} based on this date-time with the minutes added, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + public OffsetDateTime plusMinutes(long minutes) { + return with(dateTime.plusMinutes(minutes), offset); + } + + /** + * Returns a copy of this {@code OffsetDateTime} with the specified period in seconds added. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param seconds the seconds to add, may be negative + * @return an {@code OffsetDateTime} based on this date-time with the seconds added, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + public OffsetDateTime plusSeconds(long seconds) { + return with(dateTime.plusSeconds(seconds), offset); + } + + /** + * Returns a copy of this {@code OffsetDateTime} with the specified period in nanoseconds added. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param nanos the nanos to add, may be negative + * @return an {@code OffsetDateTime} based on this date-time with the nanoseconds added, not null + * @throws DateTimeException if the unit cannot be added to this type + */ + public OffsetDateTime plusNanos(long nanos) { + return with(dateTime.plusNanos(nanos), offset); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this date-time with the specified period subtracted. + *

    + * This method returns a new date-time based on this time with the specified period subtracted. + * The subtractor is typically {@link java.time.Period} but may be any other type implementing + * the {@link TemporalSubtractor} interface. + * The calculation is delegated to the specified adjuster, which typically calls + * back to {@link #minus(long, TemporalUnit)}. + * The offset is not part of the calculation and will be unchanged in the result. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param subtractor the subtractor to use, not null + * @return an {@code OffsetDateTime} based on this date-time with the subtraction made, not null + * @throws DateTimeException if the subtraction cannot be made + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public OffsetDateTime minus(TemporalSubtractor subtractor) { + return (OffsetDateTime) subtractor.subtractFrom(this); + } + + /** + * Returns a copy of this date-time with the specified period subtracted. + *

    + * This method returns a new date-time based on this date-time with the specified period subtracted. + * This can be used to subtract any period that is defined by a unit, for example to subtract years, months or days. + * The unit is responsible for the details of the calculation, including the resolution + * of any edge cases in the calculation. + * The offset is not part of the calculation and will be unchanged in the result. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param amountToSubtract the amount of the unit to subtract from the result, may be negative + * @param unit the unit of the period to subtract, not null + * @return an {@code OffsetDateTime} based on this date-time with the specified period subtracted, not null + */ + @Override + public OffsetDateTime minus(long amountToSubtract, TemporalUnit unit) { + return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit)); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this {@code OffsetDateTime} with the specified period in years subtracted. + *

    + * This method subtracts the specified amount from the years field in three steps: + *

      + *
    1. Subtract the input years to the year field
    2. + *
    3. Check if the resulting date would be invalid
    4. + *
    5. Adjust the day-of-month to the last valid day if necessary
    6. + *
    + *

    + * For example, 2008-02-29 (leap year) minus one year would result in the + * invalid date 2009-02-29 (standard year). Instead of returning an invalid + * result, the last valid day of the month, 2009-02-28, is selected instead. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param years the years to subtract, may be negative + * @return an {@code OffsetDateTime} based on this date-time with the years subtracted, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + public OffsetDateTime minusYears(long years) { + return (years == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-years)); + } + + /** + * Returns a copy of this {@code OffsetDateTime} with the specified period in months subtracted. + *

    + * This method subtracts the specified amount from the months field in three steps: + *

      + *
    1. Subtract the input months to the month-of-year field
    2. + *
    3. Check if the resulting date would be invalid
    4. + *
    5. Adjust the day-of-month to the last valid day if necessary
    6. + *
    + *

    + * For example, 2007-03-31 minus one month would result in the invalid date + * 2007-04-31. Instead of returning an invalid result, the last valid day + * of the month, 2007-04-30, is selected instead. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param months the months to subtract, may be negative + * @return an {@code OffsetDateTime} based on this date-time with the months subtracted, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + public OffsetDateTime minusMonths(long months) { + return (months == Long.MIN_VALUE ? plusMonths(Long.MAX_VALUE).plusMonths(1) : plusMonths(-months)); + } + + /** + * Returns a copy of this {@code OffsetDateTime} with the specified period in weeks subtracted. + *

    + * This method subtracts the specified amount in weeks from the days field decrementing + * the month and year fields as necessary to ensure the result remains valid. + * The result is only invalid if the maximum/minimum year is exceeded. + *

    + * For example, 2008-12-31 minus one week would result in the 2009-01-07. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param weeks the weeks to subtract, may be negative + * @return an {@code OffsetDateTime} based on this date-time with the weeks subtracted, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + public OffsetDateTime minusWeeks(long weeks) { + return (weeks == Long.MIN_VALUE ? plusWeeks(Long.MAX_VALUE).plusWeeks(1) : plusWeeks(-weeks)); + } + + /** + * Returns a copy of this {@code OffsetDateTime} with the specified period in days subtracted. + *

    + * This method subtracts the specified amount from the days field incrementing the + * month and year fields as necessary to ensure the result remains valid. + * The result is only invalid if the maximum/minimum year is exceeded. + *

    + * For example, 2008-12-31 minus one day would result in the 2009-01-01. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param days the days to subtract, may be negative + * @return an {@code OffsetDateTime} based on this date-time with the days subtracted, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + public OffsetDateTime minusDays(long days) { + return (days == Long.MIN_VALUE ? plusDays(Long.MAX_VALUE).plusDays(1) : plusDays(-days)); + } + + /** + * Returns a copy of this {@code OffsetDateTime} with the specified period in hours subtracted. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param hours the hours to subtract, may be negative + * @return an {@code OffsetDateTime} based on this date-time with the hours subtracted, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + public OffsetDateTime minusHours(long hours) { + return (hours == Long.MIN_VALUE ? plusHours(Long.MAX_VALUE).plusHours(1) : plusHours(-hours)); + } + + /** + * Returns a copy of this {@code OffsetDateTime} with the specified period in minutes subtracted. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param minutes the minutes to subtract, may be negative + * @return an {@code OffsetDateTime} based on this date-time with the minutes subtracted, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + public OffsetDateTime minusMinutes(long minutes) { + return (minutes == Long.MIN_VALUE ? plusMinutes(Long.MAX_VALUE).plusMinutes(1) : plusMinutes(-minutes)); + } + + /** + * Returns a copy of this {@code OffsetDateTime} with the specified period in seconds subtracted. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param seconds the seconds to subtract, may be negative + * @return an {@code OffsetDateTime} based on this date-time with the seconds subtracted, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + public OffsetDateTime minusSeconds(long seconds) { + return (seconds == Long.MIN_VALUE ? plusSeconds(Long.MAX_VALUE).plusSeconds(1) : plusSeconds(-seconds)); + } + + /** + * Returns a copy of this {@code OffsetDateTime} with the specified period in nanoseconds subtracted. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param nanos the nanos to subtract, may be negative + * @return an {@code OffsetDateTime} based on this date-time with the nanoseconds subtracted, not null + * @throws DateTimeException if the result exceeds the supported date range + */ + public OffsetDateTime minusNanos(long nanos) { + return (nanos == Long.MIN_VALUE ? plusNanos(Long.MAX_VALUE).plusNanos(1) : plusNanos(-nanos)); + } + + //----------------------------------------------------------------------- + /** + * Queries this date-time using the specified query. + *

    + * This queries this date-time using the specified query strategy object. + * The {@code TemporalQuery} object defines the logic to be used to + * obtain the result. Read the documentation of the query to understand + * what the result of this method will be. + *

    + * The result of this method is obtained by invoking the + * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the + * specified query passing {@code this} as the argument. + * + * @param the type of the result + * @param query the query to invoke, not null + * @return the query result, null may be returned (defined by the query) + * @throws DateTimeException if unable to query (defined by the query) + * @throws ArithmeticException if numeric overflow occurs (defined by the query) + */ + @SuppressWarnings("unchecked") + @Override + public R query(TemporalQuery query) { + if (query == Queries.chrono()) { + return (R) getDate().getChrono(); + } else if (query == Queries.precision()) { + return (R) NANOS; + } else if (query == Queries.offset() || query == Queries.zone()) { + return (R) getOffset(); + } + return Temporal.super.query(query); + } + + /** + * Adjusts the specified temporal object to have the same offset, date + * and time as this object. + *

    + * This returns a temporal object of the same observable type as the input + * with the offset, date and time changed to be the same as this. + *

    + * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)} + * three times, passing {@link ChronoField#OFFSET_SECONDS}, + * {@link ChronoField#EPOCH_DAY} and {@link ChronoField#NANO_OF_DAY} as the fields. + *

    + * In most cases, it is clearer to reverse the calling pattern by using + * {@link Temporal#with(TemporalAdjuster)}: + *

    +     *   // these two lines are equivalent, but the second approach is recommended
    +     *   temporal = thisOffsetDateTime.adjustInto(temporal);
    +     *   temporal = temporal.with(thisOffsetDateTime);
    +     * 
    + *

    + * This instance is immutable and unaffected by this method call. + * + * @param temporal the target object to be adjusted, not null + * @return the adjusted object, not null + * @throws DateTimeException if unable to make the adjustment + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public Temporal adjustInto(Temporal temporal) { + return temporal + .with(OFFSET_SECONDS, getOffset().getTotalSeconds()) + .with(EPOCH_DAY, getDate().toEpochDay()) + .with(NANO_OF_DAY, getTime().toNanoOfDay()); + } + + /** + * Calculates the period between this date-time and another date-time in + * terms of the specified unit. + *

    + * This calculates the period between two date-times in terms of a single unit. + * The start and end points are {@code this} and the specified date-time. + * The result will be negative if the end is before the start. + * For example, the period in days between two date-times can be calculated + * using {@code startDateTime.periodUntil(endDateTime, DAYS)}. + *

    + * The {@code Temporal} passed to this method must be an {@code OffsetDateTime}. + * If the offset differs between the two date-times, the specified + * end date-time is normalized to have the same offset as this date-time. + *

    + * The calculation returns a whole number, representing the number of + * complete units between the two date-times. + * For example, the period in months between 2012-06-15T00:00Z and 2012-08-14T23:59Z + * will only be one month as it is one minute short of two months. + *

    + * This method operates in association with {@link TemporalUnit#between}. + * The result of this method is a {@code long} representing the amount of + * the specified unit. By contrast, the result of {@code between} is an + * object that can be used directly in addition/subtraction: + *

    +     *   long period = start.periodUntil(end, MONTHS);   // this method
    +     *   dateTime.plus(MONTHS.between(start, end));      // use in plus/minus
    +     * 
    + *

    + * The calculation is implemented in this method for {@link ChronoUnit}. + * The units {@code NANOS}, {@code MICROS}, {@code MILLIS}, {@code SECONDS}, + * {@code MINUTES}, {@code HOURS} and {@code HALF_DAYS}, {@code DAYS}, + * {@code WEEKS}, {@code MONTHS}, {@code YEARS}, {@code DECADES}, + * {@code CENTURIES}, {@code MILLENNIA} and {@code ERAS} are supported. + * Other {@code ChronoUnit} values will throw an exception. + *

    + * If the unit is not a {@code ChronoUnit}, then the result of this method + * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)} + * passing {@code this} as the first argument and the input temporal as + * the second argument. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param endDateTime the end date-time, which must be an {@code OffsetDateTime}, not null + * @param unit the unit to measure the period in, not null + * @return the amount of the period between this date-time and the end date-time + * @throws DateTimeException if the period cannot be calculated + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public long periodUntil(Temporal endDateTime, TemporalUnit unit) { + if (endDateTime instanceof OffsetDateTime == false) { + Objects.requireNonNull(endDateTime, "endDateTime"); + throw new DateTimeException("Unable to calculate period between objects of two different types"); + } + if (unit instanceof ChronoUnit) { + OffsetDateTime end = (OffsetDateTime) endDateTime; + end = end.withOffsetSameInstant(offset); + return dateTime.periodUntil(end.dateTime, unit); + } + return unit.between(this, endDateTime).getAmount(); + } + + //----------------------------------------------------------------------- + /** + * Returns a zoned date-time formed from the instant represented by this + * date-time and the specified zone ID. + *

    + * This conversion will ignore the visible local date-time and use the underlying instant instead. + * This avoids any problems with local time-line gaps or overlaps. + * The result might have different values for fields such as hour, minute an even day. + *

    + * To attempt to retain the values of the fields, use {@link #atZoneSimilarLocal(ZoneId)}. + * To use the offset as the zone ID, use {@link #toZonedDateTime()}. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param zone the time-zone to use, not null + * @return the zoned date-time formed from this date-time, not null + */ + public ZonedDateTime atZoneSameInstant(ZoneId zone) { + return ZonedDateTime.ofInstant(dateTime, offset, zone); + } + + /** + * Returns a zoned date-time formed from this date-time and the specified zone ID. + *

    + * Time-zone rules, such as daylight savings, mean that not every time on the + * local time-line exists. If the local date-time is in a gap or overlap according to + * the rules then a resolver is used to determine the resultant local time and offset. + * This method uses {@link ZonedDateTime#ofLocal(LocalDateTime, ZoneId, ZoneOffset)} + * to retain the offset from this instance if possible. + *

    + * Finer control over gaps and overlaps is available in two ways. + * If you simply want to use the later offset at overlaps then call + * {@link ZonedDateTime#withLaterOffsetAtOverlap()} immediately after this method. + *

    + * To create a zoned date-time at the same instant irrespective of the local time-line, + * use {@link #atZoneSameInstant(ZoneId)}. + * To use the offset as the zone ID, use {@link #toZonedDateTime()}. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param zone the time-zone to use, not null + * @return the zoned date-time formed from this date and the earliest valid time for the zone, not null + */ + public ZonedDateTime atZoneSimilarLocal(ZoneId zone) { + return ZonedDateTime.ofLocal(dateTime, zone, offset); + } + + //----------------------------------------------------------------------- + /** + * Converts this date-time to an {@code OffsetDate}. + *

    + * This returns an offset date with the same local date and offset. + * + * @return an OffsetDate representing the date and offset, not null + */ + public OffsetDate toOffsetDate() { + return OffsetDate.of(dateTime.getDate(), offset); + } + + /** + * Converts this date-time to an {@code OffsetTime}. + *

    + * This returns an offset time with the same local time and offset. + * + * @return an OffsetTime representing the time and offset, not null + */ + public OffsetTime toOffsetTime() { + return OffsetTime.of(dateTime.getTime(), offset); + } + + /** + * Converts this date-time to a {@code ZonedDateTime} using the offset as the zone ID. + *

    + * This creates the simplest possible {@code ZonedDateTime} using the offset + * as the zone ID. + *

    + * To control the time-zone used, see {@link #atZoneSameInstant(ZoneId)} and + * {@link #atZoneSimilarLocal(ZoneId)}. + * + * @return a zoned date-time representing the same local date-time and offset, not null + */ + public ZonedDateTime toZonedDateTime() { + return ZonedDateTime.of(dateTime, offset); + } + + /** + * Converts this date-time to an {@code Instant}. + * + * @return an {@code Instant} representing the same instant, not null + */ + public Instant toInstant() { + return dateTime.toInstant(offset); + } + + /** + * Converts this date-time to the number of seconds from the epoch of 1970-01-01T00:00:00Z. + *

    + * This allows this date-time to be converted to a value of the + * {@link ChronoField#INSTANT_SECONDS epoch-seconds} field. This is primarily + * intended for low-level conversions rather than general application usage. + * + * @return the number of seconds from the epoch of 1970-01-01T00:00:00Z + */ + public long toEpochSecond() { + return dateTime.toEpochSecond(offset); + } + + //----------------------------------------------------------------------- + /** + * Compares this {@code OffsetDateTime} to another date-time. + *

    + * The comparison is based on the instant then on the local date-time. + * It is "consistent with equals", as defined by {@link Comparable}. + *

    + * For example, the following is the comparator order: + *

      + *
    1. {@code 2008-12-03T10:30+01:00}
    2. + *
    3. {@code 2008-12-03T11:00+01:00}
    4. + *
    5. {@code 2008-12-03T12:00+02:00}
    6. + *
    7. {@code 2008-12-03T11:30+01:00}
    8. + *
    9. {@code 2008-12-03T12:00+01:00}
    10. + *
    11. {@code 2008-12-03T12:30+01:00}
    12. + *
    + * Values #2 and #3 represent the same instant on the time-line. + * When two values represent the same instant, the local date-time is compared + * to distinguish them. This step is needed to make the ordering + * consistent with {@code equals()}. + * + * @param other the other date-time to compare to, not null + * @return the comparator value, negative if less, positive if greater + */ + @Override + public int compareTo(OffsetDateTime other) { + if (getOffset().equals(other.getOffset())) { + return getDateTime().compareTo(other.getDateTime()); + } + int cmp = Long.compare(toEpochSecond(), other.toEpochSecond()); + if (cmp == 0) { + cmp = getTime().getNano() - other.getTime().getNano(); + if (cmp == 0) { + cmp = getDateTime().compareTo(other.getDateTime()); + } + } + return cmp; + } + + //----------------------------------------------------------------------- + /** + * Checks if the instant of this date-time is after that of the specified date-time. + *

    + * This method differs from the comparison in {@link #compareTo} and {@link #equals} in that it + * only compares the instant of the date-time. This is equivalent to using + * {@code dateTime1.toInstant().isAfter(dateTime2.toInstant());}. + * + * @param other the other date-time to compare to, not null + * @return true if this is after the instant of the specified date-time + */ + public boolean isAfter(OffsetDateTime other) { + long thisEpochSec = toEpochSecond(); + long otherEpochSec = other.toEpochSecond(); + return thisEpochSec > otherEpochSec || + (thisEpochSec == otherEpochSec && getTime().getNano() > other.getTime().getNano()); + } + + /** + * Checks if the instant of this date-time is before that of the specified date-time. + *

    + * This method differs from the comparison in {@link #compareTo} in that it + * only compares the instant of the date-time. This is equivalent to using + * {@code dateTime1.toInstant().isBefore(dateTime2.toInstant());}. + * + * @param other the other date-time to compare to, not null + * @return true if this is before the instant of the specified date-time + */ + public boolean isBefore(OffsetDateTime other) { + long thisEpochSec = toEpochSecond(); + long otherEpochSec = other.toEpochSecond(); + return thisEpochSec < otherEpochSec || + (thisEpochSec == otherEpochSec && getTime().getNano() < other.getTime().getNano()); + } + + /** + * Checks if the instant of this date-time is equal to that of the specified date-time. + *

    + * This method differs from the comparison in {@link #compareTo} and {@link #equals} + * in that it only compares the instant of the date-time. This is equivalent to using + * {@code dateTime1.toInstant().equals(dateTime2.toInstant());}. + * + * @param other the other date-time to compare to, not null + * @return true if the instant equals the instant of the specified date-time + */ + public boolean isEqual(OffsetDateTime other) { + return toEpochSecond() == other.toEpochSecond() && + getTime().getNano() == other.getTime().getNano(); + } + + //----------------------------------------------------------------------- + /** + * Checks if this date-time is equal to another date-time. + *

    + * The comparison is based on the local date-time and the offset. + * To compare for the same instant on the time-line, use {@link #isEqual}. + * Only objects of type {@code OffsetDateTime} are compared, other types return false. + * + * @param obj the object to check, null returns false + * @return true if this is equal to the other date-time + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof OffsetDateTime) { + OffsetDateTime other = (OffsetDateTime) obj; + return dateTime.equals(other.dateTime) && offset.equals(other.offset); + } + return false; + } + + /** + * A hash code for this date-time. + * + * @return a suitable hash code + */ + @Override + public int hashCode() { + return dateTime.hashCode() ^ offset.hashCode(); + } + + //----------------------------------------------------------------------- + /** + * Outputs this date-time as a {@code String}, such as {@code 2007-12-03T10:15:30+01:00}. + *

    + * The output will be one of the following ISO-8601 formats: + *

      + *
    • {@code yyyy-MM-dd'T'HH:mmXXXXX}
    • + *
    • {@code yyyy-MM-dd'T'HH:mm:ssXXXXX}
    • + *
    • {@code yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX}
    • + *
    • {@code yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXXXX}
    • + *
    • {@code yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSXXXXX}
    • + *

    + * The format used will be the shortest that outputs the full value of + * the time where the omitted parts are implied to be zero. + * + * @return a string representation of this date-time, not null + */ + @Override + public String toString() { + return dateTime.toString() + offset.toString(); + } + + /** + * Outputs this date-time as a {@code String} using the formatter. + *

    + * This date-time will be passed to the formatter + * {@link DateTimeFormatter#print(TemporalAccessor) print method}. + * + * @param formatter the formatter to use, not null + * @return the formatted date-time string, not null + * @throws DateTimeException if an error occurs during printing + */ + public String toString(DateTimeFormatter formatter) { + Objects.requireNonNull(formatter, "formatter"); + return formatter.print(this); + } + + //----------------------------------------------------------------------- + /** + * Writes the object using a + * dedicated serialized form. + *

    +     *  out.writeByte(3);  // identifies this as a OffsetDateTime
    +     *  out.writeObject(dateTime);
    +     *  out.writeObject(offset);
    +     * 
    + * + * @return the instance of {@code Ser}, not null + */ + private Object writeReplace() { + return new Ser(Ser.OFFSET_DATE_TIME_TYPE, this); + } + + /** + * Defend against malicious streams. + * @return never + * @throws InvalidObjectException always + */ + private Object readResolve() throws ObjectStreamException { + throw new InvalidObjectException("Deserialization via serialization delegate"); + } + + void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(dateTime); + out.writeObject(offset); + } + + static OffsetDateTime readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + LocalDateTime dateTime = (LocalDateTime) in.readObject(); + ZoneOffset offset = (ZoneOffset) in.readObject(); + return OffsetDateTime.of(dateTime, offset); + } + +} diff --git a/jdk/src/share/classes/java/time/temporal/OffsetTime.java b/jdk/src/share/classes/java/time/temporal/OffsetTime.java new file mode 100644 index 00000000000..390b1b199d3 --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/OffsetTime.java @@ -0,0 +1,1311 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import static java.time.temporal.ChronoField.NANO_OF_DAY; +import static java.time.temporal.ChronoField.OFFSET_SECONDS; +import static java.time.temporal.ChronoLocalDateTimeImpl.NANOS_PER_HOUR; +import static java.time.temporal.ChronoLocalDateTimeImpl.NANOS_PER_MINUTE; +import static java.time.temporal.ChronoLocalDateTimeImpl.NANOS_PER_SECOND; +import static java.time.temporal.ChronoLocalDateTimeImpl.SECONDS_PER_DAY; +import static java.time.temporal.ChronoUnit.NANOS; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.time.Clock; +import java.time.DateTimeException; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatters; +import java.time.format.DateTimeParseException; +import java.time.zone.ZoneRules; +import java.util.Objects; + +/** + * A time with an offset from UTC/Greenwich in the ISO-8601 calendar system, + * such as {@code 10:15:30+01:00}. + *

    + * {@code OffsetTime} is an immutable date-time object that represents a time, often + * viewed as hour-minute-second-offset. + * This class stores all time fields, to a precision of nanoseconds, + * as well as a zone offset. + * For example, the value "13:45.30.123456789+02:00" can be stored + * in an {@code OffsetTime}. + * + *

    Specification for implementors

    + * This class is immutable and thread-safe. + * + * @since 1.8 + */ +public final class OffsetTime + implements Temporal, TemporalAdjuster, Comparable, Serializable { + + /** + * The minimum supported {@code OffsetTime}, '00:00:00+18:00'. + * This is the time of midnight at the start of the day in the maximum offset + * (larger offsets are earlier on the time-line). + * This combines {@link LocalTime#MIN} and {@link ZoneOffset#MAX}. + * This could be used by an application as a "far past" date. + */ + public static final OffsetTime MIN = LocalTime.MIN.atOffset(ZoneOffset.MAX); + /** + * The maximum supported {@code OffsetTime}, '23:59:59.999999999-18:00'. + * This is the time just before midnight at the end of the day in the minimum offset + * (larger negative offsets are later on the time-line). + * This combines {@link LocalTime#MAX} and {@link ZoneOffset#MIN}. + * This could be used by an application as a "far future" date. + */ + public static final OffsetTime MAX = LocalTime.MAX.atOffset(ZoneOffset.MIN); + + /** + * Serialization version. + */ + private static final long serialVersionUID = 7264499704384272492L; + + /** + * The local date-time. + */ + private final LocalTime time; + /** + * The offset from UTC/Greenwich. + */ + private final ZoneOffset offset; + + //----------------------------------------------------------------------- + /** + * Obtains the current time from the system clock in the default time-zone. + *

    + * This will query the {@link java.time.Clock#systemDefaultZone() system clock} in the default + * time-zone to obtain the current time. + * The offset will be calculated from the time-zone in the clock. + *

    + * Using this method will prevent the ability to use an alternate clock for testing + * because the clock is hard-coded. + * + * @return the current time using the system clock, not null + */ + public static OffsetTime now() { + return now(Clock.systemDefaultZone()); + } + + /** + * Obtains the current time from the system clock in the specified time-zone. + *

    + * This will query the {@link Clock#system(java.time.ZoneId) system clock} to obtain the current time. + * Specifying the time-zone avoids dependence on the default time-zone. + * The offset will be calculated from the specified time-zone. + *

    + * Using this method will prevent the ability to use an alternate clock for testing + * because the clock is hard-coded. + * + * @param zone the zone ID to use, not null + * @return the current time using the system clock, not null + */ + public static OffsetTime now(ZoneId zone) { + return now(Clock.system(zone)); + } + + /** + * Obtains the current time from the specified clock. + *

    + * This will query the specified clock to obtain the current time. + * The offset will be calculated from the time-zone in the clock. + *

    + * Using this method allows the use of an alternate clock for testing. + * The alternate clock may be introduced using {@link Clock dependency injection}. + * + * @param clock the clock to use, not null + * @return the current time, not null + */ + public static OffsetTime now(Clock clock) { + Objects.requireNonNull(clock, "clock"); + final Instant now = clock.instant(); // called once + return ofInstant(now, clock.getZone().getRules().getOffset(now)); + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code OffsetTime} from a local time and an offset. + * + * @param time the local time, not null + * @param offset the zone offset, not null + * @return the offset time, not null + */ + public static OffsetTime of(LocalTime time, ZoneOffset offset) { + return new OffsetTime(time, offset); + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code OffsetTime} from an {@code Instant} and zone ID. + *

    + * This creates an offset time with the same instant as that specified. + * Finding the offset from UTC/Greenwich is simple as there is only one valid + * offset for each instant. + *

    + * The date component of the instant is dropped during the conversion. + * This means that the conversion can never fail due to the instant being + * out of the valid range of dates. + * + * @param instant the instant to create the time from, not null + * @param zone the time-zone, which may be an offset, not null + * @return the offset time, not null + */ + public static OffsetTime ofInstant(Instant instant, ZoneId zone) { + Objects.requireNonNull(instant, "instant"); + Objects.requireNonNull(zone, "zone"); + ZoneRules rules = zone.getRules(); + ZoneOffset offset = rules.getOffset(instant); + long secsOfDay = instant.getEpochSecond() % SECONDS_PER_DAY; + secsOfDay = (secsOfDay + offset.getTotalSeconds()) % SECONDS_PER_DAY; + if (secsOfDay < 0) { + secsOfDay += SECONDS_PER_DAY; + } + LocalTime time = LocalTime.ofSecondOfDay(secsOfDay, instant.getNano()); + return new OffsetTime(time, offset); + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code OffsetTime} from a temporal object. + *

    + * A {@code TemporalAccessor} represents some form of date and time information. + * This factory converts the arbitrary temporal object to an instance of {@code OffsetTime}. + *

    + * The conversion extracts and combines {@code LocalTime} and {@code ZoneOffset}. + *

    + * This method matches the signature of the functional interface {@link TemporalQuery} + * allowing it to be used in queries via method reference, {@code OffsetTime::from}. + * + * @param temporal the temporal object to convert, not null + * @return the offset time, not null + * @throws DateTimeException if unable to convert to an {@code OffsetTime} + */ + public static OffsetTime from(TemporalAccessor temporal) { + if (temporal instanceof OffsetTime) { + return (OffsetTime) temporal; + } + try { + LocalTime time = LocalTime.from(temporal); + ZoneOffset offset = ZoneOffset.from(temporal); + return new OffsetTime(time, offset); + } catch (DateTimeException ex) { + throw new DateTimeException("Unable to obtain OffsetTime from TemporalAccessor: " + temporal.getClass(), ex); + } + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code OffsetTime} from a text string such as {@code 10:15:30+01:00}. + *

    + * The string must represent a valid time and is parsed using + * {@link java.time.format.DateTimeFormatters#isoOffsetTime()}. + * + * @param text the text to parse such as "10:15:30+01:00", not null + * @return the parsed local time, not null + * @throws DateTimeParseException if the text cannot be parsed + */ + public static OffsetTime parse(CharSequence text) { + return parse(text, DateTimeFormatters.isoOffsetTime()); + } + + /** + * Obtains an instance of {@code OffsetTime} from a text string using a specific formatter. + *

    + * The text is parsed using the formatter, returning a time. + * + * @param text the text to parse, not null + * @param formatter the formatter to use, not null + * @return the parsed offset time, not null + * @throws DateTimeParseException if the text cannot be parsed + */ + public static OffsetTime parse(CharSequence text, DateTimeFormatter formatter) { + Objects.requireNonNull(formatter, "formatter"); + return formatter.parse(text, OffsetTime::from); + } + + //----------------------------------------------------------------------- + /** + * Constructor. + * + * @param time the local time, not null + * @param offset the zone offset, not null + */ + private OffsetTime(LocalTime time, ZoneOffset offset) { + this.time = Objects.requireNonNull(time, "time"); + this.offset = Objects.requireNonNull(offset, "offset"); + } + + /** + * Returns a new time based on this one, returning {@code this} where possible. + * + * @param time the time to create with, not null + * @param offset the zone offset to create with, not null + */ + private OffsetTime with(LocalTime time, ZoneOffset offset) { + if (this.time == time && this.offset.equals(offset)) { + return this; + } + return new OffsetTime(time, offset); + } + + //----------------------------------------------------------------------- + /** + * Checks if the specified field is supported. + *

    + * This checks if this time can be queried for the specified field. + * If false, then calling the {@link #range(TemporalField) range} and + * {@link #get(TemporalField) get} methods will throw an exception. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The supported fields are: + *

      + *
    • {@code NANO_OF_SECOND} + *
    • {@code NANO_OF_DAY} + *
    • {@code MICRO_OF_SECOND} + *
    • {@code MICRO_OF_DAY} + *
    • {@code MILLI_OF_SECOND} + *
    • {@code MILLI_OF_DAY} + *
    • {@code SECOND_OF_MINUTE} + *
    • {@code SECOND_OF_DAY} + *
    • {@code MINUTE_OF_HOUR} + *
    • {@code MINUTE_OF_DAY} + *
    • {@code HOUR_OF_AMPM} + *
    • {@code CLOCK_HOUR_OF_AMPM} + *
    • {@code HOUR_OF_DAY} + *
    • {@code CLOCK_HOUR_OF_DAY} + *
    • {@code AMPM_OF_DAY} + *
    • {@code OFFSET_SECONDS} + *
    + * All other {@code ChronoField} instances will return false. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doIsSupported(TemporalAccessor)} + * passing {@code this} as the argument. + * Whether the field is supported is determined by the field. + * + * @param field the field to check, null returns false + * @return true if the field is supported on this time, false if not + */ + @Override + public boolean isSupported(TemporalField field) { + if (field instanceof ChronoField) { + return ((ChronoField) field).isTimeField() || field == OFFSET_SECONDS; + } + return field != null && field.doIsSupported(this); + } + + /** + * Gets the range of valid values for the specified field. + *

    + * The range object expresses the minimum and maximum valid values for a field. + * This time is used to enhance the accuracy of the returned range. + * If it is not possible to return the range, because the field is not supported + * or for some other reason, an exception is thrown. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@link #isSupported(TemporalField) supported fields} will return + * appropriate range instances. + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doRange(TemporalAccessor)} + * passing {@code this} as the argument. + * Whether the range can be obtained is determined by the field. + * + * @param field the field to query the range for, not null + * @return the range of valid values for the field, not null + * @throws DateTimeException if the range for the field cannot be obtained + */ + @Override + public ValueRange range(TemporalField field) { + if (field instanceof ChronoField) { + if (field == OFFSET_SECONDS) { + return field.range(); + } + return time.range(field); + } + return field.doRange(this); + } + + /** + * Gets the value of the specified field from this time as an {@code int}. + *

    + * This queries this time for the value for the specified field. + * The returned value will always be within the valid range of values for the field. + * If it is not possible to return the value, because the field is not supported + * or for some other reason, an exception is thrown. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@link #isSupported(TemporalField) supported fields} will return valid + * values based on this time, except {@code NANO_OF_DAY} and {@code MICRO_OF_DAY} + * which are too large to fit in an {@code int} and throw a {@code DateTimeException}. + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doGet(TemporalAccessor)} + * passing {@code this} as the argument. Whether the value can be obtained, + * and what the value represents, is determined by the field. + * + * @param field the field to get, not null + * @return the value for the field + * @throws DateTimeException if a value for the field cannot be obtained + * @throws ArithmeticException if numeric overflow occurs + */ + @Override // override for Javadoc + public int get(TemporalField field) { + return Temporal.super.get(field); + } + + /** + * Gets the value of the specified field from this time as a {@code long}. + *

    + * This queries this time for the value for the specified field. + * If it is not possible to return the value, because the field is not supported + * or for some other reason, an exception is thrown. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@link #isSupported(TemporalField) supported fields} will return valid + * values based on this time. + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doGet(TemporalAccessor)} + * passing {@code this} as the argument. Whether the value can be obtained, + * and what the value represents, is determined by the field. + * + * @param field the field to get, not null + * @return the value for the field + * @throws DateTimeException if a value for the field cannot be obtained + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public long getLong(TemporalField field) { + if (field instanceof ChronoField) { + if (field == OFFSET_SECONDS) { + return getOffset().getTotalSeconds(); + } + return time.getLong(field); + } + return field.doGet(this); + } + + //----------------------------------------------------------------------- + /** + * Gets the zone offset, such as '+01:00'. + *

    + * This is the offset of the local time from UTC/Greenwich. + * + * @return the zone offset, not null + */ + public ZoneOffset getOffset() { + return offset; + } + + /** + * Returns a copy of this {@code OffsetTime} with the specified offset ensuring + * that the result has the same local time. + *

    + * This method returns an object with the same {@code LocalTime} and the specified {@code ZoneOffset}. + * No calculation is needed or performed. + * For example, if this time represents {@code 10:30+02:00} and the offset specified is + * {@code +03:00}, then this method will return {@code 10:30+03:00}. + *

    + * To take into account the difference between the offsets, and adjust the time fields, + * use {@link #withOffsetSameInstant}. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param offset the zone offset to change to, not null + * @return an {@code OffsetTime} based on this time with the requested offset, not null + */ + public OffsetTime withOffsetSameLocal(ZoneOffset offset) { + return offset != null && offset.equals(this.offset) ? this : new OffsetTime(time, offset); + } + + /** + * Returns a copy of this {@code OffsetTime} with the specified offset ensuring + * that the result is at the same instant on an implied day. + *

    + * This method returns an object with the specified {@code ZoneOffset} and a {@code LocalTime} + * adjusted by the difference between the two offsets. + * This will result in the old and new objects representing the same instant an an implied day. + * This is useful for finding the local time in a different offset. + * For example, if this time represents {@code 10:30+02:00} and the offset specified is + * {@code +03:00}, then this method will return {@code 11:30+03:00}. + *

    + * To change the offset without adjusting the local time use {@link #withOffsetSameLocal}. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param offset the zone offset to change to, not null + * @return an {@code OffsetTime} based on this time with the requested offset, not null + */ + public OffsetTime withOffsetSameInstant(ZoneOffset offset) { + if (offset.equals(this.offset)) { + return this; + } + int difference = offset.getTotalSeconds() - this.offset.getTotalSeconds(); + LocalTime adjusted = time.plusSeconds(difference); + return new OffsetTime(adjusted, offset); + } + + //----------------------------------------------------------------------- + /** + * Gets the {@code LocalTime} part of this date-time. + *

    + * This returns a {@code LocalTime} with the same hour, minute, second and + * nanosecond as this date-time. + * + * @return the time part of this date-time, not null + */ + public LocalTime getTime() { + return time; + } + + //----------------------------------------------------------------------- + /** + * Gets the hour-of-day field. + * + * @return the hour-of-day, from 0 to 23 + */ + public int getHour() { + return time.getHour(); + } + + /** + * Gets the minute-of-hour field. + * + * @return the minute-of-hour, from 0 to 59 + */ + public int getMinute() { + return time.getMinute(); + } + + /** + * Gets the second-of-minute field. + * + * @return the second-of-minute, from 0 to 59 + */ + public int getSecond() { + return time.getSecond(); + } + + /** + * Gets the nano-of-second field. + * + * @return the nano-of-second, from 0 to 999,999,999 + */ + public int getNano() { + return time.getNano(); + } + + //----------------------------------------------------------------------- + /** + * Returns an adjusted copy of this time. + *

    + * This returns a new {@code OffsetTime}, based on this one, with the time adjusted. + * The adjustment takes place using the specified adjuster strategy object. + * Read the documentation of the adjuster to understand what adjustment will be made. + *

    + * A simple adjuster might simply set the one of the fields, such as the hour field. + * A more complex adjuster might set the time to the last hour of the day. + *

    + * The classes {@link LocalTime} and {@link ZoneOffset} implement {@code TemporalAdjuster}, + * thus this method can be used to change the time or offset: + *

    +     *  result = offsetTime.with(time);
    +     *  result = offsetTime.with(offset);
    +     * 
    + *

    + * The result of this method is obtained by invoking the + * {@link TemporalAdjuster#adjustInto(Temporal)} method on the + * specified adjuster passing {@code this} as the argument. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param adjuster the adjuster to use, not null + * @return an {@code OffsetTime} based on {@code this} with the adjustment made, not null + * @throws DateTimeException if the adjustment cannot be made + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public OffsetTime with(TemporalAdjuster adjuster) { + // optimizations + if (adjuster instanceof LocalTime) { + return with((LocalTime) adjuster, offset); + } else if (adjuster instanceof ZoneOffset) { + return with(time, (ZoneOffset) adjuster); + } else if (adjuster instanceof OffsetTime) { + return (OffsetTime) adjuster; + } + return (OffsetTime) adjuster.adjustInto(this); + } + + /** + * Returns a copy of this time with the specified field set to a new value. + *

    + * This returns a new {@code OffsetTime}, based on this one, with the value + * for the specified field changed. + * This can be used to change any supported field, such as the hour, minute or second. + * If it is not possible to set the value, because the field is not supported or for + * some other reason, an exception is thrown. + *

    + * If the field is a {@link ChronoField} then the adjustment is implemented here. + *

    + * The {@code OFFSET_SECONDS} field will return a time with the specified offset. + * The local time is unaltered. If the new offset value is outside the valid range + * then a {@code DateTimeException} will be thrown. + *

    + * The other {@link #isSupported(TemporalField) supported fields} will behave as per + * the matching method on {@link LocalTime#with(TemporalField, long)} LocalTime}. + * In this case, the offset is not part of the calculation and will be unchanged. + *

    + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doWith(Temporal, long)} + * passing {@code this} as the argument. In this case, the field determines + * whether and how to adjust the instant. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param field the field to set in the result, not null + * @param newValue the new value of the field in the result + * @return an {@code OffsetTime} based on {@code this} with the specified field set, not null + * @throws DateTimeException if the field cannot be set + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public OffsetTime with(TemporalField field, long newValue) { + if (field instanceof ChronoField) { + if (field == OFFSET_SECONDS) { + ChronoField f = (ChronoField) field; + return with(time, ZoneOffset.ofTotalSeconds(f.checkValidIntValue(newValue))); + } + return with(time.with(field, newValue), offset); + } + return field.doWith(this, newValue); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this {@code OffsetTime} with the hour-of-day value altered. + *

    + * The offset does not affect the calculation and will be the same in the result. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param hour the hour-of-day to set in the result, from 0 to 23 + * @return an {@code OffsetTime} based on this time with the requested hour, not null + * @throws DateTimeException if the hour value is invalid + */ + public OffsetTime withHour(int hour) { + return with(time.withHour(hour), offset); + } + + /** + * Returns a copy of this {@code OffsetTime} with the minute-of-hour value altered. + *

    + * The offset does not affect the calculation and will be the same in the result. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param minute the minute-of-hour to set in the result, from 0 to 59 + * @return an {@code OffsetTime} based on this time with the requested minute, not null + * @throws DateTimeException if the minute value is invalid + */ + public OffsetTime withMinute(int minute) { + return with(time.withMinute(minute), offset); + } + + /** + * Returns a copy of this {@code OffsetTime} with the second-of-minute value altered. + *

    + * The offset does not affect the calculation and will be the same in the result. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param second the second-of-minute to set in the result, from 0 to 59 + * @return an {@code OffsetTime} based on this time with the requested second, not null + * @throws DateTimeException if the second value is invalid + */ + public OffsetTime withSecond(int second) { + return with(time.withSecond(second), offset); + } + + /** + * Returns a copy of this {@code OffsetTime} with the nano-of-second value altered. + *

    + * The offset does not affect the calculation and will be the same in the result. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param nanoOfSecond the nano-of-second to set in the result, from 0 to 999,999,999 + * @return an {@code OffsetTime} based on this time with the requested nanosecond, not null + * @throws DateTimeException if the nanos value is invalid + */ + public OffsetTime withNano(int nanoOfSecond) { + return with(time.withNano(nanoOfSecond), offset); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this {@code OffsetTime} with the time truncated. + *

    + * Truncation returns a copy of the original time with fields + * smaller than the specified unit set to zero. + * For example, truncating with the {@link ChronoUnit#MINUTES minutes} unit + * will set the second-of-minute and nano-of-second field to zero. + *

    + * Not all units are accepted. The {@link ChronoUnit#DAYS days} unit and time + * units with an exact duration can be used, other units throw an exception. + *

    + * The offset does not affect the calculation and will be the same in the result. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param unit the unit to truncate to, not null + * @return an {@code OffsetTime} based on this time with the time truncated, not null + * @throws DateTimeException if unable to truncate + */ + public OffsetTime truncatedTo(TemporalUnit unit) { + return with(time.truncatedTo(unit), offset); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this date with the specified period added. + *

    + * This method returns a new time based on this time with the specified period added. + * The adder is typically {@link java.time.Period} but may be any other type implementing + * the {@link TemporalAdder} interface. + * The calculation is delegated to the specified adjuster, which typically calls + * back to {@link #plus(long, TemporalUnit)}. + * The offset is not part of the calculation and will be unchanged in the result. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param adder the adder to use, not null + * @return an {@code OffsetTime} based on this time with the addition made, not null + * @throws DateTimeException if the addition cannot be made + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public OffsetTime plus(TemporalAdder adder) { + return (OffsetTime) adder.addTo(this); + } + + /** + * Returns a copy of this time with the specified period added. + *

    + * This method returns a new time based on this time with the specified period added. + * This can be used to add any period that is defined by a unit, for example to add hours, minutes or seconds. + * The unit is responsible for the details of the calculation, including the resolution + * of any edge cases in the calculation. + * The offset is not part of the calculation and will be unchanged in the result. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param amountToAdd the amount of the unit to add to the result, may be negative + * @param unit the unit of the period to add, not null + * @return an {@code OffsetTime} based on this time with the specified period added, not null + * @throws DateTimeException if the unit cannot be added to this type + */ + @Override + public OffsetTime plus(long amountToAdd, TemporalUnit unit) { + if (unit instanceof ChronoUnit) { + return with(time.plus(amountToAdd, unit), offset); + } + return unit.doPlus(this, amountToAdd); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this {@code OffsetTime} with the specified period in hours added. + *

    + * This adds the specified number of hours to this time, returning a new time. + * The calculation wraps around midnight. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param hours the hours to add, may be negative + * @return an {@code OffsetTime} based on this time with the hours added, not null + */ + public OffsetTime plusHours(long hours) { + return with(time.plusHours(hours), offset); + } + + /** + * Returns a copy of this {@code OffsetTime} with the specified period in minutes added. + *

    + * This adds the specified number of minutes to this time, returning a new time. + * The calculation wraps around midnight. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param minutes the minutes to add, may be negative + * @return an {@code OffsetTime} based on this time with the minutes added, not null + */ + public OffsetTime plusMinutes(long minutes) { + return with(time.plusMinutes(minutes), offset); + } + + /** + * Returns a copy of this {@code OffsetTime} with the specified period in seconds added. + *

    + * This adds the specified number of seconds to this time, returning a new time. + * The calculation wraps around midnight. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param seconds the seconds to add, may be negative + * @return an {@code OffsetTime} based on this time with the seconds added, not null + */ + public OffsetTime plusSeconds(long seconds) { + return with(time.plusSeconds(seconds), offset); + } + + /** + * Returns a copy of this {@code OffsetTime} with the specified period in nanoseconds added. + *

    + * This adds the specified number of nanoseconds to this time, returning a new time. + * The calculation wraps around midnight. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param nanos the nanos to add, may be negative + * @return an {@code OffsetTime} based on this time with the nanoseconds added, not null + */ + public OffsetTime plusNanos(long nanos) { + return with(time.plusNanos(nanos), offset); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this time with the specified period subtracted. + *

    + * This method returns a new time based on this time with the specified period subtracted. + * The subtractor is typically {@link java.time.Period} but may be any other type implementing + * the {@link TemporalSubtractor} interface. + * The calculation is delegated to the specified adjuster, which typically calls + * back to {@link #minus(long, TemporalUnit)}. + * The offset is not part of the calculation and will be unchanged in the result. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param subtractor the subtractor to use, not null + * @return an {@code OffsetTime} based on this time with the subtraction made, not null + * @throws DateTimeException if the subtraction cannot be made + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public OffsetTime minus(TemporalSubtractor subtractor) { + return (OffsetTime) subtractor.subtractFrom(this); + } + + /** + * Returns a copy of this time with the specified period subtracted. + *

    + * This method returns a new time based on this time with the specified period subtracted. + * This can be used to subtract any period that is defined by a unit, for example to subtract hours, minutes or seconds. + * The unit is responsible for the details of the calculation, including the resolution + * of any edge cases in the calculation. + * The offset is not part of the calculation and will be unchanged in the result. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param amountToSubtract the amount of the unit to subtract from the result, may be negative + * @param unit the unit of the period to subtract, not null + * @return an {@code OffsetTime} based on this time with the specified period subtracted, not null + * @throws DateTimeException if the unit cannot be added to this type + */ + @Override + public OffsetTime minus(long amountToSubtract, TemporalUnit unit) { + return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit)); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this {@code OffsetTime} with the specified period in hours subtracted. + *

    + * This subtracts the specified number of hours from this time, returning a new time. + * The calculation wraps around midnight. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param hours the hours to subtract, may be negative + * @return an {@code OffsetTime} based on this time with the hours subtracted, not null + */ + public OffsetTime minusHours(long hours) { + return with(time.minusHours(hours), offset); + } + + /** + * Returns a copy of this {@code OffsetTime} with the specified period in minutes subtracted. + *

    + * This subtracts the specified number of minutes from this time, returning a new time. + * The calculation wraps around midnight. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param minutes the minutes to subtract, may be negative + * @return an {@code OffsetTime} based on this time with the minutes subtracted, not null + */ + public OffsetTime minusMinutes(long minutes) { + return with(time.minusMinutes(minutes), offset); + } + + /** + * Returns a copy of this {@code OffsetTime} with the specified period in seconds subtracted. + *

    + * This subtracts the specified number of seconds from this time, returning a new time. + * The calculation wraps around midnight. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param seconds the seconds to subtract, may be negative + * @return an {@code OffsetTime} based on this time with the seconds subtracted, not null + */ + public OffsetTime minusSeconds(long seconds) { + return with(time.minusSeconds(seconds), offset); + } + + /** + * Returns a copy of this {@code OffsetTime} with the specified period in nanoseconds subtracted. + *

    + * This subtracts the specified number of nanoseconds from this time, returning a new time. + * The calculation wraps around midnight. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param nanos the nanos to subtract, may be negative + * @return an {@code OffsetTime} based on this time with the nanoseconds subtracted, not null + */ + public OffsetTime minusNanos(long nanos) { + return with(time.minusNanos(nanos), offset); + } + + //----------------------------------------------------------------------- + /** + * Queries this time using the specified query. + *

    + * This queries this time using the specified query strategy object. + * The {@code TemporalQuery} object defines the logic to be used to + * obtain the result. Read the documentation of the query to understand + * what the result of this method will be. + *

    + * The result of this method is obtained by invoking the + * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the + * specified query passing {@code this} as the argument. + * + * @param the type of the result + * @param query the query to invoke, not null + * @return the query result, null may be returned (defined by the query) + * @throws DateTimeException if unable to query (defined by the query) + * @throws ArithmeticException if numeric overflow occurs (defined by the query) + */ + @SuppressWarnings("unchecked") + @Override + public R query(TemporalQuery query) { + if (query == Queries.precision()) { + return (R) NANOS; + } else if (query == Queries.offset() || query == Queries.zone()) { + return (R) getOffset(); + } + return Temporal.super.query(query); + } + + /** + * Adjusts the specified temporal object to have the same offset and time + * as this object. + *

    + * This returns a temporal object of the same observable type as the input + * with the offset and time changed to be the same as this. + *

    + * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)} + * twice, passing {@link ChronoField#OFFSET_SECONDS} and + * {@link ChronoField#NANO_OF_DAY} as the fields. + *

    + * In most cases, it is clearer to reverse the calling pattern by using + * {@link Temporal#with(TemporalAdjuster)}: + *

    +     *   // these two lines are equivalent, but the second approach is recommended
    +     *   temporal = thisOffsetTime.adjustInto(temporal);
    +     *   temporal = temporal.with(thisOffsetTime);
    +     * 
    + *

    + * This instance is immutable and unaffected by this method call. + * + * @param temporal the target object to be adjusted, not null + * @return the adjusted object, not null + * @throws DateTimeException if unable to make the adjustment + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public Temporal adjustInto(Temporal temporal) { + return temporal + .with(OFFSET_SECONDS, getOffset().getTotalSeconds()) + .with(NANO_OF_DAY, time.toNanoOfDay()); + } + + /** + * Calculates the period between this time and another time in + * terms of the specified unit. + *

    + * This calculates the period between two times in terms of a single unit. + * The start and end points are {@code this} and the specified time. + * The result will be negative if the end is before the start. + * For example, the period in hours between two times can be calculated + * using {@code startTime.periodUntil(endTime, HOURS)}. + *

    + * The {@code Temporal} passed to this method must be an {@code OffsetTime}. + * If the offset differs between the two times, then the specified + * end time is normalized to have the same offset as this time. + *

    + * The calculation returns a whole number, representing the number of + * complete units between the two times. + * For example, the period in hours between 11:30Z and 13:29Z will only + * be one hour as it is one minute short of two hours. + *

    + * This method operates in association with {@link TemporalUnit#between}. + * The result of this method is a {@code long} representing the amount of + * the specified unit. By contrast, the result of {@code between} is an + * object that can be used directly in addition/subtraction: + *

    +     *   long period = start.periodUntil(end, HOURS);   // this method
    +     *   dateTime.plus(HOURS.between(start, end));      // use in plus/minus
    +     * 
    + *

    + * The calculation is implemented in this method for {@link ChronoUnit}. + * The units {@code NANOS}, {@code MICROS}, {@code MILLIS}, {@code SECONDS}, + * {@code MINUTES}, {@code HOURS} and {@code HALF_DAYS} are supported. + * Other {@code ChronoUnit} values will throw an exception. + *

    + * If the unit is not a {@code ChronoUnit}, then the result of this method + * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)} + * passing {@code this} as the first argument and the input temporal as + * the second argument. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param endTime the end time, which must be an {@code OffsetTime}, not null + * @param unit the unit to measure the period in, not null + * @return the amount of the period between this time and the end time + * @throws DateTimeException if the period cannot be calculated + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public long periodUntil(Temporal endTime, TemporalUnit unit) { + if (endTime instanceof OffsetTime == false) { + Objects.requireNonNull(endTime, "endTime"); + throw new DateTimeException("Unable to calculate period between objects of two different types"); + } + if (unit instanceof ChronoUnit) { + OffsetTime end = (OffsetTime) endTime; + long nanosUntil = end.toEpochNano() - toEpochNano(); // no overflow + switch ((ChronoUnit) unit) { + case NANOS: return nanosUntil; + case MICROS: return nanosUntil / 1000; + case MILLIS: return nanosUntil / 1000_000; + case SECONDS: return nanosUntil / NANOS_PER_SECOND; + case MINUTES: return nanosUntil / NANOS_PER_MINUTE; + case HOURS: return nanosUntil / NANOS_PER_HOUR; + case HALF_DAYS: return nanosUntil / (12 * NANOS_PER_HOUR); + } + throw new DateTimeException("Unsupported unit: " + unit.getName()); + } + return unit.between(this, endTime).getAmount(); + } + + //----------------------------------------------------------------------- + /** + * Returns an offset date-time formed from this time at the specified date. + *

    + * This combines this time with the specified date to form an {@code OffsetDateTime}. + * All possible combinations of date and time are valid. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param date the date to combine with, not null + * @return the offset date-time formed from this time and the specified date, not null + */ + public OffsetDateTime atDate(LocalDate date) { + return OffsetDateTime.of(date, time, offset); + } + + //----------------------------------------------------------------------- + /** + * Converts this time to epoch nanos based on 1970-01-01Z. + * + * @return the epoch nanos value + */ + private long toEpochNano() { + long nod = time.toNanoOfDay(); + long offsetNanos = offset.getTotalSeconds() * NANOS_PER_SECOND; + return nod - offsetNanos; + } + + //----------------------------------------------------------------------- + /** + * Compares this {@code OffsetTime} to another time. + *

    + * The comparison is based first on the UTC equivalent instant, then on the local time. + * It is "consistent with equals", as defined by {@link Comparable}. + *

    + * For example, the following is the comparator order: + *

      + *
    1. {@code 10:30+01:00}
    2. + *
    3. {@code 11:00+01:00}
    4. + *
    5. {@code 12:00+02:00}
    6. + *
    7. {@code 11:30+01:00}
    8. + *
    9. {@code 12:00+01:00}
    10. + *
    11. {@code 12:30+01:00}
    12. + *
    + * Values #2 and #3 represent the same instant on the time-line. + * When two values represent the same instant, the local time is compared + * to distinguish them. This step is needed to make the ordering + * consistent with {@code equals()}. + *

    + * To compare the underlying local time of two {@code TemporalAccessor} instances, + * use {@link ChronoField#NANO_OF_DAY} as a comparator. + * + * @param other the other time to compare to, not null + * @return the comparator value, negative if less, positive if greater + * @throws NullPointerException if {@code other} is null + */ + @Override + public int compareTo(OffsetTime other) { + if (offset.equals(other.offset)) { + return time.compareTo(other.time); + } + int compare = Long.compare(toEpochNano(), other.toEpochNano()); + if (compare == 0) { + compare = time.compareTo(other.time); + } + return compare; + } + + //----------------------------------------------------------------------- + /** + * Checks if the instant of this {@code OffsetTime} is after that of the + * specified time applying both times to a common date. + *

    + * This method differs from the comparison in {@link #compareTo} in that it + * only compares the instant of the time. This is equivalent to converting both + * times to an instant using the same date and comparing the instants. + * + * @param other the other time to compare to, not null + * @return true if this is after the instant of the specified time + */ + public boolean isAfter(OffsetTime other) { + return toEpochNano() > other.toEpochNano(); + } + + /** + * Checks if the instant of this {@code OffsetTime} is before that of the + * specified time applying both times to a common date. + *

    + * This method differs from the comparison in {@link #compareTo} in that it + * only compares the instant of the time. This is equivalent to converting both + * times to an instant using the same date and comparing the instants. + * + * @param other the other time to compare to, not null + * @return true if this is before the instant of the specified time + */ + public boolean isBefore(OffsetTime other) { + return toEpochNano() < other.toEpochNano(); + } + + /** + * Checks if the instant of this {@code OffsetTime} is equal to that of the + * specified time applying both times to a common date. + *

    + * This method differs from the comparison in {@link #compareTo} and {@link #equals} + * in that it only compares the instant of the time. This is equivalent to converting both + * times to an instant using the same date and comparing the instants. + * + * @param other the other time to compare to, not null + * @return true if this is equal to the instant of the specified time + */ + public boolean isEqual(OffsetTime other) { + return toEpochNano() == other.toEpochNano(); + } + + //----------------------------------------------------------------------- + /** + * Checks if this time is equal to another time. + *

    + * The comparison is based on the local-time and the offset. + * To compare for the same instant on the time-line, use {@link #isEqual(OffsetTime)}. + *

    + * Only objects of type {@code OffsetTime} are compared, other types return false. + * To compare the underlying local time of two {@code TemporalAccessor} instances, + * use {@link ChronoField#NANO_OF_DAY} as a comparator. + * + * @param obj the object to check, null returns false + * @return true if this is equal to the other time + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof OffsetTime) { + OffsetTime other = (OffsetTime) obj; + return time.equals(other.time) && offset.equals(other.offset); + } + return false; + } + + /** + * A hash code for this time. + * + * @return a suitable hash code + */ + @Override + public int hashCode() { + return time.hashCode() ^ offset.hashCode(); + } + + //----------------------------------------------------------------------- + /** + * Outputs this time as a {@code String}, such as {@code 10:15:30+01:00}. + *

    + * The output will be one of the following ISO-8601 formats: + *

      + *
    • {@code HH:mmXXXXX}
    • + *
    • {@code HH:mm:ssXXXXX}
    • + *
    • {@code HH:mm:ss.SSSXXXXX}
    • + *
    • {@code HH:mm:ss.SSSSSSXXXXX}
    • + *
    • {@code HH:mm:ss.SSSSSSSSSXXXXX}
    • + *

    + * The format used will be the shortest that outputs the full value of + * the time where the omitted parts are implied to be zero. + * + * @return a string representation of this time, not null + */ + @Override + public String toString() { + return time.toString() + offset.toString(); + } + + /** + * Outputs this time as a {@code String} using the formatter. + *

    + * This time will be passed to the formatter + * {@link DateTimeFormatter#print(TemporalAccessor) print method}. + * + * @param formatter the formatter to use, not null + * @return the formatted time string, not null + * @throws DateTimeException if an error occurs during printing + */ + public String toString(DateTimeFormatter formatter) { + Objects.requireNonNull(formatter, "formatter"); + return formatter.print(this); + } + + // ----------------------------------------------------------------------- + /** + * Writes the object using a + * dedicated serialized form. + *

    +     *  out.writeByte(2);  // identifies this as a OffsetDateTime
    +     *  out.writeObject(time);
    +     *  out.writeObject(offset);
    +     * 
    + * + * @return the instance of {@code Ser}, not null + */ + private Object writeReplace() { + return new Ser(Ser.OFFSET_TIME_TYPE, this); + } + + /** + * Defend against malicious streams. + * @return never + * @throws InvalidObjectException always + */ + private Object readResolve() throws ObjectStreamException { + throw new InvalidObjectException("Deserialization via serialization delegate"); + } + + void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(time); + out.writeObject(offset); + } + + static OffsetTime readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + LocalTime time = (LocalTime) in.readObject(); + ZoneOffset offset = (ZoneOffset) in.readObject(); + return OffsetTime.of(time, offset); + } + +} diff --git a/jdk/src/share/classes/java/time/temporal/Queries.java b/jdk/src/share/classes/java/time/temporal/Queries.java new file mode 100644 index 00000000000..74b8f1adf85 --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/Queries.java @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import static java.time.temporal.ChronoField.OFFSET_SECONDS; + +import java.time.ZoneId; +import java.time.ZoneOffset; + +/** + * Common implementations of {@code TemporalQuery}. + *

    + * This class provides common implementations of {@link TemporalQuery}. + * These queries are primarily used as optimizations, allowing the internals + * of other objects to be extracted effectively. Note that application code + * can also use the {@code from(TemporalAccessor)} method on most temporal + * objects as a method reference matching the query interface, such as + * {@code LocalDate::from} and {@code ZoneId::from}. + *

    + * There are two equivalent ways of using a {@code TemporalQuery}. + * The first is to invoke the method on the interface directly. + * The second is to use {@link TemporalAccessor#query(TemporalQuery)}: + *

    + *   // these two lines are equivalent, but the second approach is recommended
    + *   dateTime = query.queryFrom(dateTime);
    + *   dateTime = dateTime.query(query);
    + * 
    + * It is recommended to use the second approach, {@code query(TemporalQuery)}, + * as it is a lot clearer to read in code. + * + *

    Specification for implementors

    + * This is a thread-safe utility class. + * All returned adjusters are immutable and thread-safe. + * + * @since 1.8 + */ +public final class Queries { + // note that it is vital that each method supplies a constant, not a + // calculated value, as they will be checked for using == + // it is also vital that each constant is different (due to the == checking) + // as such, alterations to use lambdas must be done with extreme care + + /** + * Private constructor since this is a utility class. + */ + private Queries() { + } + + //----------------------------------------------------------------------- + // special constants should be used to extract information from a TemporalAccessor + // that cannot be derived in other ways + // Javadoc added here, so as to pretend they are more normal than they really are + + /** + * A strict query for the {@code ZoneId}. + *

    + * This queries a {@code TemporalAccessor} for the zone. + * The zone is only returned if the date-time conceptually contains a {@code ZoneId}. + * It will not be returned if the date-time only conceptually has an {@code ZoneOffset}. + * Thus a {@link java.time.ZonedDateTime ZonedDateTime} will return the result of + * {@code getZone()}, but an {@link java.time.temporal.OffsetDateTime OffsetDateTime} will + * return null. + *

    + * In most cases, applications should use {@link #ZONE} as this query is too strict. + *

    + * The result from JDK classes implementing {@code TemporalAccessor} is as follows:
    + * {@code LocalDate} returns null
    + * {@code LocalTime} returns null
    + * {@code LocalDateTime} returns null
    + * {@code ZonedDateTime} returns the associated zone
    + * {@code OffsetDate} returns null
    + * {@code OffsetTime} returns null
    + * {@code OffsetDateTime} returns null
    + * {@code ChronoLocalDate} returns null
    + * {@code ChronoLocalDateTime} returns null
    + * {@code ChronoZonedDateTime} returns the associated zone
    + * {@code Era} returns null
    + * {@code DayOfWeek} returns null
    + * {@code Month} returns null
    + * {@code Year} returns null
    + * {@code YearMonth} returns null
    + * {@code MonthDay} returns null
    + * {@code ZoneOffset} returns null
    + * {@code Instant} returns null
    + * @return a ZoneId, may be null + */ + public static final TemporalQuery zoneId() { + return ZONE_ID; + } + static final TemporalQuery ZONE_ID = new TemporalQuery() { + @Override + public ZoneId queryFrom(TemporalAccessor temporal) { + return temporal.query(this); + } + }; + + /** + * A query for the {@code Chrono}. + *

    + * This queries a {@code TemporalAccessor} for the chronology. + * If the target {@code TemporalAccessor} represents a date, or part of a date, + * then it should return the chronology that the date is expressed in. + * As a result of this definition, objects only representing time, such as + * {@code LocalTime}, will return null. + *

    + * The result from JDK classes implementing {@code TemporalAccessor} is as follows:
    + * {@code LocalDate} returns {@code ISOChrono.INSTANCE}
    + * {@code LocalTime} returns null (does not represent a date)
    + * {@code LocalDateTime} returns {@code ISOChrono.INSTANCE}
    + * {@code ZonedDateTime} returns {@code ISOChrono.INSTANCE}
    + * {@code OffsetDate} returns {@code ISOChrono.INSTANCE}
    + * {@code OffsetTime} returns null (does not represent a date)
    + * {@code OffsetDateTime} returns {@code ISOChrono.INSTANCE}
    + * {@code ChronoLocalDate} returns the associated chronology
    + * {@code ChronoLocalDateTime} returns the associated chronology
    + * {@code ChronoZonedDateTime} returns the associated chronology
    + * {@code Era} returns the associated chronology
    + * {@code DayOfWeek} returns null (shared across chronologies)
    + * {@code Month} returns {@code ISOChrono.INSTANCE}
    + * {@code Year} returns {@code ISOChrono.INSTANCE}
    + * {@code YearMonth} returns {@code ISOChrono.INSTANCE}
    + * {@code MonthDay} returns null {@code ISOChrono.INSTANCE}
    + * {@code ZoneOffset} returns null (does not represent a date)
    + * {@code Instant} returns null (does not represent a date)
    + *

    + * The method {@link Chrono#from(TemporalAccessor)} can be used as a + * {@code TemporalQuery} via a method reference, {@code Chrono::from}. + * That method is equivalent to this query, except that it throws an + * exception if a chronology cannot be obtained. + * @return a Chrono, may be null + */ + public static final TemporalQuery> chrono() { + return CHRONO; + } + static final TemporalQuery> CHRONO = new TemporalQuery>() { + @Override + public Chrono queryFrom(TemporalAccessor temporal) { + return temporal.query(this); + } + }; + + /** + * A query for the smallest supported unit. + *

    + * This queries a {@code TemporalAccessor} for the time precision. + * If the target {@code TemporalAccessor} represents a consistent or complete date-time, + * date or time then this must return the smallest precision actually supported. + * Note that fields such as {@code NANO_OF_DAY} and {@code NANO_OF_SECOND} + * are defined to always return ignoring the precision, thus this is the only + * way to find the actual smallest supported unit. + * For example, were {@code GregorianCalendar} to implement {@code TemporalAccessor} + * it would return a precision of {@code MILLIS}. + *

    + * The result from JDK classes implementing {@code TemporalAccessor} is as follows:
    + * {@code LocalDate} returns {@code DAYS}
    + * {@code LocalTime} returns {@code NANOS}
    + * {@code LocalDateTime} returns {@code NANOS}
    + * {@code ZonedDateTime} returns {@code NANOS}
    + * {@code OffsetDate} returns {@code DAYS}
    + * {@code OffsetTime} returns {@code NANOS}
    + * {@code OffsetDateTime} returns {@code NANOS}
    + * {@code ChronoLocalDate} returns {@code DAYS}
    + * {@code ChronoLocalDateTime} returns {@code NANOS}
    + * {@code ChronoZonedDateTime} returns {@code NANOS}
    + * {@code Era} returns {@code ERAS}
    + * {@code DayOfWeek} returns {@code DAYS}
    + * {@code Month} returns {@code MONTHS}
    + * {@code Year} returns {@code YEARS}
    + * {@code YearMonth} returns {@code MONTHS}
    + * {@code MonthDay} returns null (does not represent a complete date or time)
    + * {@code ZoneOffset} returns null (does not represent a date or time)
    + * {@code Instant} returns {@code NANOS}
    + * @return a ChronoUnit, may be null + */ + public static final TemporalQuery precision() { + return PRECISION; + } + static final TemporalQuery PRECISION = new TemporalQuery() { + @Override + public ChronoUnit queryFrom(TemporalAccessor temporal) { + return temporal.query(this); + } + }; + + //----------------------------------------------------------------------- + // non-special constants are standard queries that derive information from other information + /** + * A lenient query for the {@code ZoneId}, falling back to the {@code ZoneOffset}. + *

    + * This queries a {@code TemporalAccessor} for the zone. + * It first tries to obtain the zone, using {@link #zoneId()}. + * If that is not found it tries to obtain the {@link #offset()}. + *

    + * In most cases, applications should use this query rather than {@code #zoneId()}. + *

    + * This query examines the {@link java.time.temporal.ChronoField#OFFSET_SECONDS offset-seconds} + * field and uses it to create a {@code ZoneOffset}. + *

    + * The method {@link ZoneId#from(TemporalAccessor)} can be used as a + * {@code TemporalQuery} via a method reference, {@code ZoneId::from}. + * That method is equivalent to this query, except that it throws an + * exception if a zone cannot be obtained. + * @return a ZoneId, may be null + */ + public static final TemporalQuery zone() { + return ZONE; + } + static final TemporalQuery ZONE = new TemporalQuery() { + @Override + public ZoneId queryFrom(TemporalAccessor temporal) { + ZoneId zone = temporal.query(ZONE_ID); + return (zone != null ? zone : temporal.query(OFFSET)); + } + }; + + /** + * A query for the {@code ZoneOffset}. + *

    + * This queries a {@code TemporalAccessor} for the offset. + *

    + * This query examines the {@link java.time.temporal.ChronoField#OFFSET_SECONDS offset-seconds} + * field and uses it to create a {@code ZoneOffset}. + *

    + * The method {@link ZoneOffset#from(TemporalAccessor)} can be used as a + * {@code TemporalQuery} via a method reference, {@code ZoneOffset::from}. + * That method is equivalent to this query, except that it throws an + * exception if an offset cannot be obtained. + * @return a ZoneOffset, may be null + */ + public static final TemporalQuery offset() { + return OFFSET; + } + static final TemporalQuery OFFSET = new TemporalQuery() { + @Override + public ZoneOffset queryFrom(TemporalAccessor temporal) { + if (temporal.isSupported(OFFSET_SECONDS)) { + return ZoneOffset.ofTotalSeconds(temporal.get(OFFSET_SECONDS)); + } + return null; + } + }; + +} diff --git a/jdk/src/share/classes/java/time/temporal/Ser.java b/jdk/src/share/classes/java/time/temporal/Ser.java new file mode 100644 index 00000000000..2f182e0439b --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/Ser.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2011-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.InvalidClassException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.StreamCorruptedException; +import java.time.LocalDate; +import java.time.LocalDateTime; + +/** + * The shared serialization delegate for this package. + * + *

    Implementation notes

    + * This class wraps the object being serialized, and takes a byte representing the type of the class to + * be serialized. This byte can also be used for versioning the serialization format. In this case another + * byte flag would be used in order to specify an alternative version of the type format. + * For example {@code CHRONO_TYPE_VERSION_2 = 21} + *

    + * In order to serialise the object it writes its byte and then calls back to the appropriate class where + * the serialisation is performed. In order to deserialise the object it read in the type byte, switching + * in order to select which class to call back into. + *

    + * The serialisation format is determined on a per class basis. In the case of field based classes each + * of the fields is written out with an appropriate size format in descending order of the field's size. For + * example in the case of {@link LocalDate} year is written before month. Composite classes, such as + * {@link LocalDateTime} are serialised as one object. Enum classes are serialised using the index of their + * element. + *

    + * This class is mutable and should be created once per serialization. + * + * @serial include + * @since 1.8 + */ +final class Ser implements Externalizable { + + /** + * Serialization version. + */ + private static final long serialVersionUID = -6103370247208168577L; + + static final byte OFFSET_DATE_TYPE = 1; + static final byte OFFSET_TIME_TYPE = 2; + static final byte OFFSET_DATE_TIME_TYPE = 3; + static final byte YEAR_TYPE = 4; + static final byte YEAR_MONTH_TYPE = 5; + static final byte MONTH_DAY_TYPE = 6; + static final byte CHRONO_TYPE = 7; + static final byte CHRONO_LOCAL_DATE_TIME_TYPE = 8; + static final byte CHRONO_ZONE_DATE_TIME_TYPE = 9; + static final byte SIMPLE_PERIOD_TYPE = 10; + + /** The type being serialized. */ + private byte type; + /** The object being serialized. */ + private Object object; + + /** + * Constructor for deserialization. + */ + public Ser() { + } + + /** + * Creates an instance for serialization. + * + * @param type the type + * @param object the object + */ + Ser(byte type, Object object) { + this.type = type; + this.object = object; + } + + //----------------------------------------------------------------------- + /** + * Implements the {@code Externalizable} interface to write the object. + * + * @param out the data stream to write to, not null + */ + @Override + public void writeExternal(ObjectOutput out) throws IOException { + writeInternal(type, object, out); + } + + private static void writeInternal(byte type, Object object, ObjectOutput out) throws IOException { + out.writeByte(type); + switch (type) { + case OFFSET_DATE_TYPE: + ((OffsetDate) object).writeExternal(out); + break; + case OFFSET_TIME_TYPE: + ((OffsetTime) object).writeExternal(out); + break; + case OFFSET_DATE_TIME_TYPE: + ((OffsetDateTime) object).writeExternal(out); + break; + case YEAR_TYPE: + ((Year) object).writeExternal(out); + break; + case YEAR_MONTH_TYPE: + ((YearMonth) object).writeExternal(out); + break; + case MONTH_DAY_TYPE: + ((MonthDay) object).writeExternal(out); + break; + case CHRONO_TYPE: + ((Chrono) object).writeExternal(out); + break; + case CHRONO_LOCAL_DATE_TIME_TYPE: + ((ChronoLocalDateTimeImpl) object).writeExternal(out); + break; + case CHRONO_ZONE_DATE_TIME_TYPE: + ((ChronoZonedDateTimeImpl) object).writeExternal(out); + break; + case SIMPLE_PERIOD_TYPE: + ((SimplePeriod) object).writeExternal(out); + break; + default: + throw new InvalidClassException("Unknown serialized type"); + } + } + + //----------------------------------------------------------------------- + /** + * Implements the {@code Externalizable} interface to read the object. + * + * @param in the data to read, not null + */ + @Override + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + type = in.readByte(); + object = readInternal(type, in); + } + + static Object read(ObjectInput in) throws IOException, ClassNotFoundException { + byte type = in.readByte(); + return readInternal(type, in); + } + + private static Object readInternal(byte type, ObjectInput in) throws IOException, ClassNotFoundException { + switch (type) { + case OFFSET_DATE_TYPE: return OffsetDate.readExternal(in); + case OFFSET_TIME_TYPE: return OffsetTime.readExternal(in); + case OFFSET_DATE_TIME_TYPE: return OffsetDateTime.readExternal(in); + case YEAR_TYPE: return Year.readExternal(in); + case YEAR_MONTH_TYPE: return YearMonth.readExternal(in); + case MONTH_DAY_TYPE: return MonthDay.readExternal(in); + case CHRONO_TYPE: return Chrono.readExternal(in); + case CHRONO_LOCAL_DATE_TIME_TYPE: return ChronoLocalDateTimeImpl.readExternal(in); + case CHRONO_ZONE_DATE_TIME_TYPE: return ChronoZonedDateTimeImpl.readExternal(in); + case SIMPLE_PERIOD_TYPE: return SimplePeriod.readExternal(in); + default: throw new StreamCorruptedException("Unknown serialized type"); + } + } + + /** + * Returns the object that will replace this one. + * + * @return the read object, should never be null + */ + private Object readResolve() { + return object; + } + +} diff --git a/jdk/src/share/classes/java/time/temporal/SimplePeriod.java b/jdk/src/share/classes/java/time/temporal/SimplePeriod.java new file mode 100644 index 00000000000..5eb60f5c903 --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/SimplePeriod.java @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.time.DateTimeException; +import java.util.Objects; + +/** + * A period of time, measured as an amount of a single unit, such as '3 Months'. + *

    + * A {@code SimplePeriod} represents an amount of time measured in terms of a + * single {@link TemporalUnit unit}. Any unit may be used with this class. + * An alternative period implementation is {@link java.time.Period Period}, which + * allows a combination of date and time units. + *

    + * This class is the return type from {@link TemporalUnit#between}. + * It can be used more generally, but is designed to enable the following code: + *

    + *  date = date.minus(MONTHS.between(start, end));
    + * 
    + * The unit determines which calendar systems it can be added to. + *

    + * The period is modeled as a directed amount of time, meaning that the period may + * be negative. See {@link #abs()} to ensure the period is positive. + * + *

    Specification for implementors

    + * This class is immutable and thread-safe, providing that the unit is immutable, + * which it is required to be. + * + * @since 1.8 + */ +public final class SimplePeriod + implements TemporalAdder, TemporalSubtractor, Comparable, Serializable { + + /** + * Serialization version. + */ + private static final long serialVersionUID = 3752975649629L; + + /** + * The amount of the unit. + */ + private final long amount; + /** + * The unit. + */ + private final TemporalUnit unit; + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code SimplePeriod} from a period in the specified unit. + *

    + * The parameters represent the two parts of a phrase like '6 Days'. For example: + *

    +     *  SimplePeriod.of(3, SECONDS);
    +     *  SimplePeriod.of(5, YEARS);
    +     * 
    + * + * @param amount the amount of the period, measured in terms of the unit, positive or negative + * @param unit the unit that the period is measured in, not null + * @return the period, not null + */ + public static SimplePeriod of(long amount, TemporalUnit unit) { + Objects.requireNonNull(unit, "unit"); + return new SimplePeriod(amount, unit); + } + + //----------------------------------------------------------------------- + /** + * Constructor. + * + * @param amount the amount of the period, measured in terms of the unit, positive or negative + * @param unit the unit that the period is measured in, not null + */ + SimplePeriod(long amount, TemporalUnit unit) { + this.amount = amount; + this.unit = unit; + } + + //----------------------------------------------------------------------- + /** + * Gets the amount of this period. + *

    + * In the phrase "2 Months", the amount is 2. + * + * @return the amount of the period, may be negative + */ + public long getAmount() { + return amount; + } + + /** + * Gets the unit of this period. + *

    + * In the phrase "2 Months", the unit is "Months". + * + * @return the unit of the period, not null + */ + public TemporalUnit getUnit() { + return unit; + } + + //------------------------------------------------------------------------- + /** + * Adds this period to the specified temporal object. + *

    + * This returns a temporal object of the same observable type as the input + * with this period added. + *

    + * In most cases, it is clearer to reverse the calling pattern by using + * {@link Temporal#plus(TemporalAdder)}. + *

    +     *   // these two lines are equivalent, but the second approach is recommended
    +     *   dateTime = thisPeriod.addTo(dateTime);
    +     *   dateTime = dateTime.plus(thisPeriod);
    +     * 
    + *

    + * The calculation is equivalent to invoking {@link Temporal#plus(long, TemporalUnit)}. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param temporal the temporal object to adjust, not null + * @return an object of the same type with the adjustment made, not null + * @throws DateTimeException if unable to add + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public Temporal addTo(Temporal temporal) { + return temporal.plus(amount, unit); + } + + /** + * Subtracts this period to the specified temporal object. + *

    + * This returns a temporal object of the same observable type as the input + * with this period subtracted. + *

    + * In most cases, it is clearer to reverse the calling pattern by using + * {@link Temporal#plus(TemporalAdder)}. + *

    +     *   // these two lines are equivalent, but the second approach is recommended
    +     *   dateTime = thisPeriod.subtractFrom(dateTime);
    +     *   dateTime = dateTime.minus(thisPeriod);
    +     * 
    + *

    + * The calculation is equivalent to invoking {@link Temporal#minus(long, TemporalUnit)}. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param temporal the temporal object to adjust, not null + * @return an object of the same type with the adjustment made, not null + * @throws DateTimeException if unable to subtract + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public Temporal subtractFrom(Temporal temporal) { + return temporal.minus(amount, unit); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this period with a positive amount. + *

    + * This returns a period with the absolute value of the amount and the same unit. + * If the amount of this period is positive or zero, then this period is returned. + * If the amount of this period is negative, then a period with the negated + * amount is returned. If the amount equals {@code Long.MIN_VALUE}, + * an {@code ArithmeticException} is thrown + *

    + * This is useful to convert the result of {@link TemporalUnit#between} to + * a positive amount when you do not know which date is the earlier and + * which is the later. + * + * @return a period with a positive amount and the same unit, not null + * @throws ArithmeticException if the amount is {@code Long.MIN_VALUE} + */ + public SimplePeriod abs() { + if (amount == Long.MIN_VALUE) { + throw new ArithmeticException("Unable to call abs() on MIN_VALUE"); + } + return (amount >= 0 ? this : new SimplePeriod(-amount, unit)); + } + + //----------------------------------------------------------------------- + /** + * Compares this {@code SimplePeriod} to another period. + *

    + * The comparison is based on the amount within the unit. + * Only two periods with the same unit can be compared. + * + * @param other the other period to compare to, not null + * @return the comparator value, negative if less, positive if greater + * @throws IllegalArgumentException if the units do not match + */ + @Override + public int compareTo(SimplePeriod other) { + if (unit.equals(other.unit) == false) { + throw new IllegalArgumentException("Unable to compare periods with different units"); + } + return Long.compare(amount, other.amount); + } + + //----------------------------------------------------------------------- + /** + * Checks if this period is equal to another period. + *

    + * The comparison is based on the amount and unit. + * + * @param obj the object to check, null returns false + * @return true if this is equal to the other period + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof SimplePeriod) { + SimplePeriod other = (SimplePeriod) obj; + return amount == other.amount && unit.equals(other.unit); + } + return false; + } + + /** + * A hash code for this period. + * + * @return a suitable hash code + */ + @Override + public int hashCode() { + return unit.hashCode() ^ (int) (amount ^ (amount >>> 32)); + } + + //----------------------------------------------------------------------- + /** + * Outputs this period as a {@code String}, such as {@code 2 Months}. + *

    + * The string consists of the amount, then a space, then the unit name. + * + * @return a string representation of this period, not null + */ + @Override + public String toString() { + return amount + " " + unit.getName(); + } + + //----------------------------------------------------------------------- + /** + * Writes the object using a + * dedicated serialized form. + *

    +     *  out.writeByte(10);  // identifies this as a SimplePeriod
    +     *  out.writeLong(amount);
    +     *  out.writeObject(unit);
    +     * 
    + * + * @return the instance of {@code Ser}, not null + */ + private Object writeReplace() { + return new Ser(Ser.SIMPLE_PERIOD_TYPE, this); + } + + /** + * Defend against malicious streams. + * @return never + * @throws InvalidObjectException always + */ + private Object readResolve() throws ObjectStreamException { + throw new InvalidObjectException("Deserialization via serialization delegate"); + } + + void writeExternal(ObjectOutput out) throws IOException { + out.writeLong(amount); + out.writeObject(unit); + } + + static SimplePeriod readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + long amount = in.readLong(); + TemporalUnit unit = (TemporalUnit) in.readObject(); + return SimplePeriod.of(amount, unit); + } + +} diff --git a/jdk/src/share/classes/java/time/temporal/Temporal.java b/jdk/src/share/classes/java/time/temporal/Temporal.java new file mode 100644 index 00000000000..0eddf27b964 --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/Temporal.java @@ -0,0 +1,410 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import java.time.DateTimeException; +import java.time.ZoneId; + +/** + * Framework-level interface defining read-write access to a temporal object, + * such as a date, time, offset or some combination of these. + *

    + * This is the base interface type for date, time and offset objects that + * are complete enough to be manipulated using plus and minus. + * It is implemented by those classes that can provide and manipulate information + * as {@linkplain TemporalField fields} or {@linkplain TemporalQuery queries}. + * See {@link TemporalAccessor} for the read-only version of this interface. + *

    + * Most date and time information can be represented as a number. + * These are modeled using {@code TemporalField} with the number held using + * a {@code long} to handle large values. Year, month and day-of-month are + * simple examples of fields, but they also include instant and offsets. + * See {@link ChronoField} for the standard set of fields. + *

    + * Two pieces of date/time information cannot be represented by numbers, + * the {@linkplain Chrono chronology} and the {@linkplain ZoneId time-zone}. + * These can be accessed via {@link #query(TemporalQuery) queries} using + * the static methods defined on {@link Queries}. + *

    + * This interface is a framework-level interface that should not be widely + * used in application code. Instead, applications should create and pass + * around instances of concrete types, such as {@code LocalDate}. + * There are many reasons for this, part of which is that implementations + * of this interface may be in calendar systems other than ISO. + * See {@link ChronoLocalDate} for a fuller discussion of the issues. + * + *

    When to implement

    + *

    + * A class should implement this interface if it meets three criteria: + *

      + *
    • it provides access to date/time/offset information, as per {@code TemporalAccessor} + *
    • the set of fields are contiguous from the largest to the smallest + *
    • the set of fields are complete, such that no other field is needed to define the + * valid range of values for the fields that are represented + *

    + *

    + * Four examples make this clear: + *

      + *
    • {@code LocalDate} implements this interface as it represents a set of fields + * that are contiguous from days to forever and require no external information to determine + * the validity of each date. It is therefore able to implement plus/minus correctly. + *
    • {@code LocalTime} implements this interface as it represents a set of fields + * that are contiguous from nanos to within days and require no external information to determine + * validity. It is able to implement plus/minus correctly, by wrapping around the day. + *
    • {@code MonthDay}, the combination of month-of-year and day-of-month, does not implement + * this interface. While the combination is contiguous, from days to months within years, + * the combination does not have sufficient information to define the valid range of values + * for day-of-month. As such, it is unable to implement plus/minus correctly. + *
    • The combination day-of-week and day-of-month ("Friday the 13th") should not implement + * this interface. It does not represent a contiguous set of fields, as days to weeks overlaps + * days to months. + *

    + * + *

    Specification for implementors

    + * This interface places no restrictions on the mutability of implementations, + * however immutability is strongly recommended. + * All implementations must be {@link Comparable}. + * + * @since 1.8 + */ +public interface Temporal extends TemporalAccessor { + + /** + * Returns an adjusted object of the same type as this object with the adjustment made. + *

    + * This adjusts this date-time according to the rules of the specified adjuster. + * A simple adjuster might simply set the one of the fields, such as the year field. + * A more complex adjuster might set the date to the last day of the month. + * A selection of common adjustments is provided in {@link Adjusters}. + * These include finding the "last day of the month" and "next Wednesday". + * The adjuster is responsible for handling special cases, such as the varying + * lengths of month and leap years. + *

    + * Some example code indicating how and why this method is used: + *

    +     *  date = date.with(Month.JULY);        // most key classes implement TemporalAdjuster
    +     *  date = date.with(lastDayOfMonth());  // static import from Adjusters
    +     *  date = date.with(next(WEDNESDAY));   // static import from Adjusters and DayOfWeek
    +     * 
    + * + *

    Specification for implementors

    + * Implementations must not alter either this object. + * Instead, an adjusted copy of the original must be returned. + * This provides equivalent, safe behavior for immutable and mutable implementations. + *

    + * The default implementation must behave equivalent to this code: + *

    +     *  return adjuster.adjustInto(this);
    +     * 
    + * + * @param adjuster the adjuster to use, not null + * @return an object of the same type with the specified adjustment made, not null + * @throws DateTimeException if unable to make the adjustment + * @throws ArithmeticException if numeric overflow occurs + */ + public default Temporal with(TemporalAdjuster adjuster) { + return adjuster.adjustInto(this); + } + + /** + * Returns an object of the same type as this object with the specified field altered. + *

    + * This returns a new object based on this one with the value for the specified field changed. + * For example, on a {@code LocalDate}, this could be used to set the year, month or day-of-month. + * The returned object will have the same observable type as this object. + *

    + * In some cases, changing a field is not fully defined. For example, if the target object is + * a date representing the 31st January, then changing the month to February would be unclear. + * In cases like this, the field is responsible for resolving the result. Typically it will choose + * the previous valid date, which would be the last valid day of February in this example. + * + *

    Specification for implementors

    + * Implementations must check and handle all fields defined in {@link ChronoField}. + * If the field is supported, then the adjustment must be performed. + * If unsupported, then a {@code DateTimeException} must be thrown. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doWith(Temporal, long)} + * passing {@code this} as the first argument. + *

    + * Implementations must not alter either this object or the specified temporal object. + * Instead, an adjusted copy of the original must be returned. + * This provides equivalent, safe behavior for immutable and mutable implementations. + * + * @param field the field to set in the result, not null + * @param newValue the new value of the field in the result + * @return an object of the same type with the specified field set, not null + * @throws DateTimeException if the field cannot be set + * @throws ArithmeticException if numeric overflow occurs + */ + Temporal with(TemporalField field, long newValue); + + //----------------------------------------------------------------------- + /** + * Returns an object of the same type as this object with an amount added. + *

    + * This adjusts this temporal, adding according to the rules of the specified adder. + * The adder is typically a {@link java.time.Period} but may be any other type implementing + * the {@link TemporalAdder} interface, such as {@link java.time.Duration}. + *

    + * Some example code indicating how and why this method is used: + *

    +     *  date = date.plus(period);                      // add a Period instance
    +     *  date = date.plus(duration);                    // add a Duration instance
    +     *  date = date.plus(MONTHS.between(start, end));  // static import of MONTHS field
    +     *  date = date.plus(workingDays(6));              // example user-written workingDays method
    +     * 
    + *

    + * Note that calling {@code plus} followed by {@code minus} is not guaranteed to + * return the same date-time. + * + *

    Specification for implementors

    + * Implementations must not alter either this object. + * Instead, an adjusted copy of the original must be returned. + * This provides equivalent, safe behavior for immutable and mutable implementations. + *

    + * The default implementation must behave equivalent to this code: + *

    +     *  return adder.addTo(this);
    +     * 
    + * + * @param adder the adder to use, not null + * @return an object of the same type with the specified adjustment made, not null + * @throws DateTimeException if the addition cannot be made + * @throws ArithmeticException if numeric overflow occurs + */ + public default Temporal plus(TemporalAdder adder) { + return adder.addTo(this); + } + + /** + * Returns an object of the same type as this object with the specified period added. + *

    + * This method returns a new object based on this one with the specified period added. + * For example, on a {@code LocalDate}, this could be used to add a number of years, months or days. + * The returned object will have the same observable type as this object. + *

    + * In some cases, changing a field is not fully defined. For example, if the target object is + * a date representing the 31st January, then adding one month would be unclear. + * In cases like this, the field is responsible for resolving the result. Typically it will choose + * the previous valid date, which would be the last valid day of February in this example. + *

    + * If the implementation represents a date-time that has boundaries, such as {@code LocalTime}, + * then the permitted units must include the boundary unit, but no multiples of the boundary unit. + * For example, {@code LocalTime} must accept {@code DAYS} but not {@code WEEKS} or {@code MONTHS}. + * + *

    Specification for implementors

    + * Implementations must check and handle all units defined in {@link ChronoUnit}. + * If the unit is supported, then the addition must be performed. + * If unsupported, then a {@code DateTimeException} must be thrown. + *

    + * If the unit is not a {@code ChronoUnit}, then the result of this method + * is obtained by invoking {@code TemporalUnit.doPlus(Temporal, long)} + * passing {@code this} as the first argument. + *

    + * Implementations must not alter either this object or the specified temporal object. + * Instead, an adjusted copy of the original must be returned. + * This provides equivalent, safe behavior for immutable and mutable implementations. + * + * @param amountToAdd the amount of the specified unit to add, may be negative + * @param unit the unit of the period to add, not null + * @return an object of the same type with the specified period added, not null + * @throws DateTimeException if the unit cannot be added + * @throws ArithmeticException if numeric overflow occurs + */ + Temporal plus(long amountToAdd, TemporalUnit unit); + + //----------------------------------------------------------------------- + /** + * Returns an object of the same type as this object with an amount subtracted. + *

    + * This adjusts this temporal, subtracting according to the rules of the specified subtractor. + * The subtractor is typically a {@link java.time.Period} but may be any other type implementing + * the {@link TemporalSubtractor} interface, such as {@link java.time.Duration}. + *

    + * Some example code indicating how and why this method is used: + *

    +     *  date = date.minus(period);                      // subtract a Period instance
    +     *  date = date.minus(duration);                    // subtract a Duration instance
    +     *  date = date.minus(MONTHS.between(start, end));  // static import of MONTHS field
    +     *  date = date.minus(workingDays(6));              // example user-written workingDays method
    +     * 
    + *

    + * Note that calling {@code plus} followed by {@code minus} is not guaranteed to + * return the same date-time. + * + *

    Specification for implementors

    + * Implementations must not alter either this object. + * Instead, an adjusted copy of the original must be returned. + * This provides equivalent, safe behavior for immutable and mutable implementations. + *

    + * The default implementation must behave equivalent to this code: + *

    +     *  return subtractor.subtractFrom(this);
    +     * 
    + * + * @param subtractor the subtractor to use, not null + * @return an object of the same type with the specified adjustment made, not null + * @throws DateTimeException if the subtraction cannot be made + * @throws ArithmeticException if numeric overflow occurs + */ + public default Temporal minus(TemporalSubtractor subtractor) { + return subtractor.subtractFrom(this); + } + + /** + * Returns an object of the same type as this object with the specified period subtracted. + *

    + * This method returns a new object based on this one with the specified period subtracted. + * For example, on a {@code LocalDate}, this could be used to subtract a number of years, months or days. + * The returned object will have the same observable type as this object. + *

    + * In some cases, changing a field is not fully defined. For example, if the target object is + * a date representing the 31st March, then subtracting one month would be unclear. + * In cases like this, the field is responsible for resolving the result. Typically it will choose + * the previous valid date, which would be the last valid day of February in this example. + *

    + * If the implementation represents a date-time that has boundaries, such as {@code LocalTime}, + * then the permitted units must include the boundary unit, but no multiples of the boundary unit. + * For example, {@code LocalTime} must accept {@code DAYS} but not {@code WEEKS} or {@code MONTHS}. + * + *

    Specification for implementors

    + * Implementations must behave in a manor equivalent to the default method behavior. + *

    + * Implementations must not alter either this object or the specified temporal object. + * Instead, an adjusted copy of the original must be returned. + * This provides equivalent, safe behavior for immutable and mutable implementations. + *

    + * The default implementation must behave equivalent to this code: + *

    +     *  return (amountToSubtract == Long.MIN_VALUE ?
    +     *      plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit));
    +     * 
    + * + * @param amountToSubtract the amount of the specified unit to subtract, may be negative + * @param unit the unit of the period to subtract, not null + * @return an object of the same type with the specified period subtracted, not null + * @throws DateTimeException if the unit cannot be subtracted + * @throws ArithmeticException if numeric overflow occurs + */ + public default Temporal minus(long amountToSubtract, TemporalUnit unit) { + return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit)); + } + + //----------------------------------------------------------------------- + /** + * Calculates the period between this temporal and another temporal in + * terms of the specified unit. + *

    + * This calculates the period between two temporals in terms of a single unit. + * The start and end points are {@code this} and the specified temporal. + * The result will be negative if the end is before the start. + * For example, the period in hours between two temporal objects can be + * calculated using {@code startTime.periodUntil(endTime, HOURS)}. + *

    + * The calculation returns a whole number, representing the number of + * complete units between the two temporals. + * For example, the period in hours between the times 11:30 and 13:29 + * will only be one hour as it is one minute short of two hours. + *

    + * This method operates in association with {@link TemporalUnit#between}. + * The result of this method is a {@code long} representing the amount of + * the specified unit. By contrast, the result of {@code between} is an + * object that can be used directly in addition/subtraction: + *

    +     *   long period = start.periodUntil(end, HOURS);   // this method
    +     *   dateTime.plus(HOURS.between(start, end));      // use in plus/minus
    +     * 
    + * + *

    Specification for implementors

    + * Implementations must begin by checking to ensure that the input temporal + * object is of the same observable type as the implementation. + * They must then perform the calculation for all instances of {@link ChronoUnit}. + * A {@code DateTimeException} must be thrown for {@code ChronoUnit} + * instances that are unsupported. + *

    + * If the unit is not a {@code ChronoUnit}, then the result of this method + * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)} + * passing {@code this} as the first argument and the input temporal as + * the second argument. + *

    + * In summary, implementations must behave in a manner equivalent to this code: + *

    +     *  // check input temporal is the same type as this class
    +     *  if (unit instanceof ChronoUnit) {
    +     *    // if unit is supported, then calculate and return result
    +     *    // else throw DateTimeException for unsupported units
    +     *  }
    +     *  return unit.between(this, endTime).getAmount();
    +     * 
    + *

    + * The target object must not be altered by this method. + * + * @param endTemporal the end temporal, of the same type as this object, not null + * @param unit the unit to measure the period in, not null + * @return the amount of the period between this and the end + * @throws DateTimeException if the period cannot be calculated + * @throws ArithmeticException if numeric overflow occurs + */ + long periodUntil(Temporal endTemporal, TemporalUnit unit); + +} diff --git a/jdk/src/share/classes/java/time/temporal/TemporalAccessor.java b/jdk/src/share/classes/java/time/temporal/TemporalAccessor.java new file mode 100644 index 00000000000..2f88fa74c7a --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/TemporalAccessor.java @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import java.time.DateTimeException; +import java.time.ZoneId; + +/** + * Framework-level interface defining read-only access to a temporal object, + * such as a date, time, offset or some combination of these. + *

    + * This is the base interface type for date, time and offset objects. + * It is implemented by those classes that can provide information + * as {@linkplain TemporalField fields} or {@linkplain TemporalQuery queries}. + *

    + * Most date and time information can be represented as a number. + * These are modeled using {@code TemporalField} with the number held using + * a {@code long} to handle large values. Year, month and day-of-month are + * simple examples of fields, but they also include instant and offsets. + * See {@link ChronoField} for the standard set of fields. + *

    + * Two pieces of date/time information cannot be represented by numbers, + * the {@linkplain Chrono chronology} and the {@linkplain ZoneId time-zone}. + * These can be accessed via {@link #query(TemporalQuery) queries} using + * the static methods defined on {@link Queries}. + *

    + * A sub-interface, {@link Temporal}, extends this definition to one that also + * supports adjustment and manipulation on more complete temporal objects. + *

    + * This interface is a framework-level interface that should not be widely + * used in application code. Instead, applications should create and pass + * around instances of concrete types, such as {@code LocalDate}. + * There are many reasons for this, part of which is that implementations + * of this interface may be in calendar systems other than ISO. + * See {@link ChronoLocalDate} for a fuller discussion of the issues. + * + *

    Specification for implementors

    + * This interface places no restrictions on the mutability of implementations, + * however immutability is strongly recommended. + * + * @since 1.8 + */ +public interface TemporalAccessor { + + /** + * Checks if the specified field is supported. + *

    + * This checks if the date-time can be queried for the specified field. + * If false, then calling the {@link #range(TemporalField) range} and {@link #get(TemporalField) get} + * methods will throw an exception. + * + *

    Specification for implementors

    + * Implementations must check and handle all fields defined in {@link ChronoField}. + * If the field is supported, then true is returned, otherwise false + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doIsSupported(TemporalAccessor)} + * passing {@code this} as the argument. + *

    + * Implementations must not alter either this object. + * + * @param field the field to check, null returns false + * @return true if this date-time can be queried for the field, false if not + */ + boolean isSupported(TemporalField field); + + /** + * Gets the range of valid values for the specified field. + *

    + * All fields can be expressed as a {@code long} integer. + * This method returns an object that describes the valid range for that value. + * The value of this temporal object is used to enhance the accuracy of the returned range. + * If the date-time cannot return the range, because the field is unsupported or for + * some other reason, an exception will be thrown. + *

    + * Note that the result only describes the minimum and maximum valid values + * and it is important not to read too much into them. For example, there + * could be values within the range that are invalid for the field. + * + *

    Specification for implementors

    + * Implementations must check and handle all fields defined in {@link ChronoField}. + * If the field is supported, then the range of the field must be returned. + * If unsupported, then a {@code DateTimeException} must be thrown. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doRange(TemporalAccessorl)} + * passing {@code this} as the argument. + *

    + * Implementations must not alter either this object. + *

    + * The default implementation must behave equivalent to this code: + *

    +     *  if (field instanceof ChronoField) {
    +     *    if (isSupported(field)) {
    +     *      return field.range();
    +     *    }
    +     *    throw new DateTimeException("Unsupported field: " + field.getName());
    +     *  }
    +     *  return field.doRange(this);
    +     * 
    + * + * @param field the field to query the range for, not null + * @return the range of valid values for the field, not null + * @throws DateTimeException if the range for the field cannot be obtained + */ + public default ValueRange range(TemporalField field) { + if (field instanceof ChronoField) { + if (isSupported(field)) { + return field.range(); + } + throw new DateTimeException("Unsupported field: " + field.getName()); + } + return field.doRange(this); + } + + /** + * Gets the value of the specified field as an {@code int}. + *

    + * This queries the date-time for the value for the specified field. + * The returned value will always be within the valid range of values for the field. + * If the date-time cannot return the value, because the field is unsupported or for + * some other reason, an exception will be thrown. + * + *

    Specification for implementors

    + * Implementations must check and handle all fields defined in {@link ChronoField}. + * If the field is supported and has an {@code int} range, then the value of + * the field must be returned. + * If unsupported, then a {@code DateTimeException} must be thrown. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doGet(TemporalAccessor)} + * passing {@code this} as the argument. + *

    + * Implementations must not alter either this object. + *

    + * The default implementation must behave equivalent to this code: + *

    +     *  return range(field).checkValidIntValue(getLong(field), field);
    +     * 
    + * + * @param field the field to get, not null + * @return the value for the field, within the valid range of values + * @throws DateTimeException if a value for the field cannot be obtained + * @throws DateTimeException if the range of valid values for the field exceeds an {@code int} + * @throws DateTimeException if the value is outside the range of valid values for the field + * @throws ArithmeticException if numeric overflow occurs + */ + public default int get(TemporalField field) { + return range(field).checkValidIntValue(getLong(field), field); + } + + /** + * Gets the value of the specified field as a {@code long}. + *

    + * This queries the date-time for the value for the specified field. + * The returned value may be outside the valid range of values for the field. + * If the date-time cannot return the value, because the field is unsupported or for + * some other reason, an exception will be thrown. + * + *

    Specification for implementors

    + * Implementations must check and handle all fields defined in {@link ChronoField}. + * If the field is supported, then the value of the field must be returned. + * If unsupported, then a {@code DateTimeException} must be thrown. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doGet(TemporalAccessor)} + * passing {@code this} as the argument. + *

    + * Implementations must not alter either this object. + * + * @param field the field to get, not null + * @return the value for the field + * @throws DateTimeException if a value for the field cannot be obtained + * @throws ArithmeticException if numeric overflow occurs + */ + long getLong(TemporalField field); + + /** + * Queries this date-time. + *

    + * This queries this date-time using the specified query strategy object. + *

    + * Queries are a key tool for extracting information from date-times. + * They exists to externalize the process of querying, permitting different + * approaches, as per the strategy design pattern. + * Examples might be a query that checks if the date is the day before February 29th + * in a leap year, or calculates the number of days to your next birthday. + *

    + * The most common query implementations are method references, such as + * {@code LocalDate::from} and {@code ZoneId::from}. + * Further implementations are on {@link Queries}. + * Queries may also be defined by applications. + * + *

    Specification for implementors

    + * The default implementation must behave equivalent to this code: + *
    +     *  if (query == Queries.zoneId() || query == Queries.chrono() || query == Queries.precision()) {
    +     *    return null;
    +     *  }
    +     *  return query.queryFrom(this);
    +     * 
    + * Future versions are permitted to add further queries to the if statement. + *

    + * All classes implementing this interface and overriding this method must call + * {@code TemporalAccessor.super.query(query)}. JDK classes may avoid calling + * super if they provide behavior equivalent to the default behaviour, however + * non-JDK classes may not utilize this optimization and must call {@code super}. + *

    + * If the implementation can supply a value for one of the queries listed in the + * if statement of the default implementation, then it must do so. + * For example, an application-defined {@code HourMin} class storing the hour + * and minute must override this method as follows: + *

    +     *  if (query == Queries.precision()) {
    +     *    return MINUTES;
    +     *  }
    +     *  return TemporalAccessor.super.query(query);
    +     * 
    + * + * @param the type of the result + * @param query the query to invoke, not null + * @return the query result, null may be returned (defined by the query) + * @throws DateTimeException if unable to query + * @throws ArithmeticException if numeric overflow occurs + */ + public default R query(TemporalQuery query) { + if (query == Queries.zoneId() || query == Queries.chrono() || query == Queries.precision()) { + return null; + } + return query.queryFrom(this); + } + +} diff --git a/jdk/src/share/classes/java/time/temporal/TemporalAdder.java b/jdk/src/share/classes/java/time/temporal/TemporalAdder.java new file mode 100644 index 00000000000..fad2db42abb --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/TemporalAdder.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import java.time.DateTimeException; +import java.time.Duration; +import java.time.Period; + +/** + * Strategy for adding to a temporal object. + *

    + * Adders are a key tool for modifying temporal objects. + * They exist to externalize the process of addition, permitting different + * approaches, as per the strategy design pattern. + *

    + * There are two equivalent ways of using a {@code TemporalAdder}. + * The first is to invoke the method on this interface directly. + * The second is to use {@link Temporal#plus(TemporalAdder)}: + *

    + *   // these two lines are equivalent, but the second approach is recommended
    + *   dateTime = adder.addTo(dateTime);
    + *   dateTime = dateTime.plus(adder);
    + * 
    + * It is recommended to use the second approach, {@code plus(TemporalAdder)}, + * as it is a lot clearer to read in code. + *

    + * The {@link Period} and {@link Duration} classes implement this interface. + * Adders may also be defined by applications. + * + *

    Specification for implementors

    + * This interface places no restrictions on the mutability of implementations, + * however immutability is strongly recommended. + * + * @since 1.8 + */ +public interface TemporalAdder { + + /** + * Adds to the specified temporal object. + *

    + * This adds to the specified temporal object using the logic + * encapsulated in the implementing class. + *

    + * There are two equivalent ways of using this method. + * The first is to invoke this method directly. + * The second is to use {@link Temporal#plus(TemporalAdder)}: + *

    +     *   // these two lines are equivalent, but the second approach is recommended
    +     *   dateTime = adder.addTo(dateTime);
    +     *   dateTime = dateTime.plus(adder);
    +     * 
    + * It is recommended to use the second approach, {@code plus(TemporalAdder)}, + * as it is a lot clearer to read in code. + * + *

    Specification for implementors

    + * The implementation must take the input object and add to it. + * The implementation defines the logic of the addition and is responsible for + * documenting that logic. It may use any method on {@code Temporal} to + * query the temporal object and perform the addition. + * The returned object must have the same observable type as the input object + *

    + * The input object must not be altered. + * Instead, an adjusted copy of the original must be returned. + * This provides equivalent, safe behavior for immutable and mutable temporal objects. + *

    + * The input temporal object may be in a calendar system other than ISO. + * Implementations may choose to document compatibility with other calendar systems, + * or reject non-ISO temporal objects by {@link Queries#chrono() querying the chronology}. + *

    + * This method may be called from multiple threads in parallel. + * It must be thread-safe when invoked. + * + * @param temporal the temporal object to adjust, not null + * @return an object of the same observable type with the addition made, not null + * @throws DateTimeException if unable to add + * @throws ArithmeticException if numeric overflow occurs + */ + Temporal addTo(Temporal temporal); + +} diff --git a/jdk/src/share/classes/java/time/temporal/TemporalAdjuster.java b/jdk/src/share/classes/java/time/temporal/TemporalAdjuster.java new file mode 100644 index 00000000000..4a6f03d2875 --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/TemporalAdjuster.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import java.time.DateTimeException; + +/** + * Strategy for adjusting a temporal object. + *

    + * Adjusters are a key tool for modifying temporal objects. + * They exist to externalize the process of adjustment, permitting different + * approaches, as per the strategy design pattern. + * Examples might be an adjuster that sets the date avoiding weekends, or one that + * sets the date to the last day of the month. + *

    + * There are two equivalent ways of using a {@code TemporalAdjuster}. + * The first is to invoke the method on this interface directly. + * The second is to use {@link Temporal#with(TemporalAdjuster)}: + *

    + *   // these two lines are equivalent, but the second approach is recommended
    + *   temporal = thisAdjuster.adjustInto(temporal);
    + *   temporal = temporal.with(thisAdjuster);
    + * 
    + * It is recommended to use the second approach, {@code with(TemporalAdjuster)}, + * as it is a lot clearer to read in code. + *

    + * See {@link Adjusters} for a standard set of adjusters, including finding the + * last day of the month. + * Adjusters may also be defined by applications. + * + *

    Specification for implementors

    + * This interface places no restrictions on the mutability of implementations, + * however immutability is strongly recommended. + * + * @since 1.8 + */ +public interface TemporalAdjuster { + + /** + * Adjusts the specified temporal object. + *

    + * This adjusts the specified temporal object using the logic + * encapsulated in the implementing class. + * Examples might be an adjuster that sets the date avoiding weekends, or one that + * sets the date to the last day of the month. + *

    + * There are two equivalent ways of using this method. + * The first is to invoke this method directly. + * The second is to use {@link Temporal#with(TemporalAdjuster)}: + *

    +     *   // these two lines are equivalent, but the second approach is recommended
    +     *   temporal = thisAdjuster.adjustInto(temporal);
    +     *   temporal = temporal.with(thisAdjuster);
    +     * 
    + * It is recommended to use the second approach, {@code with(TemporalAdjuster)}, + * as it is a lot clearer to read in code. + * + *

    Specification for implementors

    + * The implementation must take the input object and adjust it. + * The implementation defines the logic of the adjustment and is responsible for + * documenting that logic. It may use any method on {@code Temporal} to + * query the temporal object and perform the adjustment. + * The returned object must have the same observable type as the input object + *

    + * The input object must not be altered. + * Instead, an adjusted copy of the original must be returned. + * This provides equivalent, safe behavior for immutable and mutable temporal objects. + *

    + * The input temporal object may be in a calendar system other than ISO. + * Implementations may choose to document compatibility with other calendar systems, + * or reject non-ISO temporal objects by {@link Queries#chrono() querying the chronology}. + *

    + * This method may be called from multiple threads in parallel. + * It must be thread-safe when invoked. + * + * @param temporal the temporal object to adjust, not null + * @return an object of the same observable type with the adjustment made, not null + * @throws DateTimeException if unable to make the adjustment + * @throws ArithmeticException if numeric overflow occurs + */ + Temporal adjustInto(Temporal temporal); + +} diff --git a/jdk/src/share/classes/java/time/temporal/TemporalField.java b/jdk/src/share/classes/java/time/temporal/TemporalField.java new file mode 100644 index 00000000000..187d9e9b049 --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/TemporalField.java @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import java.time.DateTimeException; +import java.time.format.DateTimeBuilder; +import java.util.Comparator; + +/** + * A field of date-time, such as month-of-year or hour-of-minute. + *

    + * Date and time is expressed using fields which partition the time-line into something + * meaningful for humans. Implementations of this interface represent those fields. + *

    + * The most commonly used units are defined in {@link ChronoField}. + * Further fields are supplied in {@link ISOFields}, {@link WeekFields} and {@link JulianFields}. + * Fields can also be written by application code by implementing this interface. + *

    + * The field works using double dispatch. Client code calls methods on a date-time like + * {@code LocalDateTime} which check if the field is a {@code ChronoField}. + * If it is, then the date-time must handle it. + * Otherwise, the method call is re-dispatched to the matching method in this interface. + * + *

    Specification for implementors

    + * This interface must be implemented with care to ensure other classes operate correctly. + * All implementations that can be instantiated must be final, immutable and thread-safe. + * It is recommended to use an enum where possible. + * + * @since 1.8 + */ +public interface TemporalField extends Comparator { + + /** + * Gets a descriptive name for the field. + *

    + * The should be of the format 'BaseOfRange', such as 'MonthOfYear', + * unless the field has a range of {@code FOREVER}, when only + * the base unit is mentioned, such as 'Year' or 'Era'. + * + * @return the name, not null + */ + String getName(); + + /** + * Gets the unit that the field is measured in. + *

    + * The unit of the field is the period that varies within the range. + * For example, in the field 'MonthOfYear', the unit is 'Months'. + * See also {@link #getRangeUnit()}. + * + * @return the period unit defining the base unit of the field, not null + */ + TemporalUnit getBaseUnit(); + + /** + * Gets the range that the field is bound by. + *

    + * The range of the field is the period that the field varies within. + * For example, in the field 'MonthOfYear', the range is 'Years'. + * See also {@link #getBaseUnit()}. + *

    + * The range is never null. For example, the 'Year' field is shorthand for + * 'YearOfForever'. It therefore has a unit of 'Years' and a range of 'Forever'. + * + * @return the period unit defining the range of the field, not null + */ + TemporalUnit getRangeUnit(); + + //----------------------------------------------------------------------- + /** + * Compares the value of this field in two temporal objects. + *

    + * All fields implement {@link Comparator} on {@link TemporalAccessor}. + * This allows a list of date-times to be compared using the value of a field. + * For example, you could sort a list of arbitrary temporal objects by the value of + * the month-of-year field - {@code Collections.sort(list, MONTH_OF_YEAR)} + *

    + * The default implementation must behave equivalent to this code: + *

    +     *  return Long.compare(temporal1.getLong(this), temporal2.getLong(this));
    +     * 
    + * + * @param temporal1 the first temporal object to compare, not null + * @param temporal2 the second temporal object to compare, not null + * @throws DateTimeException if unable to obtain the value for this field + */ + public default int compare(TemporalAccessor temporal1, TemporalAccessor temporal2) { + return Long.compare(temporal1.getLong(this), temporal2.getLong(this)); + } + + /** + * Gets the range of valid values for the field. + *

    + * All fields can be expressed as a {@code long} integer. + * This method returns an object that describes the valid range for that value. + * This method is generally only applicable to the ISO-8601 calendar system. + *

    + * Note that the result only describes the minimum and maximum valid values + * and it is important not to read too much into them. For example, there + * could be values within the range that are invalid for the field. + * + * @return the range of valid values for the field, not null + */ + ValueRange range(); + + //----------------------------------------------------------------------- + /** + * Checks if this field is supported by the temporal object. + *

    + * This determines whether the temporal accessor supports this field. + * If this returns false, the the temporal cannot be queried for this field. + *

    + * There are two equivalent ways of using this method. + * The first is to invoke this method directly. + * The second is to use {@link TemporalAccessor#isSupported(TemporalField)}: + *

    +     *   // these two lines are equivalent, but the second approach is recommended
    +     *   temporal = thisField.doIsSupported(temporal);
    +     *   temporal = temporal.isSupported(thisField);
    +     * 
    + * It is recommended to use the second approach, {@code isSupported(TemporalField)}, + * as it is a lot clearer to read in code. + *

    + * Implementations should determine whether they are supported using the fields + * available in {@link ChronoField}. + * + * @param temporal the temporal object to query, not null + * @return true if the date-time can be queried for this field, false if not + */ + boolean doIsSupported(TemporalAccessor temporal); + + /** + * Get the range of valid values for this field using the temporal object to + * refine the result. + *

    + * This uses the temporal object to find the range of valid values for the field. + * This is similar to {@link #range()}, however this method refines the result + * using the temporal. For example, if the field is {@code DAY_OF_MONTH} the + * {@code range} method is not accurate as there are four possible month lengths, + * 28, 29, 30 and 31 days. Using this method with a date allows the range to be + * accurate, returning just one of those four options. + *

    + * There are two equivalent ways of using this method. + * The first is to invoke this method directly. + * The second is to use {@link TemporalAccessor#range(TemporalField)}: + *

    +     *   // these two lines are equivalent, but the second approach is recommended
    +     *   temporal = thisField.doRange(temporal);
    +     *   temporal = temporal.range(thisField);
    +     * 
    + * It is recommended to use the second approach, {@code range(TemporalField)}, + * as it is a lot clearer to read in code. + *

    + * Implementations should perform any queries or calculations using the fields + * available in {@link ChronoField}. + * If the field is not supported a {@code DateTimeException} must be thrown. + * + * @param temporal the temporal object used to refine the result, not null + * @return the range of valid values for this field, not null + * @throws DateTimeException if the range for the field cannot be obtained + */ + ValueRange doRange(TemporalAccessor temporal); + + /** + * Gets the value of this field from the specified temporal object. + *

    + * This queries the temporal object for the value of this field. + *

    + * There are two equivalent ways of using this method. + * The first is to invoke this method directly. + * The second is to use {@link TemporalAccessor#getLong(TemporalField)} + * (or {@link TemporalAccessor#get(TemporalField)}): + *

    +     *   // these two lines are equivalent, but the second approach is recommended
    +     *   temporal = thisField.doGet(temporal);
    +     *   temporal = temporal.getLong(thisField);
    +     * 
    + * It is recommended to use the second approach, {@code getLong(TemporalField)}, + * as it is a lot clearer to read in code. + *

    + * Implementations should perform any queries or calculations using the fields + * available in {@link ChronoField}. + * If the field is not supported a {@code DateTimeException} must be thrown. + * + * @param temporal the temporal object to query, not null + * @return the value of this field, not null + * @throws DateTimeException if a value for the field cannot be obtained + */ + long doGet(TemporalAccessor temporal); + + /** + * Returns a copy of the specified temporal object with the value of this field set. + *

    + * This returns a new temporal object based on the specified one with the value for + * this field changed. For example, on a {@code LocalDate}, this could be used to + * set the year, month or day-of-month. + * The returned object has the same observable type as the specified object. + *

    + * In some cases, changing a field is not fully defined. For example, if the target object is + * a date representing the 31st January, then changing the month to February would be unclear. + * In cases like this, the implementation is responsible for resolving the result. + * Typically it will choose the previous valid date, which would be the last valid + * day of February in this example. + *

    + * There are two equivalent ways of using this method. + * The first is to invoke this method directly. + * The second is to use {@link Temporal#with(TemporalField, long)}: + *

    +     *   // these two lines are equivalent, but the second approach is recommended
    +     *   temporal = thisField.doWith(temporal);
    +     *   temporal = temporal.with(thisField);
    +     * 
    + * It is recommended to use the second approach, {@code with(TemporalField)}, + * as it is a lot clearer to read in code. + *

    + * Implementations should perform any queries or calculations using the fields + * available in {@link ChronoField}. + * If the field is not supported a {@code DateTimeException} must be thrown. + *

    + * Implementations must not alter the specified temporal object. + * Instead, an adjusted copy of the original must be returned. + * This provides equivalent, safe behavior for immutable and mutable implementations. + * + * @param the type of the Temporal object + * @param temporal the temporal object to adjust, not null + * @param newValue the new value of the field + * @return the adjusted temporal object, not null + * @throws DateTimeException if the field cannot be set + */ + R doWith(R temporal, long newValue); + + /** + * Resolves the date/time information in the builder + *

    + * This method is invoked during the resolve of the builder. + * Implementations should combine the associated field with others to form + * objects like {@code LocalDate}, {@code LocalTime} and {@code LocalDateTime} + * + * @param builder the builder to resolve, not null + * @param value the value of the associated field + * @return true if builder has been changed, false otherwise + * @throws DateTimeException if unable to resolve + */ + boolean resolve(DateTimeBuilder builder, long value); + +} diff --git a/jdk/src/share/classes/java/time/temporal/TemporalQuery.java b/jdk/src/share/classes/java/time/temporal/TemporalQuery.java new file mode 100644 index 00000000000..a31964567b4 --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/TemporalQuery.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import java.time.DateTimeException; + +/** + * Strategy for querying a temporal object. + *

    + * Queries are a key tool for extracting information from temporal objects. + * They exist to externalize the process of querying, permitting different + * approaches, as per the strategy design pattern. + * Examples might be a query that checks if the date is the day before February 29th + * in a leap year, or calculates the number of days to your next birthday. + *

    + * The {@link TemporalField} interface provides another mechanism for querying + * temporal objects. That interface is limited to returning a {@code long}. + * By contrast, queries can return any type. + *

    + * There are two equivalent ways of using a {@code TemporalQuery}. + * The first is to invoke the method on this interface directly. + * The second is to use {@link TemporalAccessor#query(TemporalQuery)}: + *

    + *   // these two lines are equivalent, but the second approach is recommended
    + *   temporal = thisQuery.queryFrom(temporal);
    + *   temporal = temporal.query(thisQuery);
    + * 
    + * It is recommended to use the second approach, {@code query(TemporalQuery)}, + * as it is a lot clearer to read in code. + *

    + * The most common implementations are method references, such as + * {@code LocalDate::from} and {@code ZoneId::from}. + * Further implementations are on {@link Queries}. + * Queries may also be defined by applications. + * + *

    Specification for implementors

    + * This interface places no restrictions on the mutability of implementations, + * however immutability is strongly recommended. + * + * @since 1.8 + */ +public interface TemporalQuery { + + /** + * Queries the specified temporal object. + *

    + * This queries the specified temporal object to return an object using the logic + * encapsulated in the implementing class. + * Examples might be a query that checks if the date is the day before February 29th + * in a leap year, or calculates the number of days to your next birthday. + *

    + * There are two equivalent ways of using this method. + * The first is to invoke this method directly. + * The second is to use {@link TemporalAccessor#query(TemporalQuery)}: + *

    +     *   // these two lines are equivalent, but the second approach is recommended
    +     *   temporal = thisQuery.queryFrom(temporal);
    +     *   temporal = temporal.query(thisQuery);
    +     * 
    + * It is recommended to use the second approach, {@code query(TemporalQuery)}, + * as it is a lot clearer to read in code. + * + *

    Specification for implementors

    + * The implementation must take the input object and query it. + * The implementation defines the logic of the query and is responsible for + * documenting that logic. + * It may use any method on {@code TemporalAccessor} to determine the result. + * The input object must not be altered. + *

    + * The input temporal object may be in a calendar system other than ISO. + * Implementations may choose to document compatibility with other calendar systems, + * or reject non-ISO temporal objects by {@link Queries#chrono() querying the chronology}. + *

    + * This method may be called from multiple threads in parallel. + * It must be thread-safe when invoked. + * + * @param temporal the temporal object to query, not null + * @return the queried value, may return null to indicate not found + * @throws DateTimeException if unable to query + * @throws ArithmeticException if numeric overflow occurs + */ + R queryFrom(TemporalAccessor temporal); + +} diff --git a/jdk/src/share/classes/java/time/temporal/TemporalSubtractor.java b/jdk/src/share/classes/java/time/temporal/TemporalSubtractor.java new file mode 100644 index 00000000000..4da8f30bea4 --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/TemporalSubtractor.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import java.time.DateTimeException; +import java.time.Duration; +import java.time.Period; + +/** + * Strategy for subtracting from a temporal object. + *

    + * Subtractors are a key tool for modifying temporal objects. + * They exist to externalize the process of subtraction, permitting different + * approaches, as per the strategy design pattern. + *

    + * There are two equivalent ways of using a {@code TemporalSubtractor}. + * The first is to invoke the method on this interface directly. + * The second is to use {@link Temporal#minus(TemporalSubtractor)}: + *

    + *   // these two lines are equivalent, but the second approach is recommended
    + *   dateTime = subtractor.subtractFrom(dateTime);
    + *   dateTime = dateTime.minus(subtractor);
    + * 
    + * It is recommended to use the second approach, {@code minus(TemporalSubtractor)}, + * as it is a lot clearer to read in code. + *

    + * The {@link Period} and {@link Duration} classes implement this interface. + * Subtractors may also be defined by applications. + * + *

    Specification for implementors

    + * This interface places no restrictions on the mutability of implementations, + * however immutability is strongly recommended. + * + * @since 1.8 + */ +public interface TemporalSubtractor { + + /** + * Subtracts this object from the specified temporal object. + *

    + * This adds to the specified temporal object using the logic + * encapsulated in the implementing class. + *

    + * There are two equivalent ways of using this method. + * The first is to invoke this method directly. + * The second is to use {@link Temporal#minus(TemporalSubtractor)}: + *

    +     *   // these two lines are equivalent, but the second approach is recommended
    +     *   dateTime = subtractor.subtractFrom(dateTime);
    +     *   dateTime = dateTime.minus(subtractor);
    +     * 
    + * It is recommended to use the second approach, {@code minus(TemporalSubtractor)}, + * as it is a lot clearer to read in code. + * + *

    Specification for implementors

    + * The implementation must take the input object and subtract from it. + * The implementation defines the logic of the subtraction and is responsible for + * documenting that logic. It may use any method on {@code Temporal} to + * query the temporal object and perform the subtraction. + * The returned object must have the same observable type as the input object + *

    + * The input object must not be altered. + * Instead, an adjusted copy of the original must be returned. + * This provides equivalent, safe behavior for immutable and mutable temporal objects. + *

    + * The input temporal object may be in a calendar system other than ISO. + * Implementations may choose to document compatibility with other calendar systems, + * or reject non-ISO temporal objects by {@link Queries#chrono() querying the chronology}. + *

    + * This method may be called from multiple threads in parallel. + * It must be thread-safe when invoked. + * + * @param temporal the temporal object to adjust, not null + * @return an object of the same observable type with the subtraction made, not null + * @throws DateTimeException if unable to subtract + * @throws ArithmeticException if numeric overflow occurs + */ + Temporal subtractFrom(Temporal temporal); + +} diff --git a/jdk/src/share/classes/java/time/temporal/TemporalUnit.java b/jdk/src/share/classes/java/time/temporal/TemporalUnit.java new file mode 100644 index 00000000000..7c8a32b4a3d --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/TemporalUnit.java @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import java.time.DateTimeException; +import java.time.Duration; +import java.time.Period; + +/** + * A unit of date-time, such as Days or Hours. + *

    + * Measurement of time is built on units, such as years, months, days, hours, minutes and seconds. + * Implementations of this interface represent those units. + *

    + * An instance of this interface represents the unit itself, rather than an amount of the unit. + * See {@link Period} for a class that represents an amount in terms of the common units. + *

    + * The most commonly used units are defined in {@link ChronoUnit}. + * Further units are supplied in {@link ISOFields}. + * Units can also be written by application code by implementing this interface. + *

    + * The unit works using double dispatch. Client code calls methods on a date-time like + * {@code LocalDateTime} which check if the unit is a {@code ChronoUnit}. + * If it is, then the date-time must handle it. + * Otherwise, the method call is re-dispatched to the matching method in this interface. + * + *

    Specification for implementors

    + * This interface must be implemented with care to ensure other classes operate correctly. + * All implementations that can be instantiated must be final, immutable and thread-safe. + * It is recommended to use an enum where possible. + * + * @since 1.8 + */ +public interface TemporalUnit { + + /** + * Gets a descriptive name for the unit. + *

    + * This should be in the plural and upper-first camel case, such as 'Days' or 'Minutes'. + * + * @return the name, not null + */ + String getName(); + + /** + * Gets the duration of this unit, which may be an estimate. + *

    + * All units return a duration measured in standard nanoseconds from this method. + * For example, an hour has a duration of {@code 60 * 60 * 1,000,000,000ns}. + *

    + * Some units may return an accurate duration while others return an estimate. + * For example, days have an estimated duration due to the possibility of + * daylight saving time changes. + * To determine if the duration is an estimate, use {@link #isDurationEstimated()}. + * + * @return the duration of this unit, which may be an estimate, not null + */ + Duration getDuration(); + + /** + * Checks if the duration of the unit is an estimate. + *

    + * All units have a duration, however the duration is not always accurate. + * For example, days have an estimated duration due to the possibility of + * daylight saving time changes. + * This method returns true if the duration is an estimate and false if it is + * accurate. Note that accurate/estimated ignores leap seconds. + * + * @return true if the duration is estimated, false if accurate + */ + boolean isDurationEstimated(); + + //----------------------------------------------------------------------- + /** + * Checks if this unit is supported by the specified temporal object. + *

    + * This checks that the implementing date-time can add/subtract this unit. + * This can be used to avoid throwing an exception. + *

    + * This default implementation derives the value using + * {@link Temporal#plus(long, TemporalUnit)}. + * + * @param temporal the temporal object to check, not null + * @return true if the unit is supported + */ + public default boolean isSupported(Temporal temporal) { + try { + temporal.plus(1, this); + return true; + } catch (RuntimeException ex) { + try { + temporal.plus(-1, this); + return true; + } catch (RuntimeException ex2) { + return false; + } + } + } + + /** + * Returns a copy of the specified temporal object with the specified period added. + *

    + * The period added is a multiple of this unit. For example, this method + * could be used to add "3 days" to a date by calling this method on the + * instance representing "days", passing the date and the period "3". + * The period to be added may be negative, which is equivalent to subtraction. + *

    + * There are two equivalent ways of using this method. + * The first is to invoke this method directly. + * The second is to use {@link Temporal#plus(long, TemporalUnit)}: + *

    +     *   // these two lines are equivalent, but the second approach is recommended
    +     *   temporal = thisUnit.doPlus(temporal);
    +     *   temporal = temporal.plus(thisUnit);
    +     * 
    + * It is recommended to use the second approach, {@code plus(TemporalUnit)}, + * as it is a lot clearer to read in code. + *

    + * Implementations should perform any queries or calculations using the units + * available in {@link ChronoUnit} or the fields available in {@link ChronoField}. + * If the field is not supported a {@code DateTimeException} must be thrown. + *

    + * Implementations must not alter the specified temporal object. + * Instead, an adjusted copy of the original must be returned. + * This provides equivalent, safe behavior for immutable and mutable implementations. + * + * @param the type of the Temporal object + * @param dateTime the temporal object to adjust, not null + * @param periodToAdd the period of this unit to add, positive or negative + * @return the adjusted temporal object, not null + * @throws DateTimeException if the period cannot be added + */ + R doPlus(R dateTime, long periodToAdd); + + //----------------------------------------------------------------------- + /** + * Calculates the period in terms of this unit between two temporal objects of the same type. + *

    + * The period will be positive if the second date-time is after the first, and + * negative if the second date-time is before the first. + * Call {@link SimplePeriod#abs() abs()} on the result to ensure that the result + * is always positive. + *

    + * The result can be queried for the {@link SimplePeriod#getAmount() amount}, the + * {@link SimplePeriod#getUnit() unit} and used directly in addition/subtraction: + *

    +     *  date = date.minus(MONTHS.between(start, end));
    +     * 
    + * + * @param the type of the Temporal object; the two date-times must be of the same type + * @param dateTime1 the base temporal object, not null + * @param dateTime2 the other temporal object, not null + * @return the period between datetime1 and datetime2 in terms of this unit; + * positive if datetime2 is later than datetime1, not null + */ + SimplePeriod between(R dateTime1, R dateTime2); + + //----------------------------------------------------------------------- + /** + * Outputs this unit as a {@code String} using the name. + * + * @return the name of this unit, not null + */ + @Override + String toString(); + +} diff --git a/jdk/src/share/classes/java/time/temporal/ValueRange.java b/jdk/src/share/classes/java/time/temporal/ValueRange.java new file mode 100644 index 00000000000..cd47f256d4d --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/ValueRange.java @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2011-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import java.io.Serializable; +import java.time.DateTimeException; + +/** + * The range of valid values for a date-time field. + *

    + * All {@link TemporalField} instances have a valid range of values. + * For example, the ISO day-of-month runs from 1 to somewhere between 28 and 31. + * This class captures that valid range. + *

    + * It is important to be aware of the limitations of this class. + * Only the minimum and maximum values are provided. + * It is possible for there to be invalid values within the outer range. + * For example, a weird field may have valid values of 1, 2, 4, 6, 7, thus + * have a range of '1 - 7', despite that fact that values 3 and 5 are invalid. + *

    + * Instances of this class are not tied to a specific field. + * + *

    Specification for implementors

    + * This class is immutable and thread-safe. + * + * @since 1.8 + */ +public final class ValueRange implements Serializable { + + /** + * Serialization version. + */ + private static final long serialVersionUID = -7317881728594519368L; + + /** + * The smallest minimum value. + */ + private final long minSmallest; + /** + * The largest minimum value. + */ + private final long minLargest; + /** + * The smallest maximum value. + */ + private final long maxSmallest; + /** + * The largest maximum value. + */ + private final long maxLargest; + + /** + * Obtains a fixed value range. + *

    + * This factory obtains a range where the minimum and maximum values are fixed. + * For example, the ISO month-of-year always runs from 1 to 12. + * + * @param min the minimum value + * @param max the maximum value + * @return the ValueRange for min, max, not null + * @throws IllegalArgumentException if the minimum is greater than the maximum + */ + public static ValueRange of(long min, long max) { + if (min > max) { + throw new IllegalArgumentException("Minimum value must be less than maximum value"); + } + return new ValueRange(min, min, max, max); + } + + /** + * Obtains a variable value range. + *

    + * This factory obtains a range where the minimum value is fixed and the maximum value may vary. + * For example, the ISO day-of-month always starts at 1, but ends between 28 and 31. + * + * @param min the minimum value + * @param maxSmallest the smallest maximum value + * @param maxLargest the largest maximum value + * @return the ValueRange for min, smallest max, largest max, not null + * @throws IllegalArgumentException if + * the minimum is greater than the smallest maximum, + * or the smallest maximum is greater than the largest maximum + */ + public static ValueRange of(long min, long maxSmallest, long maxLargest) { + return of(min, min, maxSmallest, maxLargest); + } + + /** + * Obtains a fully variable value range. + *

    + * This factory obtains a range where both the minimum and maximum value may vary. + * + * @param minSmallest the smallest minimum value + * @param minLargest the largest minimum value + * @param maxSmallest the smallest maximum value + * @param maxLargest the largest maximum value + * @return the ValueRange for smallest min, largest min, smallest max, largest max, not null + * @throws IllegalArgumentException if + * the smallest minimum is greater than the smallest maximum, + * or the smallest maximum is greater than the largest maximum + * or the largest minimum is greater than the largest maximum + */ + public static ValueRange of(long minSmallest, long minLargest, long maxSmallest, long maxLargest) { + if (minSmallest > minLargest) { + throw new IllegalArgumentException("Smallest minimum value must be less than largest minimum value"); + } + if (maxSmallest > maxLargest) { + throw new IllegalArgumentException("Smallest maximum value must be less than largest maximum value"); + } + if (minLargest > maxLargest) { + throw new IllegalArgumentException("Minimum value must be less than maximum value"); + } + return new ValueRange(minSmallest, minLargest, maxSmallest, maxLargest); + } + + /** + * Restrictive constructor. + * + * @param minSmallest the smallest minimum value + * @param minLargest the largest minimum value + * @param maxSmallest the smallest minimum value + * @param maxLargest the largest minimum value + */ + private ValueRange(long minSmallest, long minLargest, long maxSmallest, long maxLargest) { + this.minSmallest = minSmallest; + this.minLargest = minLargest; + this.maxSmallest = maxSmallest; + this.maxLargest = maxLargest; + } + + //----------------------------------------------------------------------- + /** + * Is the value range fixed and fully known. + *

    + * For example, the ISO day-of-month runs from 1 to between 28 and 31. + * Since there is uncertainty about the maximum value, the range is not fixed. + * However, for the month of January, the range is always 1 to 31, thus it is fixed. + * + * @return true if the set of values is fixed + */ + public boolean isFixed() { + return minSmallest == minLargest && maxSmallest == maxLargest; + } + + //----------------------------------------------------------------------- + /** + * Gets the minimum value that the field can take. + *

    + * For example, the ISO day-of-month always starts at 1. + * The minimum is therefore 1. + * + * @return the minimum value for this field + */ + public long getMinimum() { + return minSmallest; + } + + /** + * Gets the largest possible minimum value that the field can take. + *

    + * For example, the ISO day-of-month always starts at 1. + * The largest minimum is therefore 1. + * + * @return the largest possible minimum value for this field + */ + public long getLargestMinimum() { + return minLargest; + } + + /** + * Gets the smallest possible maximum value that the field can take. + *

    + * For example, the ISO day-of-month runs to between 28 and 31 days. + * The smallest maximum is therefore 28. + * + * @return the smallest possible maximum value for this field + */ + public long getSmallestMaximum() { + return maxSmallest; + } + + /** + * Gets the maximum value that the field can take. + *

    + * For example, the ISO day-of-month runs to between 28 and 31 days. + * The maximum is therefore 31. + * + * @return the maximum value for this field + */ + public long getMaximum() { + return maxLargest; + } + + //----------------------------------------------------------------------- + /** + * Checks if all values in the range fit in an {@code int}. + *

    + * This checks that all valid values are within the bounds of an {@code int}. + *

    + * For example, the ISO month-of-year has values from 1 to 12, which fits in an {@code int}. + * By comparison, ISO nano-of-day runs from 1 to 86,400,000,000,000 which does not fit in an {@code int}. + *

    + * This implementation uses {@link #getMinimum()} and {@link #getMaximum()}. + * + * @return true if a valid value always fits in an {@code int} + */ + public boolean isIntValue() { + return getMinimum() >= Integer.MIN_VALUE && getMaximum() <= Integer.MAX_VALUE; + } + + /** + * Checks if the value is within the valid range. + *

    + * This checks that the value is within the stored range of values. + * + * @param value the value to check + * @return true if the value is valid + */ + public boolean isValidValue(long value) { + return (value >= getMinimum() && value <= getMaximum()); + } + + /** + * Checks if the value is within the valid range and that all values + * in the range fit in an {@code int}. + *

    + * This method combines {@link #isIntValue()} and {@link #isValidValue(long)}. + * + * @param value the value to check + * @return true if the value is valid and fits in an {@code int} + */ + public boolean isValidIntValue(long value) { + return isIntValue() && isValidValue(value); + } + + /** + * Checks that the specified value is valid. + *

    + * This validates that the value is within the valid range of values. + * The field is only used to improve the error message. + * + * @param value the value to check + * @param field the field being checked, may be null + * @return the value that was passed in + * @see #isValidValue(long) + */ + public long checkValidValue(long value, TemporalField field) { + if (isValidValue(value) == false) { + if (field != null) { + throw new DateTimeException("Invalid value for " + field.getName() + " (valid values " + this + "): " + value); + } else { + throw new DateTimeException("Invalid value (valid values " + this + "): " + value); + } + } + return value; + } + + /** + * Checks that the specified value is valid and fits in an {@code int}. + *

    + * This validates that the value is within the valid range of values and that + * all valid values are within the bounds of an {@code int}. + * The field is only used to improve the error message. + * + * @param value the value to check + * @param field the field being checked, may be null + * @return the value that was passed in + * @see #isValidIntValue(long) + */ + public int checkValidIntValue(long value, TemporalField field) { + if (isValidIntValue(value) == false) { + throw new DateTimeException("Invalid int value for " + field.getName() + ": " + value); + } + return (int) value; + } + + //----------------------------------------------------------------------- + /** + * Checks if this range is equal to another range. + *

    + * The comparison is based on the four values, minimum, largest minimum, + * smallest maximum and maximum. + * Only objects of type {@code ValueRange} are compared, other types return false. + * + * @param obj the object to check, null returns false + * @return true if this is equal to the other range + */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof ValueRange) { + ValueRange other = (ValueRange) obj; + return minSmallest == other.minSmallest && minLargest == other.minLargest && + maxSmallest == other.maxSmallest && maxLargest == other.maxLargest; + } + return false; + } + + /** + * A hash code for this range. + * + * @return a suitable hash code + */ + @Override + public int hashCode() { + long hash = minSmallest + minLargest << 16 + minLargest >> 48 + maxSmallest << 32 + + maxSmallest >> 32 + maxLargest << 48 + maxLargest >> 16; + return (int) (hash ^ (hash >>> 32)); + } + + //----------------------------------------------------------------------- + /** + * Outputs this range as a {@code String}. + *

    + * The format will be '{min}/{largestMin} - {smallestMax}/{max}', + * where the largestMin or smallestMax sections may be omitted, together + * with associated slash, if they are the same as the min or max. + * + * @return a string representation of this range, not null + */ + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(minSmallest); + if (minSmallest != minLargest) { + buf.append('/').append(minLargest); + } + buf.append(" - ").append(maxSmallest); + if (maxSmallest != maxLargest) { + buf.append('/').append(maxLargest); + } + return buf.toString(); + } + +} diff --git a/jdk/src/share/classes/java/time/temporal/WeekFields.java b/jdk/src/share/classes/java/time/temporal/WeekFields.java new file mode 100644 index 00000000000..364aa91798e --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/WeekFields.java @@ -0,0 +1,663 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2011-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import java.io.InvalidObjectException; +import java.io.Serializable; +import java.time.DayOfWeek; +import java.time.format.DateTimeBuilder; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * Localized definitions of the day-of-week, week-of-month and week-of-year fields. + *

    + * A standard week is seven days long, but cultures have different definitions for some + * other aspects of a week. This class represents the definition of the week, for the + * purpose of providing {@link TemporalField} instances. + *

    + * WeekFields provides three fields, + * {@link #dayOfWeek()}, {@link #weekOfMonth()}, and {@link #weekOfYear()} + * that provide access to the values from any {@linkplain Temporal temporal object}. + *

    + * The computations for day-of-week, week-of-month, and week-of-year are based + * on the {@linkplain ChronoField#YEAR proleptic-year}, + * {@linkplain ChronoField#MONTH_OF_YEAR month-of-year}, + * {@linkplain ChronoField#DAY_OF_MONTH day-of-month}, and + * {@linkplain ChronoField#DAY_OF_WEEK ISO day-of-week} which are based on the + * {@linkplain ChronoField#EPOCH_DAY epoch-day} and the chronology. + * The values may not be aligned with the {@linkplain ChronoField#YEAR_OF_ERA year-of-Era} + * depending on the Chronology. + *

    A week is defined by: + *

      + *
    • The first day-of-week. + * For example, the ISO-8601 standard considers Monday to be the first day-of-week. + *
    • The minimal number of days in the first week. + * For example, the ISO-08601 standard counts the first week as needing at least 4 days. + *

    + * Together these two values allow a year or month to be divided into weeks. + *

    + *

    Week of Month

    + * One field is used: week-of-month. + * The calculation ensures that weeks never overlap a month boundary. + * The month is divided into periods where each period starts on the defined first day-of-week. + * The earliest period is referred to as week 0 if it has less than the minimal number of days + * and week 1 if it has at least the minimal number of days. + *

    + * + * + * + * + * + * + * + * + * + * + * + * + *
    Examples of WeekFields
    DateDay-of-weekFirst day: Monday
    Minimal days: 4
    First day: Monday
    Minimal days: 5
    2008-12-31WednesdayWeek 5 of December 2008Week 5 of December 2008
    2009-01-01ThursdayWeek 1 of January 2009Week 0 of January 2009
    2009-01-04SundayWeek 1 of January 2009Week 0 of January 2009
    2009-01-05MondayWeek 2 of January 2009Week 1 of January 2009
    + *

    + *

    Week of Year

    + * One field is used: week-of-year. + * The calculation ensures that weeks never overlap a year boundary. + * The year is divided into periods where each period starts on the defined first day-of-week. + * The earliest period is referred to as week 0 if it has less than the minimal number of days + * and week 1 if it has at least the minimal number of days. + *

    + * This class is immutable and thread-safe. + * + * @since 1.8 + */ +public final class WeekFields implements Serializable { + // implementation notes + // querying week-of-month or week-of-year should return the week value bound within the month/year + // however, setting the week value should be lenient (use plus/minus weeks) + // allow week-of-month outer range [0 to 5] + // allow week-of-year outer range [0 to 53] + // this is because callers shouldn't be expected to know the details of validity + + /** + * The cache of rules by firstDayOfWeek plus minimalDays. + * Initialized first to be available for definition of ISO, etc. + */ + private static final ConcurrentMap CACHE = new ConcurrentHashMap<>(4, 0.75f, 2); + + /** + * The ISO-8601 definition, where a week starts on Monday and the first week + * has a minimum of 4 days. + *

    + * The ISO-8601 standard defines a calendar system based on weeks. + * It uses the week-based-year and week-of-week-based-year concepts to split + * up the passage of days instead of the standard year/month/day. + *

    + * Note that the first week may start in the previous calendar year. + * Note also that the first few days of a calendar year may be in the + * week-based-year corresponding to the previous calendar year. + */ + public static final WeekFields ISO = new WeekFields(DayOfWeek.MONDAY, 4); + + /** + * The common definition of a week that starts on Sunday. + *

    + * Defined as starting on Sunday and with a minimum of 1 day in the month. + * This week definition is in use in the US and other European countries. + * + */ + public static final WeekFields SUNDAY_START = WeekFields.of(DayOfWeek.SUNDAY, 1); + + /** + * Serialization version. + */ + private static final long serialVersionUID = -1177360819670808121L; + + /** + * The first day-of-week. + */ + private final DayOfWeek firstDayOfWeek; + /** + * The minimal number of days in the first week. + */ + private final int minimalDays; + + /** + * The field used to access the computed DayOfWeek. + */ + private transient final TemporalField dayOfWeek = ComputedDayOfField.ofDayOfWeekField(this); + + /** + * The field used to access the computed WeekOfMonth. + */ + private transient final TemporalField weekOfMonth = ComputedDayOfField.ofWeekOfMonthField(this); + + /** + * The field used to access the computed WeekOfYear. + */ + private transient final TemporalField weekOfYear = ComputedDayOfField.ofWeekOfYearField(this); + + /** + * Obtains an instance of {@code WeekFields} appropriate for a locale. + *

    + * This will look up appropriate values from the provider of localization data. + * + * @param locale the locale to use, not null + * @return the week-definition, not null + */ + public static WeekFields of(Locale locale) { + Objects.requireNonNull(locale, "locale"); + locale = new Locale(locale.getLanguage(), locale.getCountry()); // elminate variants + + // obtain these from GregorianCalendar for now + // TODO: consider getting them directly from the spi + GregorianCalendar gcal = new GregorianCalendar(locale); + int calDow = gcal.getFirstDayOfWeek(); + DayOfWeek dow = DayOfWeek.SUNDAY.plus(calDow - 1); + int minDays = gcal.getMinimalDaysInFirstWeek(); + return WeekFields.of(dow, minDays); + } + + /** + * Obtains an instance of {@code WeekFields} from the first day-of-week and minimal days. + *

    + * The first day-of-week defines the ISO {@code DayOfWeek} that is day 1 of the week. + * The minimal number of days in the first week defines how many days must be present + * in a month or year, starting from the first day-of-week, before the week is counted + * as the first week. A value of 1 will count the first day of the month or year as part + * of the first week, whereas a value of 7 will require the whole seven days to be in + * the new month or year. + *

    + * WeekFields instances are singletons; for each unique combination + * of {@code firstDayOfWeek} and {@code minimalDaysInFirstWeek} the + * the same instance will be returned. + * + * @param firstDayOfWeek the first day of the week, not null + * @param minimalDaysInFirstWeek the minimal number of days in the first week, from 1 to 7 + * @return the week-definition, not null + * @throws IllegalArgumentException if the minimal days value is less than one + * or greater than 7 + */ + public static WeekFields of(DayOfWeek firstDayOfWeek, int minimalDaysInFirstWeek) { + String key = firstDayOfWeek.toString() + minimalDaysInFirstWeek; + WeekFields rules = CACHE.get(key); + if (rules == null) { + rules = new WeekFields(firstDayOfWeek, minimalDaysInFirstWeek); + CACHE.putIfAbsent(key, rules); + rules = CACHE.get(key); + } + return rules; + } + + //----------------------------------------------------------------------- + /** + * Creates an instance of the definition. + * + * @param firstDayOfWeek the first day of the week, not null + * @param minimalDaysInFirstWeek the minimal number of days in the first week, from 1 to 7 + * @throws IllegalArgumentException if the minimal days value is invalid + */ + private WeekFields(DayOfWeek firstDayOfWeek, int minimalDaysInFirstWeek) { + Objects.requireNonNull(firstDayOfWeek, "firstDayOfWeek"); + if (minimalDaysInFirstWeek < 1 || minimalDaysInFirstWeek > 7) { + throw new IllegalArgumentException("Minimal number of days is invalid"); + } + this.firstDayOfWeek = firstDayOfWeek; + this.minimalDays = minimalDaysInFirstWeek; + } + + //----------------------------------------------------------------------- + /** + * Return the singleton WeekFields associated with the + * {@code firstDayOfWeek} and {@code minimalDays}. + * @return the singleton WeekFields for the firstDayOfWeek and minimalDays. + * @throws InvalidObjectException if the serialized object has invalid + * values for firstDayOfWeek or minimalDays. + */ + private Object readResolve() throws InvalidObjectException { + try { + return WeekFields.of(firstDayOfWeek, minimalDays); + } catch (IllegalArgumentException iae) { + throw new InvalidObjectException("Invalid serialized WeekFields: " + + iae.getMessage()); + } + } + + //----------------------------------------------------------------------- + /** + * Gets the first day-of-week. + *

    + * The first day-of-week varies by culture. + * For example, the US uses Sunday, while France and the ISO-8601 standard use Monday. + * This method returns the first day using the standard {@code DayOfWeek} enum. + * + * @return the first day-of-week, not null + */ + public DayOfWeek getFirstDayOfWeek() { + return firstDayOfWeek; + } + + /** + * Gets the minimal number of days in the first week. + *

    + * The number of days considered to define the first week of a month or year + * varies by culture. + * For example, the ISO-8601 requires 4 days (more than half a week) to + * be present before counting the first week. + * + * @return the minimal number of days in the first week of a month or year, from 1 to 7 + */ + public int getMinimalDaysInFirstWeek() { + return minimalDays; + } + + //----------------------------------------------------------------------- + /** + * Returns a field to access the day of week, + * computed based on this WeekFields. + *

    + * The days of week are numbered from 1 to 7. + * Day number 1 is the {@link #getFirstDayOfWeek() first day-of-week}. + * + * @return the field for day-of-week using this week definition, not null + */ + public TemporalField dayOfWeek() { + return dayOfWeek; + } + + /** + * Returns a field to access the week of month, + * computed based on this WeekFields. + *

    + * This represents concept of the count of weeks within the month where weeks + * start on a fixed day-of-week, such as Monday. + * This field is typically used with {@link WeekFields#dayOfWeek()}. + *

    + * Week one (1) is the week starting on the {@link WeekFields#getFirstDayOfWeek} + * where there are at least {@link WeekFields#getMinimalDaysInFirstWeek()} days in the month. + * Thus, week one may start up to {@code minDays} days before the start of the month. + * If the first week starts after the start of the month then the period before is week zero (0). + *

    + * For example:
    + * - if the 1st day of the month is a Monday, week one starts on the 1st and there is no week zero
    + * - if the 2nd day of the month is a Monday, week one starts on the 2nd and the 1st is in week zero
    + * - if the 4th day of the month is a Monday, week one starts on the 4th and the 1st to 3rd is in week zero
    + * - if the 5th day of the month is a Monday, week two starts on the 5th and the 1st to 4th is in week one
    + *

    + * This field can be used with any calendar system. + * @return a TemporalField to access the WeekOfMonth, not null + */ + public TemporalField weekOfMonth() { + return weekOfMonth; + } + + /** + * Returns a field to access the week of year, + * computed based on this WeekFields. + *

    + * This represents concept of the count of weeks within the year where weeks + * start on a fixed day-of-week, such as Monday. + * This field is typically used with {@link WeekFields#dayOfWeek()}. + *

    + * Week one(1) is the week starting on the {@link WeekFields#getFirstDayOfWeek} + * where there are at least {@link WeekFields#getMinimalDaysInFirstWeek()} days in the month. + * Thus, week one may start up to {@code minDays} days before the start of the year. + * If the first week starts after the start of the year then the period before is week zero (0). + *

    + * For example:
    + * - if the 1st day of the year is a Monday, week one starts on the 1st and there is no week zero
    + * - if the 2nd day of the year is a Monday, week one starts on the 2nd and the 1st is in week zero
    + * - if the 4th day of the year is a Monday, week one starts on the 4th and the 1st to 3rd is in week zero
    + * - if the 5th day of the year is a Monday, week two starts on the 5th and the 1st to 4th is in week one
    + *

    + * This field can be used with any calendar system. + * @return a TemporalField to access the WeekOfYear, not null + */ + public TemporalField weekOfYear() { + return weekOfYear; + } + + /** + * Checks if these rules are equal to the specified rules. + *

    + * The comparison is based on the entire state of the rules, which is + * the first day-of-week and minimal days. + * + * @param object the other rules to compare to, null returns false + * @return true if this is equal to the specified rules + */ + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object instanceof WeekFields) { + return hashCode() == object.hashCode(); + } + return false; + } + + /** + * A hash code for these rules. + * + * @return a suitable hash code + */ + @Override + public int hashCode() { + return firstDayOfWeek.ordinal() * 7 + minimalDays; + } + + //----------------------------------------------------------------------- + /** + * A string representation of this definition. + * + * @return the string representation, not null + */ + @Override + public String toString() { + return "WeekFields[" + firstDayOfWeek + ',' + minimalDays + ']'; + } + + //----------------------------------------------------------------------- + /** + * Field type that computes DayOfWeek, WeekOfMonth, and WeekOfYear + * based on a WeekFields. + * A separate Field instance is required for each different WeekFields; + * combination of start of week and minimum number of days. + * Constructors are provided to create fields for DayOfWeek, WeekOfMonth, + * and WeekOfYear. + */ + static class ComputedDayOfField implements TemporalField { + + /** + * Returns a field to access the day of week, + * computed based on a WeekFields. + *

    + * The WeekDefintion of the first day of the week is used with + * the ISO DAY_OF_WEEK field to compute week boundaries. + */ + static ComputedDayOfField ofDayOfWeekField(WeekFields weekDef) { + return new ComputedDayOfField("DayOfWeek", weekDef, + ChronoUnit.DAYS, ChronoUnit.WEEKS, DAY_OF_WEEK_RANGE); + } + + /** + * Returns a field to access the week of month, + * computed based on a WeekFields. + * @see WeekFields#weekOfMonth() + */ + static ComputedDayOfField ofWeekOfMonthField(WeekFields weekDef) { + return new ComputedDayOfField("WeekOfMonth", weekDef, + ChronoUnit.WEEKS, ChronoUnit.MONTHS, WEEK_OF_MONTH_RANGE); + } + + /** + * Returns a field to access the week of year, + * computed based on a WeekFields. + * @see WeekFields#weekOfYear() + */ + static ComputedDayOfField ofWeekOfYearField(WeekFields weekDef) { + return new ComputedDayOfField("WeekOfYear", weekDef, + ChronoUnit.WEEKS, ChronoUnit.YEARS, WEEK_OF_YEAR_RANGE); + } + private final String name; + private final WeekFields weekDef; + private final TemporalUnit baseUnit; + private final TemporalUnit rangeUnit; + private final ValueRange range; + + private ComputedDayOfField(String name, WeekFields weekDef, TemporalUnit baseUnit, TemporalUnit rangeUnit, ValueRange range) { + this.name = name; + this.weekDef = weekDef; + this.baseUnit = baseUnit; + this.rangeUnit = rangeUnit; + this.range = range; + } + + private static final ValueRange DAY_OF_WEEK_RANGE = ValueRange.of(1, 7); + private static final ValueRange WEEK_OF_MONTH_RANGE = ValueRange.of(0, 1, 4, 5); + private static final ValueRange WEEK_OF_YEAR_RANGE = ValueRange.of(0, 1, 52, 53); + + @Override + public long doGet(TemporalAccessor temporal) { + // Offset the ISO DOW by the start of this week + int sow = weekDef.getFirstDayOfWeek().getValue(); + int isoDow = temporal.get(ChronoField.DAY_OF_WEEK); + int dow = Math.floorMod(isoDow - sow, 7) + 1; + + if (rangeUnit == ChronoUnit.WEEKS) { + return dow; + } else if (rangeUnit == ChronoUnit.MONTHS) { + int dom = temporal.get(ChronoField.DAY_OF_MONTH); + int offset = startOfWeekOffset(dom, dow); + return computeWeek(offset, dom); + } else if (rangeUnit == ChronoUnit.YEARS) { + int doy = temporal.get(ChronoField.DAY_OF_YEAR); + int offset = startOfWeekOffset(doy, dow); + return computeWeek(offset, doy); + } else { + throw new IllegalStateException("unreachable"); + } + } + + /** + * Returns an offset to align week start with a day of month or day of year. + * + * @param day the day; 1 through infinity + * @param dow the day of the week of that day; 1 through 7 + * @return an offset in days to align a day with the start of the first 'full' week + */ + private int startOfWeekOffset(int day, int dow) { + // offset of first day corresponding to the day of week in first 7 days (zero origin) + int weekStart = Math.floorMod(day - dow, 7); + int offset = -weekStart; + if (weekStart + 1 > weekDef.getMinimalDaysInFirstWeek()) { + // The previous week has the minimum days in the current month to be a 'week' + offset = 7 - weekStart; + } + return offset; + } + + /** + * Returns the week number computed from the reference day and reference dayOfWeek. + * + * @param offset the offset to align a date with the start of week + * from {@link #startOfWeekOffset}. + * @param day the day for which to compute the week number + * @return the week number where zero is used for a partial week and 1 for the first full week + */ + private int computeWeek(int offset, int day) { + return ((7 + offset + (day - 1)) / 7); + } + + @Override + public R doWith(R temporal, long newValue) { + // Check the new value and get the old value of the field + int newVal = range.checkValidIntValue(newValue, this); + int currentVal = temporal.get(this); + if (newVal == currentVal) { + return temporal; + } + // Compute the difference and add that using the base using of the field + int delta = newVal - currentVal; + return (R) temporal.plus(delta, baseUnit); + } + + @Override + public boolean resolve(DateTimeBuilder builder, long value) { + int newValue = range.checkValidIntValue(value, this); + // DOW and YEAR are necessary for all fields; Chrono defaults to ISO if not present + int sow = weekDef.getFirstDayOfWeek().getValue(); + int dow = builder.get(weekDef.dayOfWeek()); + int year = builder.get(ChronoField.YEAR); + Chrono chrono = Chrono.from(builder); + + // The WOM and WOY fields are the critical values + if (rangeUnit == ChronoUnit.MONTHS) { + // Process WOM value by combining with DOW and MONTH, YEAR + int month = builder.get(ChronoField.MONTH_OF_YEAR); + ChronoLocalDate cd = chrono.date(year, month, 1); + int offset = startOfWeekOffset(1, cd.get(weekDef.dayOfWeek())); + offset += dow - 1; // offset to desired day of week + offset += 7 * (newValue - 1); // offset by week number + ChronoLocalDate result = cd.plus(offset, ChronoUnit.DAYS); + builder.addFieldValue(ChronoField.DAY_OF_MONTH, result.get(ChronoField.DAY_OF_MONTH)); + builder.removeFieldValue(this); + builder.removeFieldValue(weekDef.dayOfWeek()); + return true; + } else if (rangeUnit == ChronoUnit.YEARS) { + // Process WOY + ChronoLocalDate cd = chrono.date(year, 1, 1); + int offset = startOfWeekOffset(1, cd.get(weekDef.dayOfWeek())); + offset += dow - 1; // offset to desired day of week + offset += 7 * (newValue - 1); // offset by week number + ChronoLocalDate result = cd.plus(offset, ChronoUnit.DAYS); + builder.addFieldValue(ChronoField.DAY_OF_MONTH, result.get(ChronoField.DAY_OF_MONTH)); + builder.addFieldValue(ChronoField.MONTH_OF_YEAR, result.get(ChronoField.MONTH_OF_YEAR)); + builder.removeFieldValue(this); + builder.removeFieldValue(weekDef.dayOfWeek()); + return true; + } else { + // ignore DOW of WEEK field; the value will be processed by WOM or WOY + int isoDow = Math.floorMod((sow - 1) + (dow - 1), 7) + 1; + builder.addFieldValue(ChronoField.DAY_OF_WEEK, isoDow); + // Not removed, the week-of-xxx fields need this value + return true; + } + } + + //----------------------------------------------------------------------- + @Override + public String getName() { + return name; + } + + @Override + public TemporalUnit getBaseUnit() { + return baseUnit; + } + + @Override + public TemporalUnit getRangeUnit() { + return rangeUnit; + } + + @Override + public ValueRange range() { + return range; + } + + //------------------------------------------------------------------------- + @Override + public int compare(TemporalAccessor temporal1, TemporalAccessor temporal2) { + return Long.compare(temporal1.getLong(this), temporal2.getLong(this)); + } + + //----------------------------------------------------------------------- + @Override + public boolean doIsSupported(TemporalAccessor temporal) { + if (temporal.isSupported(ChronoField.DAY_OF_WEEK)) { + if (rangeUnit == ChronoUnit.WEEKS) { + return true; + } else if (rangeUnit == ChronoUnit.MONTHS) { + return temporal.isSupported(ChronoField.DAY_OF_MONTH); + } else if (rangeUnit == ChronoUnit.YEARS) { + return temporal.isSupported(ChronoField.DAY_OF_YEAR); + } + } + return false; + } + + @Override + public ValueRange doRange(TemporalAccessor temporal) { + if (rangeUnit == ChronoUnit.WEEKS) { + return range; + } + + TemporalField field = null; + if (rangeUnit == ChronoUnit.MONTHS) { + field = ChronoField.DAY_OF_MONTH; + } else if (rangeUnit == ChronoUnit.YEARS) { + field = ChronoField.DAY_OF_YEAR; + } else { + throw new IllegalStateException("unreachable"); + } + + // Offset the ISO DOW by the start of this week + int sow = weekDef.getFirstDayOfWeek().getValue(); + int isoDow = temporal.get(ChronoField.DAY_OF_WEEK); + int dow = Math.floorMod(isoDow - sow, 7) + 1; + + int offset = startOfWeekOffset(temporal.get(field), dow); + ValueRange fieldRange = temporal.range(field); + return ValueRange.of(computeWeek(offset, (int) fieldRange.getMinimum()), + computeWeek(offset, (int) fieldRange.getMaximum())); + } + + //----------------------------------------------------------------------- + @Override + public String toString() { + return getName() + "[" + weekDef.toString() + "]"; + } + } +} diff --git a/jdk/src/share/classes/java/time/temporal/Year.java b/jdk/src/share/classes/java/time/temporal/Year.java new file mode 100644 index 00000000000..f54acf721f8 --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/Year.java @@ -0,0 +1,996 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import static java.time.temporal.ChronoField.ERA; +import static java.time.temporal.ChronoField.YEAR; +import static java.time.temporal.ChronoField.YEAR_OF_ERA; +import static java.time.temporal.ChronoUnit.YEARS; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.time.Clock; +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.Month; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.DateTimeParseException; +import java.time.format.SignStyle; +import java.util.Objects; + +/** + * A year in the ISO-8601 calendar system, such as {@code 2007}. + *

    + * {@code Year} is an immutable date-time object that represents a year. + * Any field that can be derived from a year can be obtained. + *

    + * Note that years in the ISO chronology only align with years in the + * Gregorian-Julian system for modern years. Parts of Russia did not switch to the + * modern Gregorian/ISO rules until 1920. + * As such, historical years must be treated with caution. + *

    + * This class does not store or represent a month, day, time or time-zone. + * For example, the value "2007" can be stored in a {@code Year}. + *

    + * Years represented by this class follow the ISO-8601 standard and use + * the proleptic numbering system. Year 1 is preceded by year 0, then by year -1. + *

    + * The ISO-8601 calendar system is the modern civil calendar system used today + * in most of the world. It is equivalent to the proleptic Gregorian calendar + * system, in which today's rules for leap years are applied for all time. + * For most applications written today, the ISO-8601 rules are entirely suitable. + * However, any application that makes use of historical dates, and requires them + * to be accurate will find the ISO-8601 approach unsuitable. + * + *

    Specification for implementors

    + * This class is immutable and thread-safe. + * + * @since 1.8 + */ +public final class Year + implements Temporal, TemporalAdjuster, Comparable, Serializable { + + /** + * The minimum supported year, '-999,999,999'. + */ + public static final int MIN_VALUE = -999_999_999; + /** + * The maximum supported year, '+999,999,999'. + */ + public static final int MAX_VALUE = 999_999_999; + + /** + * Serialization version. + */ + private static final long serialVersionUID = -23038383694477807L; + /** + * Parser. + */ + private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder() + .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD) + .toFormatter(); + + /** + * The year being represented. + */ + private final int year; + + //----------------------------------------------------------------------- + /** + * Obtains the current year from the system clock in the default time-zone. + *

    + * This will query the {@link java.time.Clock#systemDefaultZone() system clock} in the default + * time-zone to obtain the current year. + *

    + * Using this method will prevent the ability to use an alternate clock for testing + * because the clock is hard-coded. + * + * @return the current year using the system clock and default time-zone, not null + */ + public static Year now() { + return now(Clock.systemDefaultZone()); + } + + /** + * Obtains the current year from the system clock in the specified time-zone. + *

    + * This will query the {@link Clock#system(java.time.ZoneId) system clock} to obtain the current year. + * Specifying the time-zone avoids dependence on the default time-zone. + *

    + * Using this method will prevent the ability to use an alternate clock for testing + * because the clock is hard-coded. + * + * @param zone the zone ID to use, not null + * @return the current year using the system clock, not null + */ + public static Year now(ZoneId zone) { + return now(Clock.system(zone)); + } + + /** + * Obtains the current year from the specified clock. + *

    + * This will query the specified clock to obtain the current year. + * Using this method allows the use of an alternate clock for testing. + * The alternate clock may be introduced using {@link Clock dependency injection}. + * + * @param clock the clock to use, not null + * @return the current year, not null + */ + public static Year now(Clock clock) { + final LocalDate now = LocalDate.now(clock); // called once + return Year.of(now.getYear()); + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code Year}. + *

    + * This method accepts a year value from the proleptic ISO calendar system. + *

    + * The year 2AD/CE is represented by 2.
    + * The year 1AD/CE is represented by 1.
    + * The year 1BC/BCE is represented by 0.
    + * The year 2BC/BCE is represented by -1.
    + * + * @param isoYear the ISO proleptic year to represent, from {@code MIN_VALUE} to {@code MAX_VALUE} + * @return the year, not null + * @throws DateTimeException if the field is invalid + */ + public static Year of(int isoYear) { + YEAR.checkValidValue(isoYear); + return new Year(isoYear); + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code Year} from a temporal object. + *

    + * A {@code TemporalAccessor} represents some form of date and time information. + * This factory converts the arbitrary temporal object to an instance of {@code Year}. + *

    + * The conversion extracts the {@link ChronoField#YEAR year} field. + * The extraction is only permitted if the temporal object has an ISO + * chronology, or can be converted to a {@code LocalDate}. + *

    + * This method matches the signature of the functional interface {@link TemporalQuery} + * allowing it to be used in queries via method reference, {@code Year::from}. + * + * @param temporal the temporal object to convert, not null + * @return the year, not null + * @throws DateTimeException if unable to convert to a {@code Year} + */ + public static Year from(TemporalAccessor temporal) { + if (temporal instanceof Year) { + return (Year) temporal; + } + try { + if (ISOChrono.INSTANCE.equals(Chrono.from(temporal)) == false) { + temporal = LocalDate.from(temporal); + } + return of(temporal.get(YEAR)); + } catch (DateTimeException ex) { + throw new DateTimeException("Unable to obtain Year from TemporalAccessor: " + temporal.getClass(), ex); + } + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code Year} from a text string such as {@code 2007}. + *

    + * The string must represent a valid year. + * Years outside the range 0000 to 9999 must be prefixed by the plus or minus symbol. + * + * @param text the text to parse such as "2007", not null + * @return the parsed year, not null + * @throws DateTimeParseException if the text cannot be parsed + */ + public static Year parse(CharSequence text) { + return parse(text, PARSER); + } + + /** + * Obtains an instance of {@code Year} from a text string using a specific formatter. + *

    + * The text is parsed using the formatter, returning a year. + * + * @param text the text to parse, not null + * @param formatter the formatter to use, not null + * @return the parsed year, not null + * @throws DateTimeParseException if the text cannot be parsed + */ + public static Year parse(CharSequence text, DateTimeFormatter formatter) { + Objects.requireNonNull(formatter, "formatter"); + return formatter.parse(text, Year::from); + } + + //------------------------------------------------------------------------- + /** + * Checks if the year is a leap year, according to the ISO proleptic + * calendar system rules. + *

    + * This method applies the current rules for leap years across the whole time-line. + * In general, a year is a leap year if it is divisible by four without + * remainder. However, years divisible by 100, are not leap years, with + * the exception of years divisible by 400 which are. + *

    + * For example, 1904 is a leap year it is divisible by 4. + * 1900 was not a leap year as it is divisible by 100, however 2000 was a + * leap year as it is divisible by 400. + *

    + * The calculation is proleptic - applying the same rules into the far future and far past. + * This is historically inaccurate, but is correct for the ISO-8601 standard. + * + * @param year the year to check + * @return true if the year is leap, false otherwise + */ + public static boolean isLeap(long year) { + return ((year & 3) == 0) && ((year % 100) != 0 || (year % 400) == 0); + } + + //----------------------------------------------------------------------- + /** + * Constructor. + * + * @param year the year to represent + */ + private Year(int year) { + this.year = year; + } + + //----------------------------------------------------------------------- + /** + * Gets the year value. + *

    + * The year returned by this method is proleptic as per {@code get(YEAR)}. + * + * @return the year, {@code MIN_VALUE} to {@code MAX_VALUE} + */ + public int getValue() { + return year; + } + + //----------------------------------------------------------------------- + /** + * Checks if the specified field is supported. + *

    + * This checks if this year can be queried for the specified field. + * If false, then calling the {@link #range(TemporalField) range} and + * {@link #get(TemporalField) get} methods will throw an exception. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@link #isSupported(TemporalField) supported fields} will return valid + * values based on this date-time. + * The supported fields are: + *

      + *
    • {@code YEAR_OF_ERA} + *
    • {@code YEAR} + *
    • {@code ERA} + *
    + * All other {@code ChronoField} instances will return false. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doIsSupported(TemporalAccessor)} + * passing {@code this} as the argument. + * Whether the field is supported is determined by the field. + * + * @param field the field to check, null returns false + * @return true if the field is supported on this year, false if not + */ + @Override + public boolean isSupported(TemporalField field) { + if (field instanceof ChronoField) { + return field == YEAR || field == YEAR_OF_ERA || field == ERA; + } + return field != null && field.doIsSupported(this); + } + + /** + * Gets the range of valid values for the specified field. + *

    + * The range object expresses the minimum and maximum valid values for a field. + * This year is used to enhance the accuracy of the returned range. + * If it is not possible to return the range, because the field is not supported + * or for some other reason, an exception is thrown. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@link #isSupported(TemporalField) supported fields} will return + * appropriate range instances. + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doRange(TemporalAccessor)} + * passing {@code this} as the argument. + * Whether the range can be obtained is determined by the field. + * + * @param field the field to query the range for, not null + * @return the range of valid values for the field, not null + * @throws DateTimeException if the range for the field cannot be obtained + */ + @Override + public ValueRange range(TemporalField field) { + if (field == YEAR_OF_ERA) { + return (year <= 0 ? ValueRange.of(1, MAX_VALUE + 1) : ValueRange.of(1, MAX_VALUE)); + } + return Temporal.super.range(field); + } + + /** + * Gets the value of the specified field from this year as an {@code int}. + *

    + * This queries this year for the value for the specified field. + * The returned value will always be within the valid range of values for the field. + * If it is not possible to return the value, because the field is not supported + * or for some other reason, an exception is thrown. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@link #isSupported(TemporalField) supported fields} will return valid + * values based on this year. + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doGet(TemporalAccessor)} + * passing {@code this} as the argument. Whether the value can be obtained, + * and what the value represents, is determined by the field. + * + * @param field the field to get, not null + * @return the value for the field + * @throws DateTimeException if a value for the field cannot be obtained + * @throws ArithmeticException if numeric overflow occurs + */ + @Override // override for Javadoc + public int get(TemporalField field) { + return range(field).checkValidIntValue(getLong(field), field); + } + + /** + * Gets the value of the specified field from this year as a {@code long}. + *

    + * This queries this year for the value for the specified field. + * If it is not possible to return the value, because the field is not supported + * or for some other reason, an exception is thrown. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@link #isSupported(TemporalField) supported fields} will return valid + * values based on this year. + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doGet(TemporalAccessor)} + * passing {@code this} as the argument. Whether the value can be obtained, + * and what the value represents, is determined by the field. + * + * @param field the field to get, not null + * @return the value for the field + * @throws DateTimeException if a value for the field cannot be obtained + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public long getLong(TemporalField field) { + if (field instanceof ChronoField) { + switch ((ChronoField) field) { + case YEAR_OF_ERA: return (year < 1 ? 1 - year : year); + case YEAR: return year; + case ERA: return (year < 1 ? 0 : 1); + } + throw new DateTimeException("Unsupported field: " + field.getName()); + } + return field.doGet(this); + } + + //----------------------------------------------------------------------- + /** + * Checks if the year is a leap year, according to the ISO proleptic + * calendar system rules. + *

    + * This method applies the current rules for leap years across the whole time-line. + * In general, a year is a leap year if it is divisible by four without + * remainder. However, years divisible by 100, are not leap years, with + * the exception of years divisible by 400 which are. + *

    + * For example, 1904 is a leap year it is divisible by 4. + * 1900 was not a leap year as it is divisible by 100, however 2000 was a + * leap year as it is divisible by 400. + *

    + * The calculation is proleptic - applying the same rules into the far future and far past. + * This is historically inaccurate, but is correct for the ISO-8601 standard. + * + * @return true if the year is leap, false otherwise + */ + public boolean isLeap() { + return Year.isLeap(year); + } + + /** + * Checks if the month-day is valid for this year. + *

    + * This method checks whether this year and the input month and day form + * a valid date. + * + * @param monthDay the month-day to validate, null returns false + * @return true if the month and day are valid for this year + */ + public boolean isValidMonthDay(MonthDay monthDay) { + return monthDay != null && monthDay.isValidYear(year); + } + + /** + * Gets the length of this year in days. + * + * @return the length of this year in days, 365 or 366 + */ + public int length() { + return isLeap() ? 366 : 365; + } + + //----------------------------------------------------------------------- + /** + * Returns an adjusted copy of this year. + *

    + * This returns a new {@code Year}, based on this one, with the year adjusted. + * The adjustment takes place using the specified adjuster strategy object. + * Read the documentation of the adjuster to understand what adjustment will be made. + *

    + * The result of this method is obtained by invoking the + * {@link TemporalAdjuster#adjustInto(Temporal)} method on the + * specified adjuster passing {@code this} as the argument. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param adjuster the adjuster to use, not null + * @return a {@code Year} based on {@code this} with the adjustment made, not null + * @throws DateTimeException if the adjustment cannot be made + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public Year with(TemporalAdjuster adjuster) { + return (Year) adjuster.adjustInto(this); + } + + /** + * Returns a copy of this year with the specified field set to a new value. + *

    + * This returns a new {@code Year}, based on this one, with the value + * for the specified field changed. + * If it is not possible to set the value, because the field is not supported or for + * some other reason, an exception is thrown. + *

    + * If the field is a {@link ChronoField} then the adjustment is implemented here. + * The supported fields behave as follows: + *

      + *
    • {@code YEAR_OF_ERA} - + * Returns a {@code Year} with the specified year-of-era + * The era will be unchanged. + *
    • {@code YEAR} - + * Returns a {@code Year} with the specified year. + * This completely replaces the date and is equivalent to {@link #of(int)}. + *
    • {@code ERA} - + * Returns a {@code Year} with the specified era. + * The year-of-era will be unchanged. + *
    + *

    + * In all cases, if the new value is outside the valid range of values for the field + * then a {@code DateTimeException} will be thrown. + *

    + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doWith(Temporal, long)} + * passing {@code this} as the argument. In this case, the field determines + * whether and how to adjust the instant. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param field the field to set in the result, not null + * @param newValue the new value of the field in the result + * @return a {@code Year} based on {@code this} with the specified field set, not null + * @throws DateTimeException if the field cannot be set + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public Year with(TemporalField field, long newValue) { + if (field instanceof ChronoField) { + ChronoField f = (ChronoField) field; + f.checkValidValue(newValue); + switch (f) { + case YEAR_OF_ERA: return Year.of((int) (year < 1 ? 1 - newValue : newValue)); + case YEAR: return Year.of((int) newValue); + case ERA: return (getLong(ERA) == newValue ? this : Year.of(1 - year)); + } + throw new DateTimeException("Unsupported field: " + field.getName()); + } + return field.doWith(this, newValue); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this year with the specified period added. + *

    + * This method returns a new year based on this year with the specified period added. + * The adder is typically {@link java.time.Period} but may be any other type implementing + * the {@link TemporalAdder} interface. + * The calculation is delegated to the specified adjuster, which typically calls + * back to {@link #plus(long, TemporalUnit)}. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param adder the adder to use, not null + * @return a {@code Year} based on this year with the addition made, not null + * @throws DateTimeException if the addition cannot be made + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public Year plus(TemporalAdder adder) { + return (Year) adder.addTo(this); + } + + /** + * {@inheritDoc} + * @throws DateTimeException {@inheritDoc} + * @throws ArithmeticException {@inheritDoc} + */ + @Override + public Year plus(long amountToAdd, TemporalUnit unit) { + if (unit instanceof ChronoUnit) { + switch ((ChronoUnit) unit) { + case YEARS: return plusYears(amountToAdd); + case DECADES: return plusYears(Math.multiplyExact(amountToAdd, 10)); + case CENTURIES: return plusYears(Math.multiplyExact(amountToAdd, 100)); + case MILLENNIA: return plusYears(Math.multiplyExact(amountToAdd, 1000)); + case ERAS: return with(ERA, Math.addExact(getLong(ERA), amountToAdd)); + } + throw new DateTimeException("Unsupported unit: " + unit.getName()); + } + return unit.doPlus(this, amountToAdd); + } + + /** + * Returns a copy of this year with the specified number of years added. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param yearsToAdd the years to add, may be negative + * @return a {@code Year} based on this year with the period added, not null + * @throws DateTimeException if the result exceeds the supported year range + */ + public Year plusYears(long yearsToAdd) { + if (yearsToAdd == 0) { + return this; + } + return of(YEAR.checkValidIntValue(year + yearsToAdd)); // overflow safe + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this year with the specified period subtracted. + *

    + * This method returns a new year based on this year with the specified period subtracted. + * The subtractor is typically {@link java.time.Period} but may be any other type implementing + * the {@link TemporalSubtractor} interface. + * The calculation is delegated to the specified adjuster, which typically calls + * back to {@link #minus(long, TemporalUnit)}. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param subtractor the subtractor to use, not null + * @return a {@code Year} based on this year with the subtraction made, not null + * @throws DateTimeException if the subtraction cannot be made + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public Year minus(TemporalSubtractor subtractor) { + return (Year) subtractor.subtractFrom(this); + } + + /** + * {@inheritDoc} + * @throws DateTimeException {@inheritDoc} + * @throws ArithmeticException {@inheritDoc} + */ + @Override + public Year minus(long amountToSubtract, TemporalUnit unit) { + return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit)); + } + + /** + * Returns a copy of this year with the specified number of years subtracted. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param yearsToSubtract the years to subtract, may be negative + * @return a {@code Year} based on this year with the period subtracted, not null + * @throws DateTimeException if the result exceeds the supported year range + */ + public Year minusYears(long yearsToSubtract) { + return (yearsToSubtract == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-yearsToSubtract)); + } + + //----------------------------------------------------------------------- + /** + * Queries this year using the specified query. + *

    + * This queries this year using the specified query strategy object. + * The {@code TemporalQuery} object defines the logic to be used to + * obtain the result. Read the documentation of the query to understand + * what the result of this method will be. + *

    + * The result of this method is obtained by invoking the + * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the + * specified query passing {@code this} as the argument. + * + * @param the type of the result + * @param query the query to invoke, not null + * @return the query result, null may be returned (defined by the query) + * @throws DateTimeException if unable to query (defined by the query) + * @throws ArithmeticException if numeric overflow occurs (defined by the query) + */ + @SuppressWarnings("unchecked") + @Override + public R query(TemporalQuery query) { + if (query == Queries.chrono()) { + return (R) ISOChrono.INSTANCE; + } else if (query == Queries.precision()) { + return (R) YEARS; + } + return Temporal.super.query(query); + } + + /** + * Adjusts the specified temporal object to have this year. + *

    + * This returns a temporal object of the same observable type as the input + * with the year changed to be the same as this. + *

    + * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)} + * passing {@link ChronoField#YEAR} as the field. + * If the specified temporal object does not use the ISO calendar system then + * a {@code DateTimeException} is thrown. + *

    + * In most cases, it is clearer to reverse the calling pattern by using + * {@link Temporal#with(TemporalAdjuster)}: + *

    +     *   // these two lines are equivalent, but the second approach is recommended
    +     *   temporal = thisYear.adjustInto(temporal);
    +     *   temporal = temporal.with(thisYear);
    +     * 
    + *

    + * This instance is immutable and unaffected by this method call. + * + * @param temporal the target object to be adjusted, not null + * @return the adjusted object, not null + * @throws DateTimeException if unable to make the adjustment + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public Temporal adjustInto(Temporal temporal) { + if (Chrono.from(temporal).equals(ISOChrono.INSTANCE) == false) { + throw new DateTimeException("Adjustment only supported on ISO date-time"); + } + return temporal.with(YEAR, year); + } + + /** + * Calculates the period between this year and another year in + * terms of the specified unit. + *

    + * This calculates the period between two years in terms of a single unit. + * The start and end points are {@code this} and the specified year. + * The result will be negative if the end is before the start. + * The {@code Temporal} passed to this method must be a {@code Year}. + * For example, the period in decades between two year can be calculated + * using {@code startYear.periodUntil(endYear, DECADES)}. + *

    + * The calculation returns a whole number, representing the number of + * complete units between the two years. + * For example, the period in decades between 2012 and 2031 + * will only be one decade as it is one year short of two decades. + *

    + * This method operates in association with {@link TemporalUnit#between}. + * The result of this method is a {@code long} representing the amount of + * the specified unit. By contrast, the result of {@code between} is an + * object that can be used directly in addition/subtraction: + *

    +     *   long period = start.periodUntil(end, YEARS);   // this method
    +     *   dateTime.plus(YEARS.between(start, end));      // use in plus/minus
    +     * 
    + *

    + * The calculation is implemented in this method for {@link ChronoUnit}. + * The units {@code YEARS}, {@code DECADES}, {@code CENTURIES}, + * {@code MILLENNIA} and {@code ERAS} are supported. + * Other {@code ChronoUnit} values will throw an exception. + *

    + * If the unit is not a {@code ChronoUnit}, then the result of this method + * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)} + * passing {@code this} as the first argument and the input temporal as + * the second argument. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param endYear the end year, which must be a {@code Year}, not null + * @param unit the unit to measure the period in, not null + * @return the amount of the period between this year and the end year + * @throws DateTimeException if the period cannot be calculated + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public long periodUntil(Temporal endYear, TemporalUnit unit) { + if (endYear instanceof Year == false) { + Objects.requireNonNull(endYear, "endYear"); + throw new DateTimeException("Unable to calculate period between objects of two different types"); + } + Year end = (Year) endYear; + if (unit instanceof ChronoUnit) { + long yearsUntil = ((long) end.year) - year; // no overflow + switch ((ChronoUnit) unit) { + case YEARS: return yearsUntil; + case DECADES: return yearsUntil / 10; + case CENTURIES: return yearsUntil / 100; + case MILLENNIA: return yearsUntil / 1000; + case ERAS: return end.getLong(ERA) - getLong(ERA); + } + throw new DateTimeException("Unsupported unit: " + unit.getName()); + } + return unit.between(this, endYear).getAmount(); + } + + //----------------------------------------------------------------------- + /** + * Returns a date formed from this year at the specified day-of-year. + *

    + * This combines this year and the specified day-of-year to form a {@code LocalDate}. + * The day-of-year value 366 is only valid in a leap year. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param dayOfYear the day-of-year to use, not null + * @return the local date formed from this year and the specified date of year, not null + * @throws DateTimeException if the day of year is 366 and this is not a leap year + */ + public LocalDate atDay(int dayOfYear) { + return LocalDate.ofYearDay(year, dayOfYear); + } + + /** + * Returns a year-month formed from this year at the specified month. + *

    + * This combines this year and the specified month to form a {@code YearMonth}. + * All possible combinations of year and month are valid. + *

    + * This method can be used as part of a chain to produce a date: + *

    +     *  LocalDate date = year.atMonth(month).atDay(day);
    +     * 
    + *

    + * This instance is immutable and unaffected by this method call. + * + * @param month the month-of-year to use, not null + * @return the year-month formed from this year and the specified month, not null + */ + public YearMonth atMonth(Month month) { + return YearMonth.of(year, month); + } + + /** + * Returns a year-month formed from this year at the specified month. + *

    + * This combines this year and the specified month to form a {@code YearMonth}. + * All possible combinations of year and month are valid. + *

    + * This method can be used as part of a chain to produce a date: + *

    +     *  LocalDate date = year.atMonth(month).atDay(day);
    +     * 
    + *

    + * This instance is immutable and unaffected by this method call. + * + * @param month the month-of-year to use, from 1 (January) to 12 (December) + * @return the year-month formed from this year and the specified month, not null + */ + public YearMonth atMonth(int month) { + return YearMonth.of(year, month); + } + + /** + * Returns a date formed from this year at the specified month-day. + *

    + * This combines this year and the specified month-day to form a {@code LocalDate}. + * The month-day value of February 29th is only valid in a leap year. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param monthDay the month-day to use, not null + * @return the local date formed from this year and the specified month-day, not null + * @throws DateTimeException if the month-day is February 29th and this is not a leap year + */ + public LocalDate atMonthDay(MonthDay monthDay) { + return LocalDate.of(year, monthDay.getMonth(), monthDay.getDayOfMonth()); + } + + //----------------------------------------------------------------------- + /** + * Compares this year to another year. + *

    + * The comparison is based on the value of the year. + * It is "consistent with equals", as defined by {@link Comparable}. + * + * @param other the other year to compare to, not null + * @return the comparator value, negative if less, positive if greater + */ + public int compareTo(Year other) { + return year - other.year; + } + + /** + * Is this year after the specified year. + * + * @param other the other year to compare to, not null + * @return true if this is after the specified year + */ + public boolean isAfter(Year other) { + return year > other.year; + } + + /** + * Is this year before the specified year. + * + * @param other the other year to compare to, not null + * @return true if this point is before the specified year + */ + public boolean isBefore(Year other) { + return year < other.year; + } + + //----------------------------------------------------------------------- + /** + * Checks if this year is equal to another year. + *

    + * The comparison is based on the time-line position of the years. + * + * @param obj the object to check, null returns false + * @return true if this is equal to the other year + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof Year) { + return year == ((Year) obj).year; + } + return false; + } + + /** + * A hash code for this year. + * + * @return a suitable hash code + */ + @Override + public int hashCode() { + return year; + } + + //----------------------------------------------------------------------- + /** + * Outputs this year as a {@code String}. + * + * @return a string representation of this year, not null + */ + @Override + public String toString() { + return Integer.toString(year); + } + + /** + * Outputs this year as a {@code String} using the formatter. + *

    + * This year will be passed to the formatter + * {@link DateTimeFormatter#print(TemporalAccessor) print method}. + * + * @param formatter the formatter to use, not null + * @return the formatted year string, not null + * @throws DateTimeException if an error occurs during printing + */ + public String toString(DateTimeFormatter formatter) { + Objects.requireNonNull(formatter, "formatter"); + return formatter.print(this); + } + + //----------------------------------------------------------------------- + /** + * Writes the object using a + * dedicated serialized form. + *

    +     *  out.writeByte(4);  // identifies this as a Year
    +     *  out.writeInt(year);
    +     * 
    + * + * @return the instance of {@code Ser}, not null + */ + private Object writeReplace() { + return new Ser(Ser.YEAR_TYPE, this); + } + + /** + * Defend against malicious streams. + * @return never + * @throws InvalidObjectException always + */ + private Object readResolve() throws ObjectStreamException { + throw new InvalidObjectException("Deserialization via serialization delegate"); + } + + void writeExternal(DataOutput out) throws IOException { + out.writeInt(year); + } + + static Year readExternal(DataInput in) throws IOException { + return Year.of(in.readInt()); + } + +} diff --git a/jdk/src/share/classes/java/time/temporal/YearMonth.java b/jdk/src/share/classes/java/time/temporal/YearMonth.java new file mode 100644 index 00000000000..246112084ca --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/YearMonth.java @@ -0,0 +1,1085 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.temporal; + +import static java.time.temporal.ChronoField.EPOCH_MONTH; +import static java.time.temporal.ChronoField.ERA; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.YEAR; +import static java.time.temporal.ChronoField.YEAR_OF_ERA; +import static java.time.temporal.ChronoUnit.MONTHS; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.time.Clock; +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.Month; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.DateTimeParseException; +import java.time.format.SignStyle; +import java.util.Objects; + +/** + * A year-month in the ISO-8601 calendar system, such as {@code 2007-12}. + *

    + * {@code YearMonth} is an immutable date-time object that represents the combination + * of a year and month. Any field that can be derived from a year and month, such as + * quarter-of-year, can be obtained. + *

    + * This class does not store or represent a day, time or time-zone. + * For example, the value "October 2007" can be stored in a {@code YearMonth}. + *

    + * The ISO-8601 calendar system is the modern civil calendar system used today + * in most of the world. It is equivalent to the proleptic Gregorian calendar + * system, in which today's rules for leap years are applied for all time. + * For most applications written today, the ISO-8601 rules are entirely suitable. + * However, any application that makes use of historical dates, and requires them + * to be accurate will find the ISO-8601 approach unsuitable. + * + *

    Specification for implementors

    + * This class is immutable and thread-safe. + * + * @since 1.8 + */ +public final class YearMonth + implements Temporal, TemporalAdjuster, Comparable, Serializable { + + /** + * Serialization version. + */ + private static final long serialVersionUID = 4183400860270640070L; + /** + * Parser. + */ + private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder() + .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD) + .appendLiteral('-') + .appendValue(MONTH_OF_YEAR, 2) + .toFormatter(); + + /** + * The year. + */ + private final int year; + /** + * The month-of-year, not null. + */ + private final int month; + + //----------------------------------------------------------------------- + /** + * Obtains the current year-month from the system clock in the default time-zone. + *

    + * This will query the {@link java.time.Clock#systemDefaultZone() system clock} in the default + * time-zone to obtain the current year-month. + * The zone and offset will be set based on the time-zone in the clock. + *

    + * Using this method will prevent the ability to use an alternate clock for testing + * because the clock is hard-coded. + * + * @return the current year-month using the system clock and default time-zone, not null + */ + public static YearMonth now() { + return now(Clock.systemDefaultZone()); + } + + /** + * Obtains the current year-month from the system clock in the specified time-zone. + *

    + * This will query the {@link Clock#system(java.time.ZoneId) system clock} to obtain the current year-month. + * Specifying the time-zone avoids dependence on the default time-zone. + *

    + * Using this method will prevent the ability to use an alternate clock for testing + * because the clock is hard-coded. + * + * @param zone the zone ID to use, not null + * @return the current year-month using the system clock, not null + */ + public static YearMonth now(ZoneId zone) { + return now(Clock.system(zone)); + } + + /** + * Obtains the current year-month from the specified clock. + *

    + * This will query the specified clock to obtain the current year-month. + * Using this method allows the use of an alternate clock for testing. + * The alternate clock may be introduced using {@link Clock dependency injection}. + * + * @param clock the clock to use, not null + * @return the current year-month, not null + */ + public static YearMonth now(Clock clock) { + final LocalDate now = LocalDate.now(clock); // called once + return YearMonth.of(now.getYear(), now.getMonth()); + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code YearMonth} from a year and month. + * + * @param year the year to represent, from MIN_YEAR to MAX_YEAR + * @param month the month-of-year to represent, not null + * @return the year-month, not null + * @throws DateTimeException if the year value is invalid + */ + public static YearMonth of(int year, Month month) { + Objects.requireNonNull(month, "month"); + return of(year, month.getValue()); + } + + /** + * Obtains an instance of {@code YearMonth} from a year and month. + * + * @param year the year to represent, from MIN_YEAR to MAX_YEAR + * @param month the month-of-year to represent, from 1 (January) to 12 (December) + * @return the year-month, not null + * @throws DateTimeException if either field value is invalid + */ + public static YearMonth of(int year, int month) { + YEAR.checkValidValue(year); + MONTH_OF_YEAR.checkValidValue(month); + return new YearMonth(year, month); + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code YearMonth} from a temporal object. + *

    + * A {@code TemporalAccessor} represents some form of date and time information. + * This factory converts the arbitrary temporal object to an instance of {@code YearMonth}. + *

    + * The conversion extracts the {@link ChronoField#YEAR YEAR} and + * {@link ChronoField#MONTH_OF_YEAR MONTH_OF_YEAR} fields. + * The extraction is only permitted if the temporal object has an ISO + * chronology, or can be converted to a {@code LocalDate}. + *

    + * This method matches the signature of the functional interface {@link TemporalQuery} + * allowing it to be used in queries via method reference, {@code YearMonth::from}. + * + * @param temporal the temporal object to convert, not null + * @return the year-month, not null + * @throws DateTimeException if unable to convert to a {@code YearMonth} + */ + public static YearMonth from(TemporalAccessor temporal) { + if (temporal instanceof YearMonth) { + return (YearMonth) temporal; + } + try { + if (ISOChrono.INSTANCE.equals(Chrono.from(temporal)) == false) { + temporal = LocalDate.from(temporal); + } + return of(temporal.get(YEAR), temporal.get(MONTH_OF_YEAR)); + } catch (DateTimeException ex) { + throw new DateTimeException("Unable to obtain YearMonth from TemporalAccessor: " + temporal.getClass(), ex); + } + } + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code YearMonth} from a text string such as {@code 2007-12}. + *

    + * The string must represent a valid year-month. + * The format must be {@code yyyy-MM}. + * Years outside the range 0000 to 9999 must be prefixed by the plus or minus symbol. + * + * @param text the text to parse such as "2007-12", not null + * @return the parsed year-month, not null + * @throws DateTimeParseException if the text cannot be parsed + */ + public static YearMonth parse(CharSequence text) { + return parse(text, PARSER); + } + + /** + * Obtains an instance of {@code YearMonth} from a text string using a specific formatter. + *

    + * The text is parsed using the formatter, returning a year-month. + * + * @param text the text to parse, not null + * @param formatter the formatter to use, not null + * @return the parsed year-month, not null + * @throws DateTimeParseException if the text cannot be parsed + */ + public static YearMonth parse(CharSequence text, DateTimeFormatter formatter) { + Objects.requireNonNull(formatter, "formatter"); + return formatter.parse(text, YearMonth::from); + } + + //----------------------------------------------------------------------- + /** + * Constructor. + * + * @param year the year to represent, validated from MIN_YEAR to MAX_YEAR + * @param month the month-of-year to represent, validated from 1 (January) to 12 (December) + */ + private YearMonth(int year, int month) { + this.year = year; + this.month = month; + } + + /** + * Returns a copy of this year-month with the new year and month, checking + * to see if a new object is in fact required. + * + * @param newYear the year to represent, validated from MIN_YEAR to MAX_YEAR + * @param newMonth the month-of-year to represent, validated not null + * @return the year-month, not null + */ + private YearMonth with(int newYear, int newMonth) { + if (year == newYear && month == newMonth) { + return this; + } + return new YearMonth(newYear, newMonth); + } + + //----------------------------------------------------------------------- + /** + * Checks if the specified field is supported. + *

    + * This checks if this year-month can be queried for the specified field. + * If false, then calling the {@link #range(TemporalField) range} and + * {@link #get(TemporalField) get} methods will throw an exception. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@link #isSupported(TemporalField) supported fields} will return valid + * values based on this date-time. + * The supported fields are: + *

      + *
    • {@code MONTH_OF_YEAR} + *
    • {@code EPOCH_MONTH} + *
    • {@code YEAR_OF_ERA} + *
    • {@code YEAR} + *
    • {@code ERA} + *
    + * All other {@code ChronoField} instances will return false. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doIsSupported(TemporalAccessor)} + * passing {@code this} as the argument. + * Whether the field is supported is determined by the field. + * + * @param field the field to check, null returns false + * @return true if the field is supported on this year-month, false if not + */ + @Override + public boolean isSupported(TemporalField field) { + if (field instanceof ChronoField) { + return field == YEAR || field == MONTH_OF_YEAR || + field == EPOCH_MONTH || field == YEAR_OF_ERA || field == ERA; + } + return field != null && field.doIsSupported(this); + } + + /** + * Gets the range of valid values for the specified field. + *

    + * The range object expresses the minimum and maximum valid values for a field. + * This year-month is used to enhance the accuracy of the returned range. + * If it is not possible to return the range, because the field is not supported + * or for some other reason, an exception is thrown. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@link #isSupported(TemporalField) supported fields} will return + * appropriate range instances. + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doRange(TemporalAccessor)} + * passing {@code this} as the argument. + * Whether the range can be obtained is determined by the field. + * + * @param field the field to query the range for, not null + * @return the range of valid values for the field, not null + * @throws DateTimeException if the range for the field cannot be obtained + */ + @Override + public ValueRange range(TemporalField field) { + if (field == YEAR_OF_ERA) { + return (getYear() <= 0 ? ValueRange.of(1, Year.MAX_VALUE + 1) : ValueRange.of(1, Year.MAX_VALUE)); + } + return Temporal.super.range(field); + } + + /** + * Gets the value of the specified field from this year-month as an {@code int}. + *

    + * This queries this year-month for the value for the specified field. + * The returned value will always be within the valid range of values for the field. + * If it is not possible to return the value, because the field is not supported + * or for some other reason, an exception is thrown. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@link #isSupported(TemporalField) supported fields} will return valid + * values based on this year-month, except {@code EPOCH_MONTH} which is too + * large to fit in an {@code int} and throw a {@code DateTimeException}. + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doGet(TemporalAccessor)} + * passing {@code this} as the argument. Whether the value can be obtained, + * and what the value represents, is determined by the field. + * + * @param field the field to get, not null + * @return the value for the field + * @throws DateTimeException if a value for the field cannot be obtained + * @throws ArithmeticException if numeric overflow occurs + */ + @Override // override for Javadoc + public int get(TemporalField field) { + return range(field).checkValidIntValue(getLong(field), field); + } + + /** + * Gets the value of the specified field from this year-month as a {@code long}. + *

    + * This queries this year-month for the value for the specified field. + * If it is not possible to return the value, because the field is not supported + * or for some other reason, an exception is thrown. + *

    + * If the field is a {@link ChronoField} then the query is implemented here. + * The {@link #isSupported(TemporalField) supported fields} will return valid + * values based on this year-month. + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doGet(TemporalAccessor)} + * passing {@code this} as the argument. Whether the value can be obtained, + * and what the value represents, is determined by the field. + * + * @param field the field to get, not null + * @return the value for the field + * @throws DateTimeException if a value for the field cannot be obtained + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public long getLong(TemporalField field) { + if (field instanceof ChronoField) { + switch ((ChronoField) field) { + case MONTH_OF_YEAR: return month; + case EPOCH_MONTH: return getEpochMonth(); + case YEAR_OF_ERA: return (year < 1 ? 1 - year : year); + case YEAR: return year; + case ERA: return (year < 1 ? 0 : 1); + } + throw new DateTimeException("Unsupported field: " + field.getName()); + } + return field.doGet(this); + } + + private long getEpochMonth() { + return ((year - 1970) * 12L) + (month - 1); + } + + //----------------------------------------------------------------------- + /** + * Gets the year field. + *

    + * This method returns the primitive {@code int} value for the year. + *

    + * The year returned by this method is proleptic as per {@code get(YEAR)}. + * + * @return the year, from MIN_YEAR to MAX_YEAR + */ + public int getYear() { + return year; + } + + /** + * Gets the month-of-year field using the {@code Month} enum. + *

    + * This method returns the enum {@link Month} for the month. + * This avoids confusion as to what {@code int} values mean. + * If you need access to the primitive {@code int} value then the enum + * provides the {@link Month#getValue() int value}. + * + * @return the month-of-year, not null + */ + public Month getMonth() { + return Month.of(month); + } + + //----------------------------------------------------------------------- + /** + * Checks if the year is a leap year, according to the ISO proleptic + * calendar system rules. + *

    + * This method applies the current rules for leap years across the whole time-line. + * In general, a year is a leap year if it is divisible by four without + * remainder. However, years divisible by 100, are not leap years, with + * the exception of years divisible by 400 which are. + *

    + * For example, 1904 is a leap year it is divisible by 4. + * 1900 was not a leap year as it is divisible by 100, however 2000 was a + * leap year as it is divisible by 400. + *

    + * The calculation is proleptic - applying the same rules into the far future and far past. + * This is historically inaccurate, but is correct for the ISO-8601 standard. + * + * @return true if the year is leap, false otherwise + */ + public boolean isLeapYear() { + return ISOChrono.INSTANCE.isLeapYear(year); + } + + /** + * Checks if the day-of-month is valid for this year-month. + *

    + * This method checks whether this year and month and the input day form + * a valid date. + * + * @param dayOfMonth the day-of-month to validate, from 1 to 31, invalid value returns false + * @return true if the day is valid for this year-month + */ + public boolean isValidDay(int dayOfMonth) { + return dayOfMonth >= 1 && dayOfMonth <= lengthOfMonth(); + } + + /** + * Returns the length of the month, taking account of the year. + *

    + * This returns the length of the month in days. + * For example, a date in January would return 31. + * + * @return the length of the month in days, from 28 to 31 + */ + public int lengthOfMonth() { + return getMonth().length(isLeapYear()); + } + + /** + * Returns the length of the year. + *

    + * This returns the length of the year in days, either 365 or 366. + * + * @return 366 if the year is leap, 365 otherwise + */ + public int lengthOfYear() { + return (isLeapYear() ? 366 : 365); + } + + //----------------------------------------------------------------------- + /** + * Returns an adjusted copy of this year-month. + *

    + * This returns a new {@code YearMonth}, based on this one, with the year-month adjusted. + * The adjustment takes place using the specified adjuster strategy object. + * Read the documentation of the adjuster to understand what adjustment will be made. + *

    + * A simple adjuster might simply set the one of the fields, such as the year field. + * A more complex adjuster might set the year-month to the next month that + * Halley's comet will pass the Earth. + *

    + * The result of this method is obtained by invoking the + * {@link TemporalAdjuster#adjustInto(Temporal)} method on the + * specified adjuster passing {@code this} as the argument. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param adjuster the adjuster to use, not null + * @return a {@code YearMonth} based on {@code this} with the adjustment made, not null + * @throws DateTimeException if the adjustment cannot be made + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public YearMonth with(TemporalAdjuster adjuster) { + return (YearMonth) adjuster.adjustInto(this); + } + + /** + * Returns a copy of this year-month with the specified field set to a new value. + *

    + * This returns a new {@code YearMonth}, based on this one, with the value + * for the specified field changed. + * This can be used to change any supported field, such as the year or month. + * If it is not possible to set the value, because the field is not supported or for + * some other reason, an exception is thrown. + *

    + * If the field is a {@link ChronoField} then the adjustment is implemented here. + * The supported fields behave as follows: + *

      + *
    • {@code MONTH_OF_YEAR} - + * Returns a {@code YearMonth} with the specified month-of-year. + * The year will be unchanged. + *
    • {@code EPOCH_MONTH} - + * Returns a {@code YearMonth} with the specified epoch-month. + * This completely replaces the year and month of this object. + *
    • {@code YEAR_OF_ERA} - + * Returns a {@code YearMonth} with the specified year-of-era + * The month and era will be unchanged. + *
    • {@code YEAR} - + * Returns a {@code YearMonth} with the specified year. + * The month will be unchanged. + *
    • {@code ERA} - + * Returns a {@code YearMonth} with the specified era. + * The month and year-of-era will be unchanged. + *
    + *

    + * In all cases, if the new value is outside the valid range of values for the field + * then a {@code DateTimeException} will be thrown. + *

    + * All other {@code ChronoField} instances will throw a {@code DateTimeException}. + *

    + * If the field is not a {@code ChronoField}, then the result of this method + * is obtained by invoking {@code TemporalField.doWith(Temporal, long)} + * passing {@code this} as the argument. In this case, the field determines + * whether and how to adjust the instant. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param field the field to set in the result, not null + * @param newValue the new value of the field in the result + * @return a {@code YearMonth} based on {@code this} with the specified field set, not null + * @throws DateTimeException if the field cannot be set + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public YearMonth with(TemporalField field, long newValue) { + if (field instanceof ChronoField) { + ChronoField f = (ChronoField) field; + f.checkValidValue(newValue); + switch (f) { + case MONTH_OF_YEAR: return withMonth((int) newValue); + case EPOCH_MONTH: return plusMonths(newValue - getLong(EPOCH_MONTH)); + case YEAR_OF_ERA: return withYear((int) (year < 1 ? 1 - newValue : newValue)); + case YEAR: return withYear((int) newValue); + case ERA: return (getLong(ERA) == newValue ? this : withYear(1 - year)); + } + throw new DateTimeException("Unsupported field: " + field.getName()); + } + return field.doWith(this, newValue); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this {@code YearMonth} with the year altered. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param year the year to set in the returned year-month, from MIN_YEAR to MAX_YEAR + * @return a {@code YearMonth} based on this year-month with the requested year, not null + * @throws DateTimeException if the year value is invalid + */ + public YearMonth withYear(int year) { + YEAR.checkValidValue(year); + return with(year, month); + } + + /** + * Returns a copy of this {@code YearMonth} with the month-of-year altered. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param month the month-of-year to set in the returned year-month, from 1 (January) to 12 (December) + * @return a {@code YearMonth} based on this year-month with the requested month, not null + * @throws DateTimeException if the month-of-year value is invalid + */ + public YearMonth withMonth(int month) { + MONTH_OF_YEAR.checkValidValue(month); + return with(year, month); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this year-month with the specified period added. + *

    + * This method returns a new year-month based on this year-month with the specified period added. + * The adder is typically {@link java.time.Period} but may be any other type implementing + * the {@link TemporalAdder} interface. + * The calculation is delegated to the specified adjuster, which typically calls + * back to {@link #plus(long, TemporalUnit)}. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param adder the adder to use, not null + * @return a {@code YearMonth} based on this year-month with the addition made, not null + * @throws DateTimeException if the addition cannot be made + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public YearMonth plus(TemporalAdder adder) { + return (YearMonth) adder.addTo(this); + } + + /** + * {@inheritDoc} + * @throws DateTimeException {@inheritDoc} + * @throws ArithmeticException {@inheritDoc} + */ + @Override + public YearMonth plus(long amountToAdd, TemporalUnit unit) { + if (unit instanceof ChronoUnit) { + switch ((ChronoUnit) unit) { + case MONTHS: return plusMonths(amountToAdd); + case YEARS: return plusYears(amountToAdd); + case DECADES: return plusYears(Math.multiplyExact(amountToAdd, 10)); + case CENTURIES: return plusYears(Math.multiplyExact(amountToAdd, 100)); + case MILLENNIA: return plusYears(Math.multiplyExact(amountToAdd, 1000)); + case ERAS: return with(ERA, Math.addExact(getLong(ERA), amountToAdd)); + } + throw new DateTimeException("Unsupported unit: " + unit.getName()); + } + return unit.doPlus(this, amountToAdd); + } + + /** + * Returns a copy of this year-month with the specified period in years added. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param yearsToAdd the years to add, may be negative + * @return a {@code YearMonth} based on this year-month with the years added, not null + * @throws DateTimeException if the result exceeds the supported range + */ + public YearMonth plusYears(long yearsToAdd) { + if (yearsToAdd == 0) { + return this; + } + int newYear = YEAR.checkValidIntValue(year + yearsToAdd); // safe overflow + return with(newYear, month); + } + + /** + * Returns a copy of this year-month with the specified period in months added. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param monthsToAdd the months to add, may be negative + * @return a {@code YearMonth} based on this year-month with the months added, not null + * @throws DateTimeException if the result exceeds the supported range + */ + public YearMonth plusMonths(long monthsToAdd) { + if (monthsToAdd == 0) { + return this; + } + long monthCount = year * 12L + (month - 1); + long calcMonths = monthCount + monthsToAdd; // safe overflow + int newYear = YEAR.checkValidIntValue(Math.floorDiv(calcMonths, 12)); + int newMonth = (int)Math.floorMod(calcMonths, 12) + 1; + return with(newYear, newMonth); + } + + //----------------------------------------------------------------------- + /** + * Returns a copy of this year-month with the specified period subtracted. + *

    + * This method returns a new year-month based on this year-month with the specified period subtracted. + * The subtractor is typically {@link java.time.Period} but may be any other type implementing + * the {@link TemporalSubtractor} interface. + * The calculation is delegated to the specified adjuster, which typically calls + * back to {@link #minus(long, TemporalUnit)}. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param subtractor the subtractor to use, not null + * @return a {@code YearMonth} based on this year-month with the subtraction made, not null + * @throws DateTimeException if the subtraction cannot be made + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public YearMonth minus(TemporalSubtractor subtractor) { + return (YearMonth) subtractor.subtractFrom(this); + } + + /** + * {@inheritDoc} + * @throws DateTimeException {@inheritDoc} + * @throws ArithmeticException {@inheritDoc} + */ + @Override + public YearMonth minus(long amountToSubtract, TemporalUnit unit) { + return (amountToSubtract == Long.MIN_VALUE ? plus(Long.MAX_VALUE, unit).plus(1, unit) : plus(-amountToSubtract, unit)); + } + + /** + * Returns a copy of this year-month with the specified period in years subtracted. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param yearsToSubtract the years to subtract, may be negative + * @return a {@code YearMonth} based on this year-month with the years subtracted, not null + * @throws DateTimeException if the result exceeds the supported range + */ + public YearMonth minusYears(long yearsToSubtract) { + return (yearsToSubtract == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-yearsToSubtract)); + } + + /** + * Returns a copy of this year-month with the specified period in months subtracted. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param monthsToSubtract the months to subtract, may be negative + * @return a {@code YearMonth} based on this year-month with the months subtracted, not null + * @throws DateTimeException if the result exceeds the supported range + */ + public YearMonth minusMonths(long monthsToSubtract) { + return (monthsToSubtract == Long.MIN_VALUE ? plusMonths(Long.MAX_VALUE).plusMonths(1) : plusMonths(-monthsToSubtract)); + } + + //----------------------------------------------------------------------- + /** + * Queries this year-month using the specified query. + *

    + * This queries this year-month using the specified query strategy object. + * The {@code TemporalQuery} object defines the logic to be used to + * obtain the result. Read the documentation of the query to understand + * what the result of this method will be. + *

    + * The result of this method is obtained by invoking the + * {@link TemporalQuery#queryFrom(TemporalAccessor)} method on the + * specified query passing {@code this} as the argument. + * + * @param the type of the result + * @param query the query to invoke, not null + * @return the query result, null may be returned (defined by the query) + * @throws DateTimeException if unable to query (defined by the query) + * @throws ArithmeticException if numeric overflow occurs (defined by the query) + */ + @SuppressWarnings("unchecked") + @Override + public R query(TemporalQuery query) { + if (query == Queries.chrono()) { + return (R) ISOChrono.INSTANCE; + } else if (query == Queries.precision()) { + return (R) MONTHS; + } + return Temporal.super.query(query); + } + + /** + * Adjusts the specified temporal object to have this year-month. + *

    + * This returns a temporal object of the same observable type as the input + * with the year and month changed to be the same as this. + *

    + * The adjustment is equivalent to using {@link Temporal#with(TemporalField, long)} + * passing {@link ChronoField#EPOCH_MONTH} as the field. + * If the specified temporal object does not use the ISO calendar system then + * a {@code DateTimeException} is thrown. + *

    + * In most cases, it is clearer to reverse the calling pattern by using + * {@link Temporal#with(TemporalAdjuster)}: + *

    +     *   // these two lines are equivalent, but the second approach is recommended
    +     *   temporal = thisYearMonth.adjustInto(temporal);
    +     *   temporal = temporal.with(thisYearMonth);
    +     * 
    + *

    + * This instance is immutable and unaffected by this method call. + * + * @param temporal the target object to be adjusted, not null + * @return the adjusted object, not null + * @throws DateTimeException if unable to make the adjustment + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public Temporal adjustInto(Temporal temporal) { + if (Chrono.from(temporal).equals(ISOChrono.INSTANCE) == false) { + throw new DateTimeException("Adjustment only supported on ISO date-time"); + } + return temporal.with(EPOCH_MONTH, getEpochMonth()); + } + + /** + * Calculates the period between this year-month and another year-month in + * terms of the specified unit. + *

    + * This calculates the period between two year-months in terms of a single unit. + * The start and end points are {@code this} and the specified year-month. + * The result will be negative if the end is before the start. + * The {@code Temporal} passed to this method must be a {@code YearMonth}. + * For example, the period in years between two year-months can be calculated + * using {@code startYearMonth.periodUntil(endYearMonth, YEARS)}. + *

    + * The calculation returns a whole number, representing the number of + * complete units between the two year-months. + * For example, the period in decades between 2012-06 and 2032-05 + * will only be one decade as it is one month short of two decades. + *

    + * This method operates in association with {@link TemporalUnit#between}. + * The result of this method is a {@code long} representing the amount of + * the specified unit. By contrast, the result of {@code between} is an + * object that can be used directly in addition/subtraction: + *

    +     *   long period = start.periodUntil(end, YEARS);   // this method
    +     *   dateTime.plus(YEARS.between(start, end));      // use in plus/minus
    +     * 
    + *

    + * The calculation is implemented in this method for {@link ChronoUnit}. + * The units {@code MONTHS}, {@code YEARS}, {@code DECADES}, + * {@code CENTURIES}, {@code MILLENNIA} and {@code ERAS} are supported. + * Other {@code ChronoUnit} values will throw an exception. + *

    + * If the unit is not a {@code ChronoUnit}, then the result of this method + * is obtained by invoking {@code TemporalUnit.between(Temporal, Temporal)} + * passing {@code this} as the first argument and the input temporal as + * the second argument. + *

    + * This instance is immutable and unaffected by this method call. + * + * @param endYearMonth the end year-month, which must be a {@code YearMonth}, not null + * @param unit the unit to measure the period in, not null + * @return the amount of the period between this year-month and the end year-month + * @throws DateTimeException if the period cannot be calculated + * @throws ArithmeticException if numeric overflow occurs + */ + @Override + public long periodUntil(Temporal endYearMonth, TemporalUnit unit) { + if (endYearMonth instanceof YearMonth == false) { + Objects.requireNonNull(endYearMonth, "endYearMonth"); + throw new DateTimeException("Unable to calculate period between objects of two different types"); + } + YearMonth end = (YearMonth) endYearMonth; + if (unit instanceof ChronoUnit) { + long monthsUntil = end.getEpochMonth() - getEpochMonth(); // no overflow + switch ((ChronoUnit) unit) { + case MONTHS: return monthsUntil; + case YEARS: return monthsUntil / 12; + case DECADES: return monthsUntil / 120; + case CENTURIES: return monthsUntil / 1200; + case MILLENNIA: return monthsUntil / 12000; + case ERAS: return end.getLong(ERA) - getLong(ERA); + } + throw new DateTimeException("Unsupported unit: " + unit.getName()); + } + return unit.between(this, endYearMonth).getAmount(); + } + + //----------------------------------------------------------------------- + /** + * Returns a date formed from this year-month at the specified day-of-month. + *

    + * This combines this year-month and the specified day-of-month to form a {@code LocalDate}. + * The day-of-month value must be valid for the year-month. + *

    + * This method can be used as part of a chain to produce a date: + *

    +     *  LocalDate date = year.atMonth(month).atDay(day);
    +     * 
    + *

    + * This instance is immutable and unaffected by this method call. + * + * @param dayOfMonth the day-of-month to use, from 1 to 31 + * @return the date formed from this year-month and the specified day, not null + * @throws DateTimeException when the day is invalid for the year-month + * @see #isValidDay(int) + */ + public LocalDate atDay(int dayOfMonth) { + return LocalDate.of(year, month, dayOfMonth); + } + + //----------------------------------------------------------------------- + /** + * Compares this year-month to another year-month. + *

    + * The comparison is based first on the value of the year, then on the value of the month. + * It is "consistent with equals", as defined by {@link Comparable}. + * + * @param other the other year-month to compare to, not null + * @return the comparator value, negative if less, positive if greater + */ + @Override + public int compareTo(YearMonth other) { + int cmp = (year - other.year); + if (cmp == 0) { + cmp = (month - other.month); + } + return cmp; + } + + /** + * Is this year-month after the specified year-month. + * + * @param other the other year-month to compare to, not null + * @return true if this is after the specified year-month + */ + public boolean isAfter(YearMonth other) { + return compareTo(other) > 0; + } + + /** + * Is this year-month before the specified year-month. + * + * @param other the other year-month to compare to, not null + * @return true if this point is before the specified year-month + */ + public boolean isBefore(YearMonth other) { + return compareTo(other) < 0; + } + + //----------------------------------------------------------------------- + /** + * Checks if this year-month is equal to another year-month. + *

    + * The comparison is based on the time-line position of the year-months. + * + * @param obj the object to check, null returns false + * @return true if this is equal to the other year-month + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof YearMonth) { + YearMonth other = (YearMonth) obj; + return year == other.year && month == other.month; + } + return false; + } + + /** + * A hash code for this year-month. + * + * @return a suitable hash code + */ + @Override + public int hashCode() { + return year ^ (month << 27); + } + + //----------------------------------------------------------------------- + /** + * Outputs this year-month as a {@code String}, such as {@code 2007-12}. + *

    + * The output will be in the format {@code yyyy-MM}: + * + * @return a string representation of this year-month, not null + */ + @Override + public String toString() { + int absYear = Math.abs(year); + StringBuilder buf = new StringBuilder(9); + if (absYear < 1000) { + if (year < 0) { + buf.append(year - 10000).deleteCharAt(1); + } else { + buf.append(year + 10000).deleteCharAt(0); + } + } else { + buf.append(year); + } + return buf.append(month < 10 ? "-0" : "-") + .append(month) + .toString(); + } + + /** + * Outputs this year-month as a {@code String} using the formatter. + *

    + * This year-month will be passed to the formatter + * {@link DateTimeFormatter#print(TemporalAccessor) print method}. + * + * @param formatter the formatter to use, not null + * @return the formatted year-month string, not null + * @throws DateTimeException if an error occurs during printing + */ + public String toString(DateTimeFormatter formatter) { + Objects.requireNonNull(formatter, "formatter"); + return formatter.print(this); + } + + //----------------------------------------------------------------------- + /** + * Writes the object using a + * dedicated serialized form. + *

    +     *  out.writeByte(5);  // identifies this as a Year
    +     *  out.writeInt(year);
    +     *  out.writeByte(month);
    +     * 
    + * + * @return the instance of {@code Ser}, not null + */ + private Object writeReplace() { + return new Ser(Ser.YEAR_MONTH_TYPE, this); + } + + /** + * Defend against malicious streams. + * @return never + * @throws InvalidObjectException always + */ + private Object readResolve() throws ObjectStreamException { + throw new InvalidObjectException("Deserialization via serialization delegate"); + } + + void writeExternal(DataOutput out) throws IOException { + out.writeInt(year); + out.writeByte(month); + } + + static YearMonth readExternal(DataInput in) throws IOException { + int year = in.readInt(); + byte month = in.readByte(); + return YearMonth.of(year, month); + } + +} diff --git a/jdk/src/share/classes/java/time/temporal/package-info.java b/jdk/src/share/classes/java/time/temporal/package-info.java new file mode 100644 index 00000000000..da9a4b702e8 --- /dev/null +++ b/jdk/src/share/classes/java/time/temporal/package-info.java @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + *

    + * Access to date and time using fields and units, additional value type classes and + * base support for calendar systems other than the default ISO. + *

    + *

    + * This package expands on the base package to provide additional functionality for + * more powerful use cases. Support is included for: + *

    + *
      + *
    • Units of date-time, such as years, months, days and hours
    • + *
    • Fields of date-time, such as month-of-year, day-of-week or hour-of-day
    • + *
    • Date-time adjustment functions
    • + *
    • Different definitions of weeks
    • + *
    • Alternate calendar systems
    • + *
    • Additional value types
    • + *
    + * + *

    Fields and Units

    + *

    + * Dates and times are expressed in terms of fields and units. + * A unit is used to measure an amount of time, such as years, days or minutes. + * All units implement {@link java.time.temporal.TemporalUnit}. + * The set of well known units is defined in {@link java.time.temporal.ChronoUnit}, such as {@code DAYS}. + * The unit interface is designed to allow applications defined units. + *

    + *

    + * A field is used to express part of a larger date-time, such as year, month-of-year or second-of-minute. + * All fields implement {@link java.time.temporal.TemporalField}. + * The set of well known fields are defined in {@link java.time.temporal.ChronoField}, such as {@code HOUR_OF_DAY}. + * Additional fields are defined by {@link java.time.temporal.JulianFields}, {@link java.time.temporal.WeekFields} + * and {@link java.time.temporal.ISOFields}. + * The field interface is designed to allow applications defined fields. + *

    + *

    + * This package provides tools that allow the units and fields of date and time to be accessed + * in a general way most suited for frameworks. + * {@link java.time.temporal.Temporal} provides the abstraction for date time types that support fields. + * Its methods support getting the value of a field, creating a new date time with the value of + * a field modified, and querying for additional information, typically used to extract the offset or time-zone. + *

    + *

    + * One use of fields in application code is to retrieve fields for which there is no convenience method. + * For example, getting the day-of-month is common enough that there is a method on {@code LocalDate} + * called {@code getDayOfMonth()}. However for more unusual fields it is necessary to use the field. + * For example, {@code date.get(ChronoField.ALIGNED_WEEK_OF_MONTH)}. + * The fields also provide access to the range of valid values. + *

    + * + *

    Adjustment and Query

    + *

    + * A key part of the date-time problem space is adjusting a date to a new, related value, + * such as the "last day of the month", or "next Wednesday". + * These are modeled as functions that adjust a base date-time. + * The functions implement {@link java.time.temporal.TemporalAdjuster} and operate on {@code Temporal}. + * A set of common functions are provided in {@link java.time.temporal.Adjusters}. + * For example, to find the first occurrence of a day-of-week after a given date, use + * {@link java.time.temporal.Adjusters#next(DayOfWeek)}, such as + * {@code date.with(next(MONDAY))}. + * Applications can also define adjusters by implementing {@code TemporalAdjuster}. + *

    + *

    + * There are additional interfaces to model addition to and subtraction from a date-time. + * These are {@link java.time.temporal.TemporalAdder} and {@link java.time.temporal.TemporalSubtractor}. + *

    + *

    + * In addition to adjusting a date-time, an interface is provided to enable querying - + * {@link java.time.temporal.TemporalQuery}. + * The most common implementations of the query interface are method references. + * The {@code from(TemporalAccessor)} methods on major classes can all be used, such as + * {@code LocalDate::from} or {@code Month::from}. + * Further implementations are provided in {@link java.time.temporal.Queries}. + * Applications can also define queries by implementing {@code TemporalQuery}. + *

    + * + *

    Weeks

    + *

    + * Different locales have different definitions of the week. + * For example, in Europe the week typically starts on a Monday, while in the US it starts on a Sunday. + * The {@link java.time.temporal.WeekFields} class models this distinction. + *

    + *

    + * The ISO calendar system defines an additional week-based division of years. + * This defines a year based on whole Monday to Monday weeks. + * This is modeled in {@link java.time.temporal.ISOFields}. + *

    + * + *

    Alternate calendar systems

    + *

    + * The main API is based around the calendar system defined in ISO-8601. + * However, there are other calendar systems, and this package provides basic support for them. + * The alternate calendars are provided in the {@code java.time.calendar} package. + *

    + *

    + * A calendar system is defined by the {@link java.time.temporal.Chrono} interface, + * while a date in a calendar system is defined by the {@link java.time.temporal.ChronoLocalDate} interface. + *

    + *

    + * It is intended that applications use the main API whenever possible, including code to read and write + * from a persistent data store, such as a database, and to send dates and times across a network. + * The "chrono" classes are then used at the user interface level to deal with localized input/output. + *

    + *

    + * Using non-ISO calendar systems in an application introduces significant extra complexity. + * Ensure that the warnings and recommendations in {@code ChronoLocalDate} have been read before + * working with the "chrono" interfaces. + *

    + *

    + * This example creates and uses a date in a non-ISO calendar system. + *

    + *
    + *   // Print the Thai Buddhist date
    + *       ChronoLocalDate<ThaiBuddhistChrono> now1 = ThaiBuddhistChrono.INSTANCE.dateNow();
    + *       int day = now1.get(ChronoField.DAY_OF_MONTH);
    + *       int dow = now1.get(ChronoField.DAY_OF_WEEK);
    + *       int month = now1.get(ChronoField.MONTH_OF_YEAR);
    + *       int year = now1.get(ChronoField.YEAR);
    + *       System.out.printf("  Today is %s %s %d-%s-%d%n", now1.getChrono().getId(),
    + *                 dow, day, month, year);
    + *
    + *   // Enumerate the list of available calendars and print today for each
    + *       Set<Chrono<?>> chronos = Chrono.getAvailableChronologies();
    + *       for (Chrono<?> chrono : chronos) {
    + *         ChronoLocalDate<?> date = chrono.dateNow();
    + *         System.out.printf("   %20s: %s%n", chrono.getId(), date.toString());
    + *       }
    + *
    + *   // Print today's date and the last day of the year for the Thai Buddhist Calendar.
    + *       ChronoLocalDate<ThaiBuddhistChrono> first = now1
    + *                 .with(ChronoField.DAY_OF_MONTH, 1)
    + *                 .with(ChronoField.MONTH_OF_YEAR, 1);
    + *       ChronoLocalDate<ThaiBuddhistChrono> last = first
    + *                 .plus(1, ChronoUnit.YEARS)
    + *                 .minus(1, ChronoUnit.DAYS);
    + *       System.out.printf("  %s: 1st of year: %s; end of year: %s%n", last.getChrono().getId(),
    + *                 first, last);
    + *  
    + * + *

    Package specification

    + *

    + * Unless otherwise noted, passing a null argument to a constructor or method in any class or interface + * in this package will cause a {@link java.lang.NullPointerException NullPointerException} to be thrown. + * The Javadoc "@param" definition is used to summarise the null-behavior. + * The "@throws {@link java.lang.NullPointerException}" is not explicitly documented in each method. + *

    + *

    + * All calculations should check for numeric overflow and throw either an {@link java.lang.ArithmeticException} + * or a {@link java.time.DateTimeException}. + *

    + * @since JDK1.8 + */ +package java.time.temporal; diff --git a/jdk/src/share/classes/java/time/zone/Ser.java b/jdk/src/share/classes/java/time/zone/Ser.java new file mode 100644 index 00000000000..7d5c0c6719b --- /dev/null +++ b/jdk/src/share/classes/java/time/zone/Ser.java @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2011-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.zone; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.Externalizable; +import java.io.IOException; +import java.io.InvalidClassException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.StreamCorruptedException; +import java.time.ZoneOffset; + +/** + * The shared serialization delegate for this package. + * + *

    Implementation notes

    + * This class is mutable and should be created once per serialization. + * + * @serial include + * @since 1.8 + */ +final class Ser implements Externalizable { + + /** + * Serialization version. + */ + private static final long serialVersionUID = -8885321777449118786L; + + /** Type for ZoneRules. */ + static final byte ZRULES = 1; + /** Type for ZoneOffsetTransition. */ + static final byte ZOT = 2; + /** Type for ZoneOffsetTransition. */ + static final byte ZOTRULE = 3; + + /** The type being serialized. */ + private byte type; + /** The object being serialized. */ + private Object object; + + /** + * Constructor for deserialization. + */ + public Ser() { + } + + /** + * Creates an instance for serialization. + * + * @param type the type + * @param object the object + */ + Ser(byte type, Object object) { + this.type = type; + this.object = object; + } + + //----------------------------------------------------------------------- + /** + * Implements the {@code Externalizable} interface to write the object. + * + * @param out the data stream to write to, not null + */ + public void writeExternal(ObjectOutput out) throws IOException { + writeInternal(type, object, out); + } + + static void write(Object object, DataOutput out) throws IOException { + writeInternal(ZRULES, object, out); + } + + private static void writeInternal(byte type, Object object, DataOutput out) throws IOException { + out.writeByte(type); + switch (type) { + case ZRULES: + ((ZoneRules) object).writeExternal(out); + break; + case ZOT: + ((ZoneOffsetTransition) object).writeExternal(out); + break; + case ZOTRULE: + ((ZoneOffsetTransitionRule) object).writeExternal(out); + break; + default: + throw new InvalidClassException("Unknown serialized type"); + } + } + + //----------------------------------------------------------------------- + /** + * Implements the {@code Externalizable} interface to read the object. + * + * @param in the data to read, not null + */ + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + type = in.readByte(); + object = readInternal(type, in); + } + + static Object read(DataInput in) throws IOException, ClassNotFoundException { + byte type = in.readByte(); + return readInternal(type, in); + } + + private static Object readInternal(byte type, DataInput in) throws IOException, ClassNotFoundException { + switch (type) { + case ZRULES: + return ZoneRules.readExternal(in); + case ZOT: + return ZoneOffsetTransition.readExternal(in); + case ZOTRULE: + return ZoneOffsetTransitionRule.readExternal(in); + default: + throw new StreamCorruptedException("Unknown serialized type"); + } + } + + /** + * Returns the object that will replace this one. + * + * @return the read object, should never be null + */ + private Object readResolve() { + return object; + } + + //----------------------------------------------------------------------- + /** + * Writes the state to the stream. + * + * @param offset the offset, not null + * @param out the output stream, not null + * @throws IOException if an error occurs + */ + static void writeOffset(ZoneOffset offset, DataOutput out) throws IOException { + final int offsetSecs = offset.getTotalSeconds(); + int offsetByte = offsetSecs % 900 == 0 ? offsetSecs / 900 : 127; // compress to -72 to +72 + out.writeByte(offsetByte); + if (offsetByte == 127) { + out.writeInt(offsetSecs); + } + } + + /** + * Reads the state from the stream. + * + * @param in the input stream, not null + * @return the created object, not null + * @throws IOException if an error occurs + */ + static ZoneOffset readOffset(DataInput in) throws IOException { + int offsetByte = in.readByte(); + return (offsetByte == 127 ? ZoneOffset.ofTotalSeconds(in.readInt()) : ZoneOffset.ofTotalSeconds(offsetByte * 900)); + } + + //----------------------------------------------------------------------- + /** + * Writes the state to the stream. + * + * @param epochSec the epoch seconds, not null + * @param out the output stream, not null + * @throws IOException if an error occurs + */ + static void writeEpochSec(long epochSec, DataOutput out) throws IOException { + if (epochSec >= -4575744000L && epochSec < 10413792000L && epochSec % 900 == 0) { // quarter hours between 1825 and 2300 + int store = (int) ((epochSec + 4575744000L) / 900); + out.writeByte((store >>> 16) & 255); + out.writeByte((store >>> 8) & 255); + out.writeByte(store & 255); + } else { + out.writeByte(255); + out.writeLong(epochSec); + } + } + + /** + * Reads the state from the stream. + * + * @param in the input stream, not null + * @return the epoch seconds, not null + * @throws IOException if an error occurs + */ + static long readEpochSec(DataInput in) throws IOException { + int hiByte = in.readByte() & 255; + if (hiByte == 255) { + return in.readLong(); + } else { + int midByte = in.readByte() & 255; + int loByte = in.readByte() & 255; + long tot = ((hiByte << 16) + (midByte << 8) + loByte); + return (tot * 900) - 4575744000L; + } + } + +} diff --git a/jdk/src/share/classes/java/time/zone/TzdbZoneRulesProvider.java b/jdk/src/share/classes/java/time/zone/TzdbZoneRulesProvider.java new file mode 100644 index 00000000000..ed81a06dae2 --- /dev/null +++ b/jdk/src/share/classes/java/time/zone/TzdbZoneRulesProvider.java @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2009-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.zone; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.File; +import java.io.IOException; +import java.io.StreamCorruptedException; +import java.nio.file.FileSystems; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; +import java.time.DateTimeException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.NavigableMap; +import java.util.Objects; +import java.util.Set; +import java.util.TreeMap; +import java.util.concurrent.ConcurrentNavigableMap; +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.atomic.AtomicReferenceArray; +import java.util.zip.ZipFile; + +/** + * Loads time-zone rules for 'TZDB'. + *

    + * This class is public for the service loader to access. + * + *

    Specification for implementors

    + * This class is immutable and thread-safe. + * + * @since 1.8 + */ +final class TzdbZoneRulesProvider extends ZoneRulesProvider { + // service loader seems to need it to be public + + /** + * All the regions that are available. + */ + private final Set regionIds = new CopyOnWriteArraySet<>(); + /** + * All the versions that are available. + */ + private final ConcurrentNavigableMap versions = new ConcurrentSkipListMap<>(); + + /** + * Creates an instance. + * Created by the {@code ServiceLoader}. + * + * @throws ZoneRulesException if unable to load + */ + public TzdbZoneRulesProvider() { + super(); + if (load(ClassLoader.getSystemClassLoader()) == false) { + throw new ZoneRulesException("No time-zone rules found for 'TZDB'"); + } + } + + //----------------------------------------------------------------------- + @Override + protected Set provideZoneIds() { + return new HashSet<>(regionIds); + } + + @Override + protected ZoneRules provideRules(String zoneId) { + Objects.requireNonNull(zoneId, "zoneId"); + ZoneRules rules = versions.lastEntry().getValue().getRules(zoneId); + if (rules == null) { + throw new ZoneRulesException("Unknown time-zone ID: " + zoneId); + } + return rules; + } + + @Override + protected NavigableMap provideVersions(String zoneId) { + TreeMap map = new TreeMap<>(); + for (Version version : versions.values()) { + ZoneRules rules = version.getRules(zoneId); + if (rules != null) { + map.put(version.versionId, rules); + } + } + return map; + } + + //------------------------------------------------------------------------- + /** + * Loads the rules. + * + * @param classLoader the class loader to use, not null + * @return true if updated + * @throws ZoneRulesException if unable to load + */ + private boolean load(ClassLoader classLoader) { + Object updated = Boolean.FALSE; + try { + updated = AccessController.doPrivileged(new PrivilegedExceptionAction() { + public Object run() throws IOException, ClassNotFoundException { + File tzdbJar = null; + // TBD: workaround for now, so test/java/time tests can be + // run against Java runtime that does not have tzdb + String tzdbProp = System.getProperty("java.time.zone.tzdbjar"); + if (tzdbProp != null) { + tzdbJar = new File(tzdbProp); + } else { + String libDir = System.getProperty("java.home") + File.separator + "lib"; + try { + libDir = FileSystems.getDefault().getPath(libDir).toRealPath().toString(); + } catch(Exception e) {} + tzdbJar = new File(libDir, "tzdb.jar"); + } + try (ZipFile zf = new ZipFile(tzdbJar); + DataInputStream dis = new DataInputStream( + zf.getInputStream(zf.getEntry("TZDB.dat")))) { + Iterable loadedVersions = load(dis); + for (Version loadedVersion : loadedVersions) { + if (versions.putIfAbsent(loadedVersion.versionId, loadedVersion) != null) { + throw new DateTimeException( + "Data already loaded for TZDB time-zone rules version: " + + loadedVersion.versionId); + } + } + } + return Boolean.TRUE; + } + }); + } catch (Exception ex) { + throw new ZoneRulesException("Unable to load TZDB time-zone rules", ex); + } + return updated == Boolean.TRUE; + } + + /** + * Loads the rules from a DateInputStream, often in a jar file. + * + * @param dis the DateInputStream to load, not null + * @throws Exception if an error occurs + */ + private Iterable load(DataInputStream dis) throws ClassNotFoundException, IOException { + if (dis.readByte() != 1) { + throw new StreamCorruptedException("File format not recognised"); + } + // group + String groupId = dis.readUTF(); + if ("TZDB".equals(groupId) == false) { + throw new StreamCorruptedException("File format not recognised"); + } + // versions + int versionCount = dis.readShort(); + String[] versionArray = new String[versionCount]; + for (int i = 0; i < versionCount; i++) { + versionArray[i] = dis.readUTF(); + } + // regions + int regionCount = dis.readShort(); + String[] regionArray = new String[regionCount]; + for (int i = 0; i < regionCount; i++) { + regionArray[i] = dis.readUTF(); + } + regionIds.addAll(Arrays.asList(regionArray)); + // rules + int ruleCount = dis.readShort(); + Object[] ruleArray = new Object[ruleCount]; + for (int i = 0; i < ruleCount; i++) { + byte[] bytes = new byte[dis.readShort()]; + dis.readFully(bytes); + ruleArray[i] = bytes; + } + AtomicReferenceArray ruleData = new AtomicReferenceArray<>(ruleArray); + // link version-region-rules + Set versionSet = new HashSet(versionCount); + for (int i = 0; i < versionCount; i++) { + int versionRegionCount = dis.readShort(); + String[] versionRegionArray = new String[versionRegionCount]; + short[] versionRulesArray = new short[versionRegionCount]; + for (int j = 0; j < versionRegionCount; j++) { + versionRegionArray[j] = regionArray[dis.readShort()]; + versionRulesArray[j] = dis.readShort(); + } + versionSet.add(new Version(versionArray[i], versionRegionArray, versionRulesArray, ruleData)); + } + return versionSet; + } + + @Override + public String toString() { + return "TZDB"; + } + + //----------------------------------------------------------------------- + /** + * A version of the TZDB rules. + */ + static class Version { + private final String versionId; + private final String[] regionArray; + private final short[] ruleIndices; + private final AtomicReferenceArray ruleData; + + Version(String versionId, String[] regionIds, short[] ruleIndices, AtomicReferenceArray ruleData) { + this.ruleData = ruleData; + this.versionId = versionId; + this.regionArray = regionIds; + this.ruleIndices = ruleIndices; + } + + ZoneRules getRules(String regionId) { + int regionIndex = Arrays.binarySearch(regionArray, regionId); + if (regionIndex < 0) { + return null; + } + try { + return createRule(ruleIndices[regionIndex]); + } catch (Exception ex) { + throw new ZoneRulesException("Invalid binary time-zone data: TZDB:" + regionId + ", version: " + versionId, ex); + } + } + + ZoneRules createRule(short index) throws Exception { + Object obj = ruleData.get(index); + if (obj instanceof byte[]) { + byte[] bytes = (byte[]) obj; + DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes)); + obj = Ser.read(dis); + ruleData.set(index, obj); + } + return (ZoneRules) obj; + } + + @Override + public String toString() { + return versionId; + } + } + +} diff --git a/jdk/src/share/classes/java/time/zone/ZoneOffsetTransition.java b/jdk/src/share/classes/java/time/zone/ZoneOffsetTransition.java new file mode 100644 index 00000000000..6d080aba27d --- /dev/null +++ b/jdk/src/share/classes/java/time/zone/ZoneOffsetTransition.java @@ -0,0 +1,431 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2009-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.zone; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.io.Serializable; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * A transition between two offsets caused by a discontinuity in the local time-line. + *

    + * A transition between two offsets is normally the result of a daylight savings cutover. + * The discontinuity is normally a gap in spring and an overlap in autumn. + * {@code ZoneOffsetTransition} models the transition between the two offsets. + *

    + * Gaps occur where there are local date-times that simply do not not exist. + * An example would be when the offset changes from {@code +03:00} to {@code +04:00}. + * This might be described as 'the clocks will move forward one hour tonight at 1am'. + *

    + * Overlaps occur where there are local date-times that exist twice. + * An example would be when the offset changes from {@code +04:00} to {@code +03:00}. + * This might be described as 'the clocks will move back one hour tonight at 2am'. + * + *

    Specification for implementors

    + * This class is immutable and thread-safe. + * + * @since 1.8 + */ +public final class ZoneOffsetTransition + implements Comparable, Serializable { + + /** + * Serialization version. + */ + private static final long serialVersionUID = -6946044323557704546L; + /** + * The local transition date-time at the transition. + */ + private final LocalDateTime transition; + /** + * The offset before transition. + */ + private final ZoneOffset offsetBefore; + /** + * The offset after transition. + */ + private final ZoneOffset offsetAfter; + + //----------------------------------------------------------------------- + /** + * Obtains an instance defining a transition between two offsets. + *

    + * Applications should normally obtain an instance from {@link ZoneRules}. + * This factory is only intended for use when creating {@link ZoneRules}. + * + * @param transition the transition date-time at the transition, which never + * actually occurs, expressed local to the before offset, not null + * @param offsetBefore the offset before the transition, not null + * @param offsetAfter the offset at and after the transition, not null + * @return the transition, not null + * @throws IllegalArgumentException if {@code offsetBefore} and {@code offsetAfter} + * are equal, or {@code transition.getNano()} returns non-zero value + */ + public static ZoneOffsetTransition of(LocalDateTime transition, ZoneOffset offsetBefore, ZoneOffset offsetAfter) { + Objects.requireNonNull(transition, "transition"); + Objects.requireNonNull(offsetBefore, "offsetBefore"); + Objects.requireNonNull(offsetAfter, "offsetAfter"); + if (offsetBefore.equals(offsetAfter)) { + throw new IllegalArgumentException("Offsets must not be equal"); + } + if (transition.getNano() != 0) { + throw new IllegalArgumentException("Nano-of-second must be zero"); + } + return new ZoneOffsetTransition(transition, offsetBefore, offsetAfter); + } + + /** + * Creates an instance defining a transition between two offsets. + * + * @param transition the transition date-time with the offset before the transition, not null + * @param offsetBefore the offset before the transition, not null + * @param offsetAfter the offset at and after the transition, not null + */ + ZoneOffsetTransition(LocalDateTime transition, ZoneOffset offsetBefore, ZoneOffset offsetAfter) { + this.transition = transition; + this.offsetBefore = offsetBefore; + this.offsetAfter = offsetAfter; + } + + /** + * Creates an instance from epoch-second and offsets. + * + * @param epochSecond the transition epoch-second + * @param offsetBefore the offset before the transition, not null + * @param offsetAfter the offset at and after the transition, not null + */ + ZoneOffsetTransition(long epochSecond, ZoneOffset offsetBefore, ZoneOffset offsetAfter) { + this.transition = LocalDateTime.ofEpochSecond(epochSecond, 0, offsetBefore); + this.offsetBefore = offsetBefore; + this.offsetAfter = offsetAfter; + } + + //----------------------------------------------------------------------- + /** + * Uses a serialization delegate. + * + * @return the replacing object, not null + */ + private Object writeReplace() { + return new Ser(Ser.ZOT, this); + } + + /** + * Writes the state to the stream. + * + * @param out the output stream, not null + * @throws IOException if an error occurs + */ + void writeExternal(DataOutput out) throws IOException { + Ser.writeEpochSec(toEpochSecond(), out); + Ser.writeOffset(offsetBefore, out); + Ser.writeOffset(offsetAfter, out); + } + + /** + * Reads the state from the stream. + * + * @param in the input stream, not null + * @return the created object, not null + * @throws IOException if an error occurs + */ + static ZoneOffsetTransition readExternal(DataInput in) throws IOException { + long epochSecond = Ser.readEpochSec(in); + ZoneOffset before = Ser.readOffset(in); + ZoneOffset after = Ser.readOffset(in); + if (before.equals(after)) { + throw new IllegalArgumentException("Offsets must not be equal"); + } + return new ZoneOffsetTransition(epochSecond, before, after); + } + + //----------------------------------------------------------------------- + /** + * Gets the transition instant. + *

    + * This is the instant of the discontinuity, which is defined as the first + * instant that the 'after' offset applies. + *

    + * The methods {@link #getInstant()}, {@link #getDateTimeBefore()} and {@link #getDateTimeAfter()} + * all represent the same instant. + * + * @return the transition instant, not null + */ + public Instant getInstant() { + return transition.toInstant(offsetBefore); + } + + /** + * Gets the transition instant as an epoch second. + * + * @return the transition epoch second + */ + public long toEpochSecond() { + return transition.toEpochSecond(offsetBefore); + } + + //------------------------------------------------------------------------- + /** + * Gets the local transition date-time, as would be expressed with the 'before' offset. + *

    + * This is the date-time where the discontinuity begins expressed with the 'before' offset. + * At this instant, the 'after' offset is actually used, therefore the combination of this + * date-time and the 'before' offset will never occur. + *

    + * The combination of the 'before' date-time and offset represents the same instant + * as the 'after' date-time and offset. + * + * @return the transition date-time expressed with the before offset, not null + */ + public LocalDateTime getDateTimeBefore() { + return transition; + } + + /** + * Gets the local transition date-time, as would be expressed with the 'after' offset. + *

    + * This is the first date-time after the discontinuity, when the new offset applies. + *

    + * The combination of the 'before' date-time and offset represents the same instant + * as the 'after' date-time and offset. + * + * @return the transition date-time expressed with the after offset, not null + */ + public LocalDateTime getDateTimeAfter() { + return transition.plusSeconds(getDurationSeconds()); + } + + /** + * Gets the offset before the transition. + *

    + * This is the offset in use before the instant of the transition. + * + * @return the offset before the transition, not null + */ + public ZoneOffset getOffsetBefore() { + return offsetBefore; + } + + /** + * Gets the offset after the transition. + *

    + * This is the offset in use on and after the instant of the transition. + * + * @return the offset after the transition, not null + */ + public ZoneOffset getOffsetAfter() { + return offsetAfter; + } + + /** + * Gets the duration of the transition. + *

    + * In most cases, the transition duration is one hour, however this is not always the case. + * The duration will be positive for a gap and negative for an overlap. + * Time-zones are second-based, so the nanosecond part of the duration will be zero. + * + * @return the duration of the transition, positive for gaps, negative for overlaps + */ + public Duration getDuration() { + return Duration.ofSeconds(getDurationSeconds()); + } + + /** + * Gets the duration of the transition in seconds. + * + * @return the duration in seconds + */ + private int getDurationSeconds() { + return getOffsetAfter().getTotalSeconds() - getOffsetBefore().getTotalSeconds(); + } + + /** + * Does this transition represent a gap in the local time-line. + *

    + * Gaps occur where there are local date-times that simply do not not exist. + * An example would be when the offset changes from {@code +01:00} to {@code +02:00}. + * This might be described as 'the clocks will move forward one hour tonight at 1am'. + * + * @return true if this transition is a gap, false if it is an overlap + */ + public boolean isGap() { + return getOffsetAfter().getTotalSeconds() > getOffsetBefore().getTotalSeconds(); + } + + /** + * Does this transition represent a gap in the local time-line. + *

    + * Overlaps occur where there are local date-times that exist twice. + * An example would be when the offset changes from {@code +02:00} to {@code +01:00}. + * This might be described as 'the clocks will move back one hour tonight at 2am'. + * + * @return true if this transition is an overlap, false if it is a gap + */ + public boolean isOverlap() { + return getOffsetAfter().getTotalSeconds() < getOffsetBefore().getTotalSeconds(); + } + + /** + * Checks if the specified offset is valid during this transition. + *

    + * This checks to see if the given offset will be valid at some point in the transition. + * A gap will always return false. + * An overlap will return true if the offset is either the before or after offset. + * + * @param offset the offset to check, null returns false + * @return true if the offset is valid during the transition + */ + public boolean isValidOffset(ZoneOffset offset) { + return isGap() ? false : (getOffsetBefore().equals(offset) || getOffsetAfter().equals(offset)); + } + + /** + * Gets the valid offsets during this transition. + *

    + * A gap will return an empty list, while an overlap will return both offsets. + * + * @return the list of valid offsets + */ + List getValidOffsets() { + if (isGap()) { + return Collections.emptyList(); + } + return Arrays.asList(getOffsetBefore(), getOffsetAfter()); + } + + //----------------------------------------------------------------------- + /** + * Compares this transition to another based on the transition instant. + *

    + * This compares the instants of each transition. + * The offsets are ignored, making this order inconsistent with equals. + * + * @param transition the transition to compare to, not null + * @return the comparator value, negative if less, positive if greater + */ + @Override + public int compareTo(ZoneOffsetTransition transition) { + return this.getInstant().compareTo(transition.getInstant()); + } + + //----------------------------------------------------------------------- + /** + * Checks if this object equals another. + *

    + * The entire state of the object is compared. + * + * @param other the other object to compare to, null returns false + * @return true if equal + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (other instanceof ZoneOffsetTransition) { + ZoneOffsetTransition d = (ZoneOffsetTransition) other; + return transition.equals(d.transition) && + offsetBefore.equals(d.offsetBefore) && offsetAfter.equals(d.offsetAfter); + } + return false; + } + + /** + * Returns a suitable hash code. + * + * @return the hash code + */ + @Override + public int hashCode() { + return transition.hashCode() ^ offsetBefore.hashCode() ^ Integer.rotateLeft(offsetAfter.hashCode(), 16); + } + + //----------------------------------------------------------------------- + /** + * Returns a string describing this object. + * + * @return a string for debugging, not null + */ + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append("Transition[") + .append(isGap() ? "Gap" : "Overlap") + .append(" at ") + .append(transition) + .append(offsetBefore) + .append(" to ") + .append(offsetAfter) + .append(']'); + return buf.toString(); + } + +} diff --git a/jdk/src/share/classes/java/time/zone/ZoneOffsetTransitionRule.java b/jdk/src/share/classes/java/time/zone/ZoneOffsetTransitionRule.java new file mode 100644 index 00000000000..4b6f7e90722 --- /dev/null +++ b/jdk/src/share/classes/java/time/zone/ZoneOffsetTransitionRule.java @@ -0,0 +1,575 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2009-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.zone; + +import static java.time.temporal.Adjusters.nextOrSame; +import static java.time.temporal.Adjusters.previousOrSame; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.io.Serializable; +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Month; +import java.time.ZoneOffset; +import java.time.temporal.ISOChrono; +import java.util.Objects; + +/** + * A rule expressing how to create a transition. + *

    + * This class allows rules for identifying future transitions to be expressed. + * A rule might be written in many forms: + *

      + *
    • the 16th March + *
    • the Sunday on or after the 16th March + *
    • the Sunday on or before the 16th March + *
    • the last Sunday in February + *

    + * These different rule types can be expressed and queried. + * + *

    Specification for implementors

    + * This class is immutable and thread-safe. + * + * @since 1.8 + */ +public final class ZoneOffsetTransitionRule implements Serializable { + + /** + * Serialization version. + */ + private static final long serialVersionUID = 6889046316657758795L; + + /** + * The month of the month-day of the first day of the cutover week. + * The actual date will be adjusted by the dowChange field. + */ + private final Month month; + /** + * The day-of-month of the month-day of the cutover week. + * If positive, it is the start of the week where the cutover can occur. + * If negative, it represents the end of the week where cutover can occur. + * The value is the number of days from the end of the month, such that + * {@code -1} is the last day of the month, {@code -2} is the second + * to last day, and so on. + */ + private final byte dom; + /** + * The cutover day-of-week, null to retain the day-of-month. + */ + private final DayOfWeek dow; + /** + * The cutover time in the 'before' offset. + */ + private final LocalTime time; + /** + * Whether the cutover time is midnight at the end of day. + */ + private final boolean timeEndOfDay; + /** + * The definition of how the local time should be interpreted. + */ + private final TimeDefinition timeDefinition; + /** + * The standard offset at the cutover. + */ + private final ZoneOffset standardOffset; + /** + * The offset before the cutover. + */ + private final ZoneOffset offsetBefore; + /** + * The offset after the cutover. + */ + private final ZoneOffset offsetAfter; + + /** + * Obtains an instance defining the yearly rule to create transitions between two offsets. + *

    + * Applications should normally obtain an instance from {@link ZoneRules}. + * This factory is only intended for use when creating {@link ZoneRules}. + * + * @param month the month of the month-day of the first day of the cutover week, not null + * @param dayOfMonthIndicator the day of the month-day of the cutover week, positive if the week is that + * day or later, negative if the week is that day or earlier, counting from the last day of the month, + * from -28 to 31 excluding 0 + * @param dayOfWeek the required day-of-week, null if the month-day should not be changed + * @param time the cutover time in the 'before' offset, not null + * @param timeEndOfDay whether the time is midnight at the end of day + * @param timeDefnition how to interpret the cutover + * @param standardOffset the standard offset in force at the cutover, not null + * @param offsetBefore the offset before the cutover, not null + * @param offsetAfter the offset after the cutover, not null + * @return the rule, not null + * @throws IllegalArgumentException if the day of month indicator is invalid + * @throws IllegalArgumentException if the end of day flag is true when the time is not midnight + */ + public static ZoneOffsetTransitionRule of( + Month month, + int dayOfMonthIndicator, + DayOfWeek dayOfWeek, + LocalTime time, + boolean timeEndOfDay, + TimeDefinition timeDefnition, + ZoneOffset standardOffset, + ZoneOffset offsetBefore, + ZoneOffset offsetAfter) { + Objects.requireNonNull(month, "month"); + Objects.requireNonNull(time, "time"); + Objects.requireNonNull(timeDefnition, "timeDefnition"); + Objects.requireNonNull(standardOffset, "standardOffset"); + Objects.requireNonNull(offsetBefore, "offsetBefore"); + Objects.requireNonNull(offsetAfter, "offsetAfter"); + if (dayOfMonthIndicator < -28 || dayOfMonthIndicator > 31 || dayOfMonthIndicator == 0) { + throw new IllegalArgumentException("Day of month indicator must be between -28 and 31 inclusive excluding zero"); + } + if (timeEndOfDay && time.equals(LocalTime.MIDNIGHT) == false) { + throw new IllegalArgumentException("Time must be midnight when end of day flag is true"); + } + return new ZoneOffsetTransitionRule(month, dayOfMonthIndicator, dayOfWeek, time, timeEndOfDay, timeDefnition, standardOffset, offsetBefore, offsetAfter); + } + + /** + * Creates an instance defining the yearly rule to create transitions between two offsets. + * + * @param month the month of the month-day of the first day of the cutover week, not null + * @param dayOfMonthIndicator the day of the month-day of the cutover week, positive if the week is that + * day or later, negative if the week is that day or earlier, counting from the last day of the month, + * from -28 to 31 excluding 0 + * @param dayOfWeek the required day-of-week, null if the month-day should not be changed + * @param time the cutover time in the 'before' offset, not null + * @param timeEndOfDay whether the time is midnight at the end of day + * @param timeDefnition how to interpret the cutover + * @param standardOffset the standard offset in force at the cutover, not null + * @param offsetBefore the offset before the cutover, not null + * @param offsetAfter the offset after the cutover, not null + * @throws IllegalArgumentException if the day of month indicator is invalid + * @throws IllegalArgumentException if the end of day flag is true when the time is not midnight + */ + ZoneOffsetTransitionRule( + Month month, + int dayOfMonthIndicator, + DayOfWeek dayOfWeek, + LocalTime time, + boolean timeEndOfDay, + TimeDefinition timeDefnition, + ZoneOffset standardOffset, + ZoneOffset offsetBefore, + ZoneOffset offsetAfter) { + this.month = month; + this.dom = (byte) dayOfMonthIndicator; + this.dow = dayOfWeek; + this.time = time; + this.timeEndOfDay = timeEndOfDay; + this.timeDefinition = timeDefnition; + this.standardOffset = standardOffset; + this.offsetBefore = offsetBefore; + this.offsetAfter = offsetAfter; + } + + //----------------------------------------------------------------------- + /** + * Uses a serialization delegate. + * + * @return the replacing object, not null + */ + private Object writeReplace() { + return new Ser(Ser.ZOTRULE, this); + } + + /** + * Writes the state to the stream. + * + * @param out the output stream, not null + * @throws IOException if an error occurs + */ + void writeExternal(DataOutput out) throws IOException { + final int timeSecs = (timeEndOfDay ? 86400 : time.toSecondOfDay()); + final int stdOffset = standardOffset.getTotalSeconds(); + final int beforeDiff = offsetBefore.getTotalSeconds() - stdOffset; + final int afterDiff = offsetAfter.getTotalSeconds() - stdOffset; + final int timeByte = (timeSecs % 3600 == 0 ? (timeEndOfDay ? 24 : time.getHour()) : 31); + final int stdOffsetByte = (stdOffset % 900 == 0 ? stdOffset / 900 + 128 : 255); + final int beforeByte = (beforeDiff == 0 || beforeDiff == 1800 || beforeDiff == 3600 ? beforeDiff / 1800 : 3); + final int afterByte = (afterDiff == 0 || afterDiff == 1800 || afterDiff == 3600 ? afterDiff / 1800 : 3); + final int dowByte = (dow == null ? 0 : dow.getValue()); + int b = (month.getValue() << 28) + // 4 bits + ((dom + 32) << 22) + // 6 bits + (dowByte << 19) + // 3 bits + (timeByte << 14) + // 5 bits + (timeDefinition.ordinal() << 12) + // 2 bits + (stdOffsetByte << 4) + // 8 bits + (beforeByte << 2) + // 2 bits + afterByte; // 2 bits + out.writeInt(b); + if (timeByte == 31) { + out.writeInt(timeSecs); + } + if (stdOffsetByte == 255) { + out.writeInt(stdOffset); + } + if (beforeByte == 3) { + out.writeInt(offsetBefore.getTotalSeconds()); + } + if (afterByte == 3) { + out.writeInt(offsetAfter.getTotalSeconds()); + } + } + + /** + * Reads the state from the stream. + * + * @param in the input stream, not null + * @return the created object, not null + * @throws IOException if an error occurs + */ + static ZoneOffsetTransitionRule readExternal(DataInput in) throws IOException { + int data = in.readInt(); + Month month = Month.of(data >>> 28); + int dom = ((data & (63 << 22)) >>> 22) - 32; + int dowByte = (data & (7 << 19)) >>> 19; + DayOfWeek dow = dowByte == 0 ? null : DayOfWeek.of(dowByte); + int timeByte = (data & (31 << 14)) >>> 14; + TimeDefinition defn = TimeDefinition.values()[(data & (3 << 12)) >>> 12]; + int stdByte = (data & (255 << 4)) >>> 4; + int beforeByte = (data & (3 << 2)) >>> 2; + int afterByte = (data & 3); + LocalTime time = (timeByte == 31 ? LocalTime.ofSecondOfDay(in.readInt()) : LocalTime.of(timeByte % 24, 0)); + ZoneOffset std = (stdByte == 255 ? ZoneOffset.ofTotalSeconds(in.readInt()) : ZoneOffset.ofTotalSeconds((stdByte - 128) * 900)); + ZoneOffset before = (beforeByte == 3 ? ZoneOffset.ofTotalSeconds(in.readInt()) : ZoneOffset.ofTotalSeconds(std.getTotalSeconds() + beforeByte * 1800)); + ZoneOffset after = (afterByte == 3 ? ZoneOffset.ofTotalSeconds(in.readInt()) : ZoneOffset.ofTotalSeconds(std.getTotalSeconds() + afterByte * 1800)); + return ZoneOffsetTransitionRule.of(month, dom, dow, time, timeByte == 24, defn, std, before, after); + } + + //----------------------------------------------------------------------- + /** + * Gets the month of the transition. + *

    + * If the rule defines an exact date then the month is the month of that date. + *

    + * If the rule defines a week where the transition might occur, then the month + * if the month of either the earliest or latest possible date of the cutover. + * + * @return the month of the transition, not null + */ + public Month getMonth() { + return month; + } + + /** + * Gets the indicator of the day-of-month of the transition. + *

    + * If the rule defines an exact date then the day is the month of that date. + *

    + * If the rule defines a week where the transition might occur, then the day + * defines either the start of the end of the transition week. + *

    + * If the value is positive, then it represents a normal day-of-month, and is the + * earliest possible date that the transition can be. + * The date may refer to 29th February which should be treated as 1st March in non-leap years. + *

    + * If the value is negative, then it represents the number of days back from the + * end of the month where {@code -1} is the last day of the month. + * In this case, the day identified is the latest possible date that the transition can be. + * + * @return the day-of-month indicator, from -28 to 31 excluding 0 + */ + public int getDayOfMonthIndicator() { + return dom; + } + + /** + * Gets the day-of-week of the transition. + *

    + * If the rule defines an exact date then this returns null. + *

    + * If the rule defines a week where the cutover might occur, then this method + * returns the day-of-week that the month-day will be adjusted to. + * If the day is positive then the adjustment is later. + * If the day is negative then the adjustment is earlier. + * + * @return the day-of-week that the transition occurs, null if the rule defines an exact date + */ + public DayOfWeek getDayOfWeek() { + return dow; + } + + /** + * Gets the local time of day of the transition which must be checked with + * {@link #isMidnightEndOfDay()}. + *

    + * The time is converted into an instant using the time definition. + * + * @return the local time of day of the transition, not null + */ + public LocalTime getLocalTime() { + return time; + } + + /** + * Is the transition local time midnight at the end of day. + *

    + * The transition may be represented as occurring at 24:00. + * + * @return whether a local time of midnight is at the start or end of the day + */ + public boolean isMidnightEndOfDay() { + return timeEndOfDay; + } + + /** + * Gets the time definition, specifying how to convert the time to an instant. + *

    + * The local time can be converted to an instant using the standard offset, + * the wall offset or UTC. + * + * @return the time definition, not null + */ + public TimeDefinition getTimeDefinition() { + return timeDefinition; + } + + /** + * Gets the standard offset in force at the transition. + * + * @return the standard offset, not null + */ + public ZoneOffset getStandardOffset() { + return standardOffset; + } + + /** + * Gets the offset before the transition. + * + * @return the offset before, not null + */ + public ZoneOffset getOffsetBefore() { + return offsetBefore; + } + + /** + * Gets the offset after the transition. + * + * @return the offset after, not null + */ + public ZoneOffset getOffsetAfter() { + return offsetAfter; + } + + //----------------------------------------------------------------------- + /** + * Creates a transition instance for the specified year. + *

    + * Calculations are performed using the ISO-8601 chronology. + * + * @param year the year to create a transition for, not null + * @return the transition instance, not null + */ + public ZoneOffsetTransition createTransition(int year) { + LocalDate date; + if (dom < 0) { + date = LocalDate.of(year, month, month.length(ISOChrono.INSTANCE.isLeapYear(year)) + 1 + dom); + if (dow != null) { + date = date.with(previousOrSame(dow)); + } + } else { + date = LocalDate.of(year, month, dom); + if (dow != null) { + date = date.with(nextOrSame(dow)); + } + } + if (timeEndOfDay) { + date = date.plusDays(1); + } + LocalDateTime localDT = LocalDateTime.of(date, time); + LocalDateTime transition = timeDefinition.createDateTime(localDT, standardOffset, offsetBefore); + return new ZoneOffsetTransition(transition, offsetBefore, offsetAfter); + } + + //----------------------------------------------------------------------- + /** + * Checks if this object equals another. + *

    + * The entire state of the object is compared. + * + * @param otherRule the other object to compare to, null returns false + * @return true if equal + */ + @Override + public boolean equals(Object otherRule) { + if (otherRule == this) { + return true; + } + if (otherRule instanceof ZoneOffsetTransitionRule) { + ZoneOffsetTransitionRule other = (ZoneOffsetTransitionRule) otherRule; + return month == other.month && dom == other.dom && dow == other.dow && + timeDefinition == other.timeDefinition && + time.equals(other.time) && + timeEndOfDay == other.timeEndOfDay && + standardOffset.equals(other.standardOffset) && + offsetBefore.equals(other.offsetBefore) && + offsetAfter.equals(other.offsetAfter); + } + return false; + } + + /** + * Returns a suitable hash code. + * + * @return the hash code + */ + @Override + public int hashCode() { + int hash = ((time.toSecondOfDay() + (timeEndOfDay ? 1 : 0)) << 15) + + (month.ordinal() << 11) + ((dom + 32) << 5) + + ((dow == null ? 7 : dow.ordinal()) << 2) + (timeDefinition.ordinal()); + return hash ^ standardOffset.hashCode() ^ + offsetBefore.hashCode() ^ offsetAfter.hashCode(); + } + + //----------------------------------------------------------------------- + /** + * Returns a string describing this object. + * + * @return a string for debugging, not null + */ + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append("TransitionRule[") + .append(offsetBefore.compareTo(offsetAfter) > 0 ? "Gap " : "Overlap ") + .append(offsetBefore).append(" to ").append(offsetAfter).append(", "); + if (dow != null) { + if (dom == -1) { + buf.append(dow.name()).append(" on or before last day of ").append(month.name()); + } else if (dom < 0) { + buf.append(dow.name()).append(" on or before last day minus ").append(-dom - 1).append(" of ").append(month.name()); + } else { + buf.append(dow.name()).append(" on or after ").append(month.name()).append(' ').append(dom); + } + } else { + buf.append(month.name()).append(' ').append(dom); + } + buf.append(" at ").append(timeEndOfDay ? "24:00" : time.toString()) + .append(" ").append(timeDefinition) + .append(", standard offset ").append(standardOffset) + .append(']'); + return buf.toString(); + } + + //----------------------------------------------------------------------- + /** + * A definition of the way a local time can be converted to the actual + * transition date-time. + *

    + * Time zone rules are expressed in one of three ways: + *

      + *
    • Relative to UTC
    • + *
    • Relative to the standard offset in force
    • + *
    • Relative to the wall offset (what you would see on a clock on the wall)
    • + *

    + */ + public static enum TimeDefinition { + /** The local date-time is expressed in terms of the UTC offset. */ + UTC, + /** The local date-time is expressed in terms of the wall offset. */ + WALL, + /** The local date-time is expressed in terms of the standard offset. */ + STANDARD; + + /** + * Converts the specified local date-time to the local date-time actually + * seen on a wall clock. + *

    + * This method converts using the type of this enum. + * The output is defined relative to the 'before' offset of the transition. + *

    + * The UTC type uses the UTC offset. + * The STANDARD type uses the standard offset. + * The WALL type returns the input date-time. + * The result is intended for use with the wall-offset. + * + * @param dateTime the local date-time, not null + * @param standardOffset the standard offset, not null + * @param wallOffset the wall offset, not null + * @return the date-time relative to the wall/before offset, not null + */ + public LocalDateTime createDateTime(LocalDateTime dateTime, ZoneOffset standardOffset, ZoneOffset wallOffset) { + switch (this) { + case UTC: { + int difference = wallOffset.getTotalSeconds() - ZoneOffset.UTC.getTotalSeconds(); + return dateTime.plusSeconds(difference); + } + case STANDARD: { + int difference = wallOffset.getTotalSeconds() - standardOffset.getTotalSeconds(); + return dateTime.plusSeconds(difference); + } + default: // WALL + return dateTime; + } + } + } + +} diff --git a/jdk/src/share/classes/java/time/zone/ZoneRules.java b/jdk/src/share/classes/java/time/zone/ZoneRules.java new file mode 100644 index 00000000000..159d6e0d36a --- /dev/null +++ b/jdk/src/share/classes/java/time/zone/ZoneRules.java @@ -0,0 +1,959 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2009-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.zone; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.io.Serializable; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.temporal.Year; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * The rules defining how the zone offset varies for a single time-zone. + *

    + * The rules model all the historic and future transitions for a time-zone. + * {@link ZoneOffsetTransition} is used for known transitions, typically historic. + * {@link ZoneOffsetTransitionRule} is used for future transitions that are based + * on the result of an algorithm. + *

    + * The rules are loaded via {@link ZoneRulesProvider} using a {@link ZoneId}. + * The same rules may be shared internally between multiple zone IDs. + *

    + * Serializing an instance of {@code ZoneRules} will store the entire set of rules. + * It does not store the zone ID as it is not part of the state of this object. + *

    + * A rule implementation may or may not store full information about historic + * and future transitions, and the information stored is only as accurate as + * that supplied to the implementation by the rules provider. + * Applications should treat the data provided as representing the best information + * available to the implementation of this rule. + * + *

    Specification for implementors

    + * This class is immutable and thread-safe. + * + * @since 1.8 + */ +public final class ZoneRules implements Serializable { + + /** + * Serialization version. + */ + private static final long serialVersionUID = 3044319355680032515L; + /** + * The last year to have its transitions cached. + */ + private static final int LAST_CACHED_YEAR = 2100; + + /** + * The transitions between standard offsets (epoch seconds), sorted. + */ + private final long[] standardTransitions; + /** + * The standard offsets. + */ + private final ZoneOffset[] standardOffsets; + /** + * The transitions between instants (epoch seconds), sorted. + */ + private final long[] savingsInstantTransitions; + /** + * The transitions between local date-times, sorted. + * This is a paired array, where the first entry is the start of the transition + * and the second entry is the end of the transition. + */ + private final LocalDateTime[] savingsLocalTransitions; + /** + * The wall offsets. + */ + private final ZoneOffset[] wallOffsets; + /** + * The last rule. + */ + private final ZoneOffsetTransitionRule[] lastRules; + /** + * The map of recent transitions. + */ + private final ConcurrentMap lastRulesCache = + new ConcurrentHashMap(); + /** + * The zero-length long array. + */ + private static final long[] EMPTY_LONG_ARRAY = new long[0]; + /** + * The zero-length lastrules array. + */ + private static final ZoneOffsetTransitionRule[] EMPTY_LASTRULES = + new ZoneOffsetTransitionRule[0]; + /** + * The zero-length ldt array. + */ + private static final LocalDateTime[] EMPTY_LDT_ARRAY = new LocalDateTime[0]; + + /** + * Obtains an instance of a ZoneRules. + * + * @param baseStandardOffset the standard offset to use before legal rules were set, not null + * @param baseWallOffset the wall offset to use before legal rules were set, not null + * @param standardOffsetTransitionList the list of changes to the standard offset, not null + * @param transitionList the list of transitions, not null + * @param lastRules the recurring last rules, size 16 or less, not null + * @return the zone rules, not null + */ + public static ZoneRules of(ZoneOffset baseStandardOffset, + ZoneOffset baseWallOffset, + List standardOffsetTransitionList, + List transitionList, + List lastRules) { + Objects.requireNonNull(baseStandardOffset, "baseStandardOffset"); + Objects.requireNonNull(baseWallOffset, "baseWallOffset"); + Objects.requireNonNull(standardOffsetTransitionList, "standardOffsetTransitionList"); + Objects.requireNonNull(transitionList, "transitionList"); + Objects.requireNonNull(lastRules, "lastRules"); + return new ZoneRules(baseStandardOffset, baseWallOffset, + standardOffsetTransitionList, transitionList, lastRules); + } + + /** + * Obtains an instance of ZoneRules that has fixed zone rules. + * + * @param offset the offset this fixed zone rules is based on, not null + * @return the zone rules, not null + * @see #isFixedOffset() + */ + public static ZoneRules of(ZoneOffset offset) { + Objects.requireNonNull(offset, "offset"); + return new ZoneRules(offset); + } + + /** + * Creates an instance. + * + * @param baseStandardOffset the standard offset to use before legal rules were set, not null + * @param baseWallOffset the wall offset to use before legal rules were set, not null + * @param standardOffsetTransitionList the list of changes to the standard offset, not null + * @param transitionList the list of transitions, not null + * @param lastRules the recurring last rules, size 16 or less, not null + */ + ZoneRules(ZoneOffset baseStandardOffset, + ZoneOffset baseWallOffset, + List standardOffsetTransitionList, + List transitionList, + List lastRules) { + super(); + + // convert standard transitions + + this.standardTransitions = new long[standardOffsetTransitionList.size()]; + + this.standardOffsets = new ZoneOffset[standardOffsetTransitionList.size() + 1]; + this.standardOffsets[0] = baseStandardOffset; + for (int i = 0; i < standardOffsetTransitionList.size(); i++) { + this.standardTransitions[i] = standardOffsetTransitionList.get(i).toEpochSecond(); + this.standardOffsets[i + 1] = standardOffsetTransitionList.get(i).getOffsetAfter(); + } + + // convert savings transitions to locals + List localTransitionList = new ArrayList<>(); + List localTransitionOffsetList = new ArrayList<>(); + localTransitionOffsetList.add(baseWallOffset); + for (ZoneOffsetTransition trans : transitionList) { + if (trans.isGap()) { + localTransitionList.add(trans.getDateTimeBefore()); + localTransitionList.add(trans.getDateTimeAfter()); + } else { + localTransitionList.add(trans.getDateTimeAfter()); + localTransitionList.add(trans.getDateTimeBefore()); + } + localTransitionOffsetList.add(trans.getOffsetAfter()); + } + this.savingsLocalTransitions = localTransitionList.toArray(new LocalDateTime[localTransitionList.size()]); + this.wallOffsets = localTransitionOffsetList.toArray(new ZoneOffset[localTransitionOffsetList.size()]); + + // convert savings transitions to instants + this.savingsInstantTransitions = new long[transitionList.size()]; + for (int i = 0; i < transitionList.size(); i++) { + this.savingsInstantTransitions[i] = transitionList.get(i).toEpochSecond(); + } + + // last rules + if (lastRules.size() > 16) { + throw new IllegalArgumentException("Too many transition rules"); + } + this.lastRules = lastRules.toArray(new ZoneOffsetTransitionRule[lastRules.size()]); + } + + /** + * Constructor. + * + * @param standardTransitions the standard transitions, not null + * @param standardOffsets the standard offsets, not null + * @param savingsInstantTransitions the standard transitions, not null + * @param wallOffsets the wall offsets, not null + * @param lastRules the recurring last rules, size 15 or less, not null + */ + private ZoneRules(long[] standardTransitions, + ZoneOffset[] standardOffsets, + long[] savingsInstantTransitions, + ZoneOffset[] wallOffsets, + ZoneOffsetTransitionRule[] lastRules) { + super(); + + this.standardTransitions = standardTransitions; + this.standardOffsets = standardOffsets; + this.savingsInstantTransitions = savingsInstantTransitions; + this.wallOffsets = wallOffsets; + this.lastRules = lastRules; + + if (savingsInstantTransitions.length == 0) { + this.savingsLocalTransitions = EMPTY_LDT_ARRAY; + } else { + // convert savings transitions to locals + List localTransitionList = new ArrayList<>(); + for (int i = 0; i < savingsInstantTransitions.length; i++) { + ZoneOffset before = wallOffsets[i]; + ZoneOffset after = wallOffsets[i + 1]; + ZoneOffsetTransition trans = new ZoneOffsetTransition(savingsInstantTransitions[i], before, after); + if (trans.isGap()) { + localTransitionList.add(trans.getDateTimeBefore()); + localTransitionList.add(trans.getDateTimeAfter()); + } else { + localTransitionList.add(trans.getDateTimeAfter()); + localTransitionList.add(trans.getDateTimeBefore()); + } + } + this.savingsLocalTransitions = localTransitionList.toArray(new LocalDateTime[localTransitionList.size()]); + } + } + + /** + * Creates an instance of ZoneRules that has fixed zone rules. + * + * @param offset the offset this fixed zone rules is based on, not null + * @return the zone rules, not null + * @see #isFixedOffset() + */ + private ZoneRules(ZoneOffset offset) { + this.standardOffsets = new ZoneOffset[1]; + this.standardOffsets[0] = offset; + this.standardTransitions = EMPTY_LONG_ARRAY; + this.savingsInstantTransitions = EMPTY_LONG_ARRAY; + this.savingsLocalTransitions = EMPTY_LDT_ARRAY; + this.wallOffsets = standardOffsets; + this.lastRules = EMPTY_LASTRULES; + } + + /** + * Uses a serialization delegate. + * + * @return the replacing object, not null + */ + private Object writeReplace() { + return new Ser(Ser.ZRULES, this); + } + + /** + * Writes the state to the stream. + * + * @param out the output stream, not null + * @throws IOException if an error occurs + */ + void writeExternal(DataOutput out) throws IOException { + out.writeInt(standardTransitions.length); + for (long trans : standardTransitions) { + Ser.writeEpochSec(trans, out); + } + for (ZoneOffset offset : standardOffsets) { + Ser.writeOffset(offset, out); + } + out.writeInt(savingsInstantTransitions.length); + for (long trans : savingsInstantTransitions) { + Ser.writeEpochSec(trans, out); + } + for (ZoneOffset offset : wallOffsets) { + Ser.writeOffset(offset, out); + } + out.writeByte(lastRules.length); + for (ZoneOffsetTransitionRule rule : lastRules) { + rule.writeExternal(out); + } + } + + /** + * Reads the state from the stream. + * + * @param in the input stream, not null + * @return the created object, not null + * @throws IOException if an error occurs + */ + static ZoneRules readExternal(DataInput in) throws IOException, ClassNotFoundException { + int stdSize = in.readInt(); + long[] stdTrans = (stdSize == 0) ? EMPTY_LONG_ARRAY + : new long[stdSize]; + for (int i = 0; i < stdSize; i++) { + stdTrans[i] = Ser.readEpochSec(in); + } + ZoneOffset[] stdOffsets = new ZoneOffset[stdSize + 1]; + for (int i = 0; i < stdOffsets.length; i++) { + stdOffsets[i] = Ser.readOffset(in); + } + int savSize = in.readInt(); + long[] savTrans = (savSize == 0) ? EMPTY_LONG_ARRAY + : new long[savSize]; + for (int i = 0; i < savSize; i++) { + savTrans[i] = Ser.readEpochSec(in); + } + ZoneOffset[] savOffsets = new ZoneOffset[savSize + 1]; + for (int i = 0; i < savOffsets.length; i++) { + savOffsets[i] = Ser.readOffset(in); + } + int ruleSize = in.readByte(); + ZoneOffsetTransitionRule[] rules = (ruleSize == 0) ? + EMPTY_LASTRULES : new ZoneOffsetTransitionRule[ruleSize]; + for (int i = 0; i < ruleSize; i++) { + rules[i] = ZoneOffsetTransitionRule.readExternal(in); + } + return new ZoneRules(stdTrans, stdOffsets, savTrans, savOffsets, rules); + } + + /** + * Checks of the zone rules are fixed, such that the offset never varies. + * + * @return true if the time-zone is fixed and the offset never changes + */ + public boolean isFixedOffset() { + return savingsInstantTransitions.length == 0; + } + + /** + * Gets the offset applicable at the specified instant in these rules. + *

    + * The mapping from an instant to an offset is simple, there is only + * one valid offset for each instant. + * This method returns that offset. + * + * @param instant the instant to find the offset for, not null, but null + * may be ignored if the rules have a single offset for all instants + * @return the offset, not null + */ + public ZoneOffset getOffset(Instant instant) { + if (savingsInstantTransitions.length == 0) { + return standardOffsets[0]; + } + long epochSec = instant.getEpochSecond(); + // check if using last rules + if (lastRules.length > 0 && + epochSec > savingsInstantTransitions[savingsInstantTransitions.length - 1]) { + int year = findYear(epochSec, wallOffsets[wallOffsets.length - 1]); + ZoneOffsetTransition[] transArray = findTransitionArray(year); + ZoneOffsetTransition trans = null; + for (int i = 0; i < transArray.length; i++) { + trans = transArray[i]; + if (epochSec < trans.toEpochSecond()) { + return trans.getOffsetBefore(); + } + } + return trans.getOffsetAfter(); + } + + // using historic rules + int index = Arrays.binarySearch(savingsInstantTransitions, epochSec); + if (index < 0) { + // switch negative insert position to start of matched range + index = -index - 2; + } + return wallOffsets[index + 1]; + } + + /** + * Gets a suitable offset for the specified local date-time in these rules. + *

    + * The mapping from a local date-time to an offset is not straightforward. + * There are three cases: + *

      + *
    • Normal, with one valid offset. For the vast majority of the year, the normal + * case applies, where there is a single valid offset for the local date-time.
    • + *
    • Gap, with zero valid offsets. This is when clocks jump forward typically + * due to the spring daylight savings change from "winter" to "summer". + * In a gap there are local date-time values with no valid offset.
    • + *
    • Overlap, with two valid offsets. This is when clocks are set back typically + * due to the autumn daylight savings change from "summer" to "winter". + * In an overlap there are local date-time values with two valid offsets.
    • + *

    + * Thus, for any given local date-time there can be zero, one or two valid offsets. + * This method returns the single offset in the Normal case, and in the Gap or Overlap + * case it returns the offset before the transition. + *

    + * Since, in the case of Gap and Overlap, the offset returned is a "best" value, rather + * than the "correct" value, it should be treated with care. Applications that care + * about the correct offset should use a combination of this method, + * {@link #getValidOffsets(LocalDateTime)} and {@link #getTransition(LocalDateTime)}. + * + * @param localDateTime the local date-time to query, not null, but null + * may be ignored if the rules have a single offset for all instants + * @return the best available offset for the local date-time, not null + */ + public ZoneOffset getOffset(LocalDateTime localDateTime) { + Object info = getOffsetInfo(localDateTime); + if (info instanceof ZoneOffsetTransition) { + return ((ZoneOffsetTransition) info).getOffsetBefore(); + } + return (ZoneOffset) info; + } + + /** + * Gets the offset applicable at the specified local date-time in these rules. + *

    + * The mapping from a local date-time to an offset is not straightforward. + * There are three cases: + *

      + *
    • Normal, with one valid offset. For the vast majority of the year, the normal + * case applies, where there is a single valid offset for the local date-time.
    • + *
    • Gap, with zero valid offsets. This is when clocks jump forward typically + * due to the spring daylight savings change from "winter" to "summer". + * In a gap there are local date-time values with no valid offset.
    • + *
    • Overlap, with two valid offsets. This is when clocks are set back typically + * due to the autumn daylight savings change from "summer" to "winter". + * In an overlap there are local date-time values with two valid offsets.
    • + *

    + * Thus, for any given local date-time there can be zero, one or two valid offsets. + * This method returns that list of valid offsets, which is a list of size 0, 1 or 2. + * In the case where there are two offsets, the earlier offset is returned at index 0 + * and the later offset at index 1. + *

    + * There are various ways to handle the conversion from a {@code LocalDateTime}. + * One technique, using this method, would be: + *

    +     *  List<ZoneOffset> validOffsets = rules.getOffset(localDT);
    +     *  if (validOffsets.size() == 1) {
    +     *    // Normal case: only one valid offset
    +     *    zoneOffset = validOffsets.get(0);
    +     *  } else {
    +     *    // Gap or Overlap: determine what to do from transition (which will be non-null)
    +     *    ZoneOffsetTransition trans = rules.getTransition(localDT);
    +     *  }
    +     * 
    + *

    + * In theory, it is possible for there to be more than two valid offsets. + * This would happen if clocks to be put back more than once in quick succession. + * This has never happened in the history of time-zones and thus has no special handling. + * However, if it were to happen, then the list would return more than 2 entries. + * + * @param localDateTime the local date-time to query for valid offsets, not null, but null + * may be ignored if the rules have a single offset for all instants + * @return the list of valid offsets, may be immutable, not null + */ + public List getValidOffsets(LocalDateTime localDateTime) { + // should probably be optimized + Object info = getOffsetInfo(localDateTime); + if (info instanceof ZoneOffsetTransition) { + return ((ZoneOffsetTransition) info).getValidOffsets(); + } + return Collections.singletonList((ZoneOffset) info); + } + + /** + * Gets the offset transition applicable at the specified local date-time in these rules. + *

    + * The mapping from a local date-time to an offset is not straightforward. + * There are three cases: + *

      + *
    • Normal, with one valid offset. For the vast majority of the year, the normal + * case applies, where there is a single valid offset for the local date-time.
    • + *
    • Gap, with zero valid offsets. This is when clocks jump forward typically + * due to the spring daylight savings change from "winter" to "summer". + * In a gap there are local date-time values with no valid offset.
    • + *
    • Overlap, with two valid offsets. This is when clocks are set back typically + * due to the autumn daylight savings change from "summer" to "winter". + * In an overlap there are local date-time values with two valid offsets.
    • + *

    + * A transition is used to model the cases of a Gap or Overlap. + * The Normal case will return null. + *

    + * There are various ways to handle the conversion from a {@code LocalDateTime}. + * One technique, using this method, would be: + *

    +     *  ZoneOffsetTransition trans = rules.getTransition(localDT);
    +     *  if (trans == null) {
    +     *    // Gap or Overlap: determine what to do from transition
    +     *  } else {
    +     *    // Normal case: only one valid offset
    +     *    zoneOffset = rule.getOffset(localDT);
    +     *  }
    +     * 
    + * + * @param localDateTime the local date-time to query for offset transition, not null, but null + * may be ignored if the rules have a single offset for all instants + * @return the offset transition, null if the local date-time is not in transition + */ + public ZoneOffsetTransition getTransition(LocalDateTime localDateTime) { + Object info = getOffsetInfo(localDateTime); + return (info instanceof ZoneOffsetTransition ? (ZoneOffsetTransition) info : null); + } + + private Object getOffsetInfo(LocalDateTime dt) { + if (savingsInstantTransitions.length == 0) { + return standardOffsets[0]; + } + // check if using last rules + if (lastRules.length > 0 && + dt.isAfter(savingsLocalTransitions[savingsLocalTransitions.length - 1])) { + ZoneOffsetTransition[] transArray = findTransitionArray(dt.getYear()); + Object info = null; + for (ZoneOffsetTransition trans : transArray) { + info = findOffsetInfo(dt, trans); + if (info instanceof ZoneOffsetTransition || info.equals(trans.getOffsetBefore())) { + return info; + } + } + return info; + } + + // using historic rules + int index = Arrays.binarySearch(savingsLocalTransitions, dt); + if (index == -1) { + // before first transition + return wallOffsets[0]; + } + if (index < 0) { + // switch negative insert position to start of matched range + index = -index - 2; + } else if (index < savingsLocalTransitions.length - 1 && + savingsLocalTransitions[index].equals(savingsLocalTransitions[index + 1])) { + // handle overlap immediately following gap + index++; + } + if ((index & 1) == 0) { + // gap or overlap + LocalDateTime dtBefore = savingsLocalTransitions[index]; + LocalDateTime dtAfter = savingsLocalTransitions[index + 1]; + ZoneOffset offsetBefore = wallOffsets[index / 2]; + ZoneOffset offsetAfter = wallOffsets[index / 2 + 1]; + if (offsetAfter.getTotalSeconds() > offsetBefore.getTotalSeconds()) { + // gap + return new ZoneOffsetTransition(dtBefore, offsetBefore, offsetAfter); + } else { + // overlap + return new ZoneOffsetTransition(dtAfter, offsetBefore, offsetAfter); + } + } else { + // normal (neither gap or overlap) + return wallOffsets[index / 2 + 1]; + } + } + + /** + * Finds the offset info for a local date-time and transition. + * + * @param dt the date-time, not null + * @param trans the transition, not null + * @return the offset info, not null + */ + private Object findOffsetInfo(LocalDateTime dt, ZoneOffsetTransition trans) { + LocalDateTime localTransition = trans.getDateTimeBefore(); + if (trans.isGap()) { + if (dt.isBefore(localTransition)) { + return trans.getOffsetBefore(); + } + if (dt.isBefore(trans.getDateTimeAfter())) { + return trans; + } else { + return trans.getOffsetAfter(); + } + } else { + if (dt.isBefore(localTransition) == false) { + return trans.getOffsetAfter(); + } + if (dt.isBefore(trans.getDateTimeAfter())) { + return trans.getOffsetBefore(); + } else { + return trans; + } + } + } + + /** + * Finds the appropriate transition array for the given year. + * + * @param year the year, not null + * @return the transition array, not null + */ + private ZoneOffsetTransition[] findTransitionArray(int year) { + Integer yearObj = year; // should use Year class, but this saves a class load + ZoneOffsetTransition[] transArray = lastRulesCache.get(yearObj); + if (transArray != null) { + return transArray; + } + ZoneOffsetTransitionRule[] ruleArray = lastRules; + transArray = new ZoneOffsetTransition[ruleArray.length]; + for (int i = 0; i < ruleArray.length; i++) { + transArray[i] = ruleArray[i].createTransition(year); + } + if (year < LAST_CACHED_YEAR) { + lastRulesCache.putIfAbsent(yearObj, transArray); + } + return transArray; + } + + /** + * Gets the standard offset for the specified instant in this zone. + *

    + * This provides access to historic information on how the standard offset + * has changed over time. + * The standard offset is the offset before any daylight saving time is applied. + * This is typically the offset applicable during winter. + * + * @param instant the instant to find the offset information for, not null, but null + * may be ignored if the rules have a single offset for all instants + * @return the standard offset, not null + */ + public ZoneOffset getStandardOffset(Instant instant) { + if (savingsInstantTransitions.length == 0) { + return standardOffsets[0]; + } + long epochSec = instant.getEpochSecond(); + int index = Arrays.binarySearch(standardTransitions, epochSec); + if (index < 0) { + // switch negative insert position to start of matched range + index = -index - 2; + } + return standardOffsets[index + 1]; + } + + /** + * Gets the amount of daylight savings in use for the specified instant in this zone. + *

    + * This provides access to historic information on how the amount of daylight + * savings has changed over time. + * This is the difference between the standard offset and the actual offset. + * Typically the amount is zero during winter and one hour during summer. + * Time-zones are second-based, so the nanosecond part of the duration will be zero. + *

    + * This default implementation calculates the duration from the + * {@link #getOffset(java.time.Instant) actual} and + * {@link #getStandardOffset(java.time.Instant) standard} offsets. + * + * @param instant the instant to find the daylight savings for, not null, but null + * may be ignored if the rules have a single offset for all instants + * @return the difference between the standard and actual offset, not null + */ + public Duration getDaylightSavings(Instant instant) { + if (savingsInstantTransitions.length == 0) { + return Duration.ZERO; + } + ZoneOffset standardOffset = getStandardOffset(instant); + ZoneOffset actualOffset = getOffset(instant); + return Duration.ofSeconds(actualOffset.getTotalSeconds() - standardOffset.getTotalSeconds()); + } + + /** + * Checks if the specified instant is in daylight savings. + *

    + * This checks if the standard offset and the actual offset are the same + * for the specified instant. + * If they are not, it is assumed that daylight savings is in operation. + *

    + * This default implementation compares the {@link #getOffset(java.time.Instant) actual} + * and {@link #getStandardOffset(java.time.Instant) standard} offsets. + * + * @param instant the instant to find the offset information for, not null, but null + * may be ignored if the rules have a single offset for all instants + * @return the standard offset, not null + */ + public boolean isDaylightSavings(Instant instant) { + return (getStandardOffset(instant).equals(getOffset(instant)) == false); + } + + /** + * Checks if the offset date-time is valid for these rules. + *

    + * To be valid, the local date-time must not be in a gap and the offset + * must match one of the valid offsets. + *

    + * This default implementation checks if {@link #getValidOffsets(java.time.LocalDateTime)} + * contains the specified offset. + * + * @param localDateTime the date-time to check, not null, but null + * may be ignored if the rules have a single offset for all instants + * @param offset the offset to check, null returns false + * @return true if the offset date-time is valid for these rules + */ + public boolean isValidOffset(LocalDateTime localDateTime, ZoneOffset offset) { + return getValidOffsets(localDateTime).contains(offset); + } + + /** + * Gets the next transition after the specified instant. + *

    + * This returns details of the next transition after the specified instant. + * For example, if the instant represents a point where "Summer" daylight savings time + * applies, then the method will return the transition to the next "Winter" time. + * + * @param instant the instant to get the next transition after, not null, but null + * may be ignored if the rules have a single offset for all instants + * @return the next transition after the specified instant, null if this is after the last transition + */ + public ZoneOffsetTransition nextTransition(Instant instant) { + if (savingsInstantTransitions.length == 0) { + return null; + } + long epochSec = instant.getEpochSecond(); + // check if using last rules + if (epochSec >= savingsInstantTransitions[savingsInstantTransitions.length - 1]) { + if (lastRules.length == 0) { + return null; + } + // search year the instant is in + int year = findYear(epochSec, wallOffsets[wallOffsets.length - 1]); + ZoneOffsetTransition[] transArray = findTransitionArray(year); + for (ZoneOffsetTransition trans : transArray) { + if (epochSec < trans.toEpochSecond()) { + return trans; + } + } + // use first from following year + if (year < Year.MAX_VALUE) { + transArray = findTransitionArray(year + 1); + return transArray[0]; + } + return null; + } + + // using historic rules + int index = Arrays.binarySearch(savingsInstantTransitions, epochSec); + if (index < 0) { + index = -index - 1; // switched value is the next transition + } else { + index += 1; // exact match, so need to add one to get the next + } + return new ZoneOffsetTransition(savingsInstantTransitions[index], wallOffsets[index], wallOffsets[index + 1]); + } + + /** + * Gets the previous transition before the specified instant. + *

    + * This returns details of the previous transition after the specified instant. + * For example, if the instant represents a point where "summer" daylight saving time + * applies, then the method will return the transition from the previous "winter" time. + * + * @param instant the instant to get the previous transition after, not null, but null + * may be ignored if the rules have a single offset for all instants + * @return the previous transition after the specified instant, null if this is before the first transition + */ + public ZoneOffsetTransition previousTransition(Instant instant) { + if (savingsInstantTransitions.length == 0) { + return null; + } + long epochSec = instant.getEpochSecond(); + if (instant.getNano() > 0 && epochSec < Long.MAX_VALUE) { + epochSec += 1; // allow rest of method to only use seconds + } + + // check if using last rules + long lastHistoric = savingsInstantTransitions[savingsInstantTransitions.length - 1]; + if (lastRules.length > 0 && epochSec > lastHistoric) { + // search year the instant is in + ZoneOffset lastHistoricOffset = wallOffsets[wallOffsets.length - 1]; + int year = findYear(epochSec, lastHistoricOffset); + ZoneOffsetTransition[] transArray = findTransitionArray(year); + for (int i = transArray.length - 1; i >= 0; i--) { + if (epochSec > transArray[i].toEpochSecond()) { + return transArray[i]; + } + } + // use last from preceeding year + int lastHistoricYear = findYear(lastHistoric, lastHistoricOffset); + if (--year > lastHistoricYear) { + transArray = findTransitionArray(year); + return transArray[transArray.length - 1]; + } + // drop through + } + + // using historic rules + int index = Arrays.binarySearch(savingsInstantTransitions, epochSec); + if (index < 0) { + index = -index - 1; + } + if (index <= 0) { + return null; + } + return new ZoneOffsetTransition(savingsInstantTransitions[index - 1], wallOffsets[index - 1], wallOffsets[index]); + } + + private int findYear(long epochSecond, ZoneOffset offset) { + // inline for performance + long localSecond = epochSecond + offset.getTotalSeconds(); + long localEpochDay = Math.floorDiv(localSecond, 86400); + return LocalDate.ofEpochDay(localEpochDay).getYear(); + } + + /** + * Gets the complete list of fully defined transitions. + *

    + * The complete set of transitions for this rules instance is defined by this method + * and {@link #getTransitionRules()}. This method returns those transitions that have + * been fully defined. These are typically historical, but may be in the future. + *

    + * The list will be empty for fixed offset rules and for any time-zone where there has + * only ever been a single offset. The list will also be empty if the transition rules are unknown. + * + * @return an immutable list of fully defined transitions, not null + */ + public List getTransitions() { + List list = new ArrayList<>(); + for (int i = 0; i < savingsInstantTransitions.length; i++) { + list.add(new ZoneOffsetTransition(savingsInstantTransitions[i], wallOffsets[i], wallOffsets[i + 1])); + } + return Collections.unmodifiableList(list); + } + + /** + * Gets the list of transition rules for years beyond those defined in the transition list. + *

    + * The complete set of transitions for this rules instance is defined by this method + * and {@link #getTransitions()}. This method returns instances of {@link ZoneOffsetTransitionRule} + * that define an algorithm for when transitions will occur. + *

    + * For any given {@code ZoneRules}, this list contains the transition rules for years + * beyond those years that have been fully defined. These rules typically refer to future + * daylight saving time rule changes. + *

    + * If the zone defines daylight savings into the future, then the list will normally + * be of size two and hold information about entering and exiting daylight savings. + * If the zone does not have daylight savings, or information about future changes + * is uncertain, then the list will be empty. + *

    + * The list will be empty for fixed offset rules and for any time-zone where there is no + * daylight saving time. The list will also be empty if the transition rules are unknown. + * + * @return an immutable list of transition rules, not null + */ + public List getTransitionRules() { + return Collections.unmodifiableList(Arrays.asList(lastRules)); + } + + /** + * Checks if this set of rules equals another. + *

    + * Two rule sets are equal if they will always result in the same output + * for any given input instant or local date-time. + * Rules from two different groups may return false even if they are in fact the same. + *

    + * This definition should result in implementations comparing their entire state. + * + * @param otherRules the other rules, null returns false + * @return true if this rules is the same as that specified + */ + @Override + public boolean equals(Object otherRules) { + if (this == otherRules) { + return true; + } + if (otherRules instanceof ZoneRules) { + ZoneRules other = (ZoneRules) otherRules; + return Arrays.equals(standardTransitions, other.standardTransitions) && + Arrays.equals(standardOffsets, other.standardOffsets) && + Arrays.equals(savingsInstantTransitions, other.savingsInstantTransitions) && + Arrays.equals(wallOffsets, other.wallOffsets) && + Arrays.equals(lastRules, other.lastRules); + } + return false; + } + + /** + * Returns a suitable hash code given the definition of {@code #equals}. + * + * @return the hash code + */ + @Override + public int hashCode() { + return Arrays.hashCode(standardTransitions) ^ + Arrays.hashCode(standardOffsets) ^ + Arrays.hashCode(savingsInstantTransitions) ^ + Arrays.hashCode(wallOffsets) ^ + Arrays.hashCode(lastRules); + } + + /** + * Returns a string describing this object. + * + * @return a string for debugging, not null + */ + @Override + public String toString() { + return "ZoneRules[currentStandardOffset=" + standardOffsets[standardOffsets.length - 1] + "]"; + } + +} diff --git a/jdk/src/share/classes/java/time/zone/ZoneRulesException.java b/jdk/src/share/classes/java/time/zone/ZoneRulesException.java new file mode 100644 index 00000000000..e965af0dc34 --- /dev/null +++ b/jdk/src/share/classes/java/time/zone/ZoneRulesException.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.zone; + +import java.time.DateTimeException; + +/** + * Thrown to indicate a problem with time-zone configuration. + *

    + * This exception is used to indicate a problems with the configured + * time-zone rules. + * + *

    Specification for implementors

    + * This class is intended for use in a single thread. + * + * @since 1.8 + */ +public class ZoneRulesException extends DateTimeException { + + /** + * Serialization version. + */ + private static final long serialVersionUID = -1632418723876261839L; + + /** + * Constructs a new date-time exception with the specified message. + * + * @param message the message to use for this exception, may be null + */ + public ZoneRulesException(String message) { + super(message); + } + + /** + * Constructs a new date-time exception with the specified message and cause. + * + * @param message the message to use for this exception, may be null + * @param cause the cause of the exception, may be null + */ + public ZoneRulesException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/jdk/src/share/classes/java/time/zone/ZoneRulesProvider.java b/jdk/src/share/classes/java/time/zone/ZoneRulesProvider.java new file mode 100644 index 00000000000..2c187a7e32e --- /dev/null +++ b/jdk/src/share/classes/java/time/zone/ZoneRulesProvider.java @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2009-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package java.time.zone; + +import java.time.DateTimeException; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.NavigableMap; +import java.util.Objects; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Provider of time-zone rules to the system. + *

    + * This class manages the configuration of time-zone rules. + * The static methods provide the public API that can be used to manage the providers. + * The abstract methods provide the SPI that allows rules to be provided. + *

    + * Rules are looked up primarily by zone ID, as used by {@link ZoneId}. + * Only zone region IDs may be used, zone offset IDs are not used here. + *

    + * Time-zone rules are political, thus the data can change at any time. + * Each provider will provide the latest rules for each zone ID, but they + * may also provide the history of how the rules changed. + * + *

    Specification for implementors

    + * This interface is a service provider that can be called by multiple threads. + * Implementations must be immutable and thread-safe. + *

    + * Providers must ensure that once a rule has been seen by the application, the + * rule must continue to be available. + *

    + * Many systems would like to update time-zone rules dynamically without stopping the JVM. + * When examined in detail, this is a complex problem. + * Providers may choose to handle dynamic updates, however the default provider does not. + * + * @since 1.8 + */ +public abstract class ZoneRulesProvider { + + /** + * The set of loaded providers. + */ + private static final CopyOnWriteArrayList PROVIDERS = new CopyOnWriteArrayList<>(); + /** + * The lookup from zone region ID to provider. + */ + private static final ConcurrentMap ZONES = new ConcurrentHashMap<>(512, 0.75f, 2); + static { + registerProvider(new TzdbZoneRulesProvider()); + ServiceLoader sl = ServiceLoader.load(ZoneRulesProvider.class, ClassLoader.getSystemClassLoader()); + List loaded = new ArrayList<>(); + Iterator it = sl.iterator(); + while (it.hasNext()) { + ZoneRulesProvider provider; + try { + provider = it.next(); + } catch (ServiceConfigurationError ex) { + if (ex.getCause() instanceof SecurityException) { + continue; // ignore the security exception, try the next provider + } + throw ex; + } + registerProvider0(provider); + } + // CopyOnWriteList could be slow if lots of providers and each added individually + PROVIDERS.addAll(loaded); + } + + //------------------------------------------------------------------------- + /** + * Gets the set of available zone IDs. + *

    + * These zone IDs are loaded and available for use by {@code ZoneId}. + * + * @return a modifiable copy of the set of zone IDs, not null + */ + public static Set getAvailableZoneIds() { + return new HashSet<>(ZONES.keySet()); + } + + /** + * Gets the rules for the zone ID. + *

    + * This returns the latest available rules for the zone ID. + *

    + * This method relies on time-zone data provider files that are configured. + * These are loaded using a {@code ServiceLoader}. + * + * @param zoneId the zone region ID as used by {@code ZoneId}, not null + * @return the rules for the ID, not null + * @throws ZoneRulesException if the zone ID is unknown + */ + public static ZoneRules getRules(String zoneId) { + Objects.requireNonNull(zoneId, "zoneId"); + return getProvider(zoneId).provideRules(zoneId); + } + + /** + * Gets the history of rules for the zone ID. + *

    + * Time-zones are defined by governments and change frequently. + * This method allows applications to find the history of changes to the + * rules for a single zone ID. The map is keyed by a string, which is the + * version string associated with the rules. + *

    + * The exact meaning and format of the version is provider specific. + * The version must follow lexicographical order, thus the returned map will + * be order from the oldest known rules to the newest available rules. + * The default 'TZDB' group uses version numbering consisting of the year + * followed by a letter, such as '2009e' or '2012f'. + *

    + * Implementations must provide a result for each valid zone ID, however + * they do not have to provide a history of rules. + * Thus the map will always contain one element, and will only contain more + * than one element if historical rule information is available. + * + * @param zoneId the zone region ID as used by {@code ZoneId}, not null + * @return a modifiable copy of the history of the rules for the ID, sorted + * from oldest to newest, not null + * @throws ZoneRulesException if the zone ID is unknown + */ + public static NavigableMap getVersions(String zoneId) { + Objects.requireNonNull(zoneId, "zoneId"); + return getProvider(zoneId).provideVersions(zoneId); + } + + /** + * Gets the provider for the zone ID. + * + * @param zoneId the zone region ID as used by {@code ZoneId}, not null + * @return the provider, not null + * @throws ZoneRulesException if the zone ID is unknown + */ + private static ZoneRulesProvider getProvider(String zoneId) { + ZoneRulesProvider provider = ZONES.get(zoneId); + if (provider == null) { + if (ZONES.isEmpty()) { + throw new ZoneRulesException("No time-zone data files registered"); + } + throw new ZoneRulesException("Unknown time-zone ID: " + zoneId); + } + return provider; + } + + //------------------------------------------------------------------------- + /** + * Registers a zone rules provider. + *

    + * This adds a new provider to those currently available. + * A provider supplies rules for one or more zone IDs. + * A provider cannot be registered if it supplies a zone ID that has already been + * registered. See the notes on time-zone IDs in {@link ZoneId}, especially + * the section on using the concept of a "group" to make IDs unique. + *

    + * To ensure the integrity of time-zones already created, there is no way + * to deregister providers. + * + * @param provider the provider to register, not null + * @throws ZoneRulesException if a region is already registered + */ + public static void registerProvider(ZoneRulesProvider provider) { + Objects.requireNonNull(provider, "provider"); + registerProvider0(provider); + PROVIDERS.add(provider); + } + + /** + * Registers the provider. + * + * @param provider the provider to register, not null + * @throws ZoneRulesException if unable to complete the registration + */ + private static void registerProvider0(ZoneRulesProvider provider) { + for (String zoneId : provider.provideZoneIds()) { + Objects.requireNonNull(zoneId, "zoneId"); + ZoneRulesProvider old = ZONES.putIfAbsent(zoneId, provider.provideBind(zoneId)); + if (old != null) { + throw new ZoneRulesException( + "Unable to register zone as one already registered with that ID: " + zoneId + + ", currently loading from provider: " + provider); + } + } + } + + //------------------------------------------------------------------------- + /** + * Refreshes the rules from the underlying data provider. + *

    + * This method is an extension point that allows providers to refresh their + * rules dynamically at a time of the applications choosing. + * After calling this method, the offset stored in any {@link ZonedDateTime} + * may be invalid for the zone ID. + *

    + * Dynamic behavior is entirely optional and most providers, including the + * default provider, do not support it. + * + * @return true if the rules were updated + * @throws ZoneRulesException if an error occurs during the refresh + */ + public static boolean refresh() { + boolean changed = false; + for (ZoneRulesProvider provider : PROVIDERS) { + changed |= provider.provideRefresh(); + } + return changed; + } + + //----------------------------------------------------------------------- + /** + * Constructor. + */ + protected ZoneRulesProvider() { + } + + //----------------------------------------------------------------------- + /** + * SPI method to get the available zone IDs. + *

    + * This obtains the IDs that this {@code ZoneRulesProvider} provides. + * A provider should provide data for at least one region. + *

    + * The returned regions remain available and valid for the lifetime of the application. + * A dynamic provider may increase the set of regions as more data becomes available. + * + * @return the unmodifiable set of region IDs being provided, not null + */ + protected abstract Set provideZoneIds(); + + /** + * SPI method to bind to the specified zone ID. + *

    + * {@code ZoneRulesProvider} has a lookup from zone ID to provider. + * This method is used when building that lookup, allowing providers + * to insert a derived provider that is precisely tuned to the zone ID. + * This replaces two hash map lookups by one, enhancing performance. + *

    + * This optimization is optional. Returning {@code this} is acceptable. + *

    + * This implementation creates a bound provider that caches the + * rules from the underlying provider. The request to version history + * is forward on to the underlying. This is suitable for providers that + * cannot change their contents during the lifetime of the JVM. + * + * @param zoneId the zone region ID as used by {@code ZoneId}, not null + * @return the resolved provider for the ID, not null + * @throws DateTimeException if there is no provider for the specified group + */ + protected ZoneRulesProvider provideBind(String zoneId) { + return new BoundProvider(this, zoneId); + } + + /** + * SPI method to get the rules for the zone ID. + *

    + * This loads the rules for the region and version specified. + * The version may be null to indicate the "latest" version. + * + * @param regionId the time-zone region ID, not null + * @return the rules, not null + * @throws DateTimeException if rules cannot be obtained + */ + protected abstract ZoneRules provideRules(String regionId); + + /** + * SPI method to get the history of rules for the zone ID. + *

    + * This returns a map of historical rules keyed by a version string. + * The exact meaning and format of the version is provider specific. + * The version must follow lexicographical order, thus the returned map will + * be order from the oldest known rules to the newest available rules. + * The default 'TZDB' group uses version numbering consisting of the year + * followed by a letter, such as '2009e' or '2012f'. + *

    + * Implementations must provide a result for each valid zone ID, however + * they do not have to provide a history of rules. + * Thus the map will always contain one element, and will only contain more + * than one element if historical rule information is available. + *

    + * The returned versions remain available and valid for the lifetime of the application. + * A dynamic provider may increase the set of versions as more data becomes available. + * + * @param zoneId the zone region ID as used by {@code ZoneId}, not null + * @return a modifiable copy of the history of the rules for the ID, sorted + * from oldest to newest, not null + * @throws ZoneRulesException if the zone ID is unknown + */ + protected abstract NavigableMap provideVersions(String zoneId); + + /** + * SPI method to refresh the rules from the underlying data provider. + *

    + * This method provides the opportunity for a provider to dynamically + * recheck the underlying data provider to find the latest rules. + * This could be used to load new rules without stopping the JVM. + * Dynamic behavior is entirely optional and most providers do not support it. + *

    + * This implementation returns false. + * + * @return true if the rules were updated + * @throws DateTimeException if an error occurs during the refresh + */ + protected boolean provideRefresh() { + return false; + } + + //------------------------------------------------------------------------- + /** + * A provider bound to a single zone ID. + */ + private static class BoundProvider extends ZoneRulesProvider { + private final ZoneRulesProvider provider; + private final String zoneId; + private final ZoneRules rules; + + private BoundProvider(ZoneRulesProvider provider, String zoneId) { + this.provider = provider; + this.zoneId = zoneId; + this.rules = provider.provideRules(zoneId); + } + + @Override + protected Set provideZoneIds() { + return new HashSet<>(Collections.singleton(zoneId)); + } + + @Override + protected ZoneRules provideRules(String regionId) { + return rules; + } + + @Override + protected NavigableMap provideVersions(String zoneId) { + return provider.provideVersions(zoneId); + } + + @Override + public String toString() { + return zoneId; + } + } + +} diff --git a/jdk/src/share/classes/java/time/zone/package-info.java b/jdk/src/share/classes/java/time/zone/package-info.java new file mode 100644 index 00000000000..f50b3de2f5d --- /dev/null +++ b/jdk/src/share/classes/java/time/zone/package-info.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + *

    + * Support for time-zones and their rules. + *

    + *

    + * Daylight Saving Time and Time-Zones are concepts used by Governments to alter local time. + * This package provides support for time-zones, their rules and the resulting + * gaps and overlaps in the local time-line typically caused by Daylight Saving Time. + *

    + * + *

    Package specification

    + *

    + * Unless otherwise noted, passing a null argument to a constructor or method in any class or interface + * in this package will cause a {@link java.lang.NullPointerException NullPointerException} to be thrown. + * The Javadoc "@param" definition is used to summarise the null-behavior. + * The "@throws {@link java.lang.NullPointerException}" is not explicitly documented in each method. + *

    + *

    + * All calculations should check for numeric overflow and throw either an {@link java.lang.ArithmeticException} + * or a {@link java.time.DateTimeException}. + *

    + * @since JDK1.8 + */ +package java.time.zone; diff --git a/jdk/src/share/classes/java/util/Formatter.java b/jdk/src/share/classes/java/util/Formatter.java index c13e4ea3ed5..f5f479588c1 100644 --- a/jdk/src/share/classes/java/util/Formatter.java +++ b/jdk/src/share/classes/java/util/Formatter.java @@ -50,6 +50,21 @@ import java.text.NumberFormat; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.time.Clock; +import java.time.DateTimeException; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.Queries; +import java.time.temporal.OffsetDate; +import java.time.temporal.OffsetDateTime; +import java.time.temporal.OffsetTime; +import java.time.temporal.ChronoZonedDateTime; +import java.time.format.TextStyle; +import java.time.zone.ZoneRules; + import sun.misc.DoubleConsts; import sun.misc.FormattedFloatingDecimal; @@ -254,8 +269,8 @@ import sun.misc.FormattedFloatingDecimal; * * *
  • Date/Time - may be applied to Java types which are capable of - * encoding a date or time: {@code long}, {@link Long}, {@link Calendar}, and - * {@link Date}. + * encoding a date or time: {@code long}, {@link Long}, {@link Calendar}, + * {@link Date} and {@link TemporalAccessor TemporalAccessor} * *
  • Percent - produces a literal {@code '%'} * ('\u0025') @@ -1488,7 +1503,7 @@ import sun.misc.FormattedFloatingDecimal; *

    Date/Time

    * *

    This conversion may be applied to {@code long}, {@link Long}, {@link - * Calendar}, and {@link Date}. + * Calendar}, {@link Date} and {@link TemporalAccessor TemporalAccessor} * * * @@ -2782,6 +2797,9 @@ public final class Formatter implements Closeable, Flushable { } else if (arg instanceof Calendar) { cal = (Calendar) ((Calendar)arg).clone(); cal.setLenient(true); + } else if (arg instanceof TemporalAccessor) { + print((TemporalAccessor)arg, c, l); + return; } else { failConversion(c, arg); } @@ -4032,6 +4050,242 @@ public final class Formatter implements Closeable, Flushable { return sb; } + private void print(TemporalAccessor t, char c, Locale l) throws IOException { + StringBuilder sb = new StringBuilder(); + print(sb, t, c, l); + // justify based on width + String s = justify(sb.toString()); + if (f.contains(Flags.UPPERCASE)) + s = s.toUpperCase(); + a.append(s); + } + + private Appendable print(StringBuilder sb, TemporalAccessor t, char c, + Locale l) throws IOException { + if (sb == null) + sb = new StringBuilder(); + try { + switch (c) { + case DateTime.HOUR_OF_DAY_0: { // 'H' (00 - 23) + int i = t.get(ChronoField.HOUR_OF_DAY); + sb.append(localizedMagnitude(null, i, Flags.ZERO_PAD, 2, l)); + break; + } + case DateTime.HOUR_OF_DAY: { // 'k' (0 - 23) -- like H + int i = t.get(ChronoField.HOUR_OF_DAY); + sb.append(localizedMagnitude(null, i, Flags.NONE, 2, l)); + break; + } + case DateTime.HOUR_0: { // 'I' (01 - 12) + int i = t.get(ChronoField.CLOCK_HOUR_OF_AMPM); + sb.append(localizedMagnitude(null, i, Flags.ZERO_PAD, 2, l)); + break; + } + case DateTime.HOUR: { // 'l' (1 - 12) -- like I + int i = t.get(ChronoField.CLOCK_HOUR_OF_AMPM); + sb.append(localizedMagnitude(null, i, Flags.NONE, 2, l)); + break; + } + case DateTime.MINUTE: { // 'M' (00 - 59) + int i = t.get(ChronoField.MINUTE_OF_HOUR); + Flags flags = Flags.ZERO_PAD; + sb.append(localizedMagnitude(null, i, flags, 2, l)); + break; + } + case DateTime.NANOSECOND: { // 'N' (000000000 - 999999999) + int i = t.get(ChronoField.MILLI_OF_SECOND) * 1000000; + Flags flags = Flags.ZERO_PAD; + sb.append(localizedMagnitude(null, i, flags, 9, l)); + break; + } + case DateTime.MILLISECOND: { // 'L' (000 - 999) + int i = t.get(ChronoField.MILLI_OF_SECOND); + Flags flags = Flags.ZERO_PAD; + sb.append(localizedMagnitude(null, i, flags, 3, l)); + break; + } + case DateTime.MILLISECOND_SINCE_EPOCH: { // 'Q' (0 - 99...?) + long i = t.getLong(ChronoField.INSTANT_SECONDS) * 1000L + + t.getLong(ChronoField.MILLI_OF_SECOND); + Flags flags = Flags.NONE; + sb.append(localizedMagnitude(null, i, flags, width, l)); + break; + } + case DateTime.AM_PM: { // 'p' (am or pm) + // Calendar.AM = 0, Calendar.PM = 1, LocaleElements defines upper + String[] ampm = { "AM", "PM" }; + if (l != null && l != Locale.US) { + DateFormatSymbols dfs = DateFormatSymbols.getInstance(l); + ampm = dfs.getAmPmStrings(); + } + String s = ampm[t.get(ChronoField.AMPM_OF_DAY)]; + sb.append(s.toLowerCase(l != null ? l : Locale.US)); + break; + } + case DateTime.SECONDS_SINCE_EPOCH: { // 's' (0 - 99...?) + long i = t.getLong(ChronoField.INSTANT_SECONDS); + Flags flags = Flags.NONE; + sb.append(localizedMagnitude(null, i, flags, width, l)); + break; + } + case DateTime.SECOND: { // 'S' (00 - 60 - leap second) + int i = t.get(ChronoField.SECOND_OF_MINUTE); + Flags flags = Flags.ZERO_PAD; + sb.append(localizedMagnitude(null, i, flags, 2, l)); + break; + } + case DateTime.ZONE_NUMERIC: { // 'z' ({-|+}####) - ls minus? + int i = t.get(ChronoField.OFFSET_SECONDS); + boolean neg = i < 0; + sb.append(neg ? '-' : '+'); + if (neg) + i = -i; + int min = i / 60; + // combine minute and hour into a single integer + int offset = (min / 60) * 100 + (min % 60); + Flags flags = Flags.ZERO_PAD; + sb.append(localizedMagnitude(null, offset, flags, 4, l)); + break; + } + case DateTime.ZONE: { // 'Z' (symbol) + ZoneId zid = t.query(Queries.zone()); + if (zid == null) { + throw new IllegalFormatConversionException(c, t.getClass()); + } + if (!(zid instanceof ZoneOffset) && + t.isSupported(ChronoField.INSTANT_SECONDS)) { + Instant instant = Instant.from(t); + sb.append(TimeZone.getTimeZone(zid.getId()) + .getDisplayName(zid.getRules().isDaylightSavings(instant), + TimeZone.SHORT, + (l == null) ? Locale.US : l)); + break; + } + sb.append(zid.getId()); + break; + } + // Date + case DateTime.NAME_OF_DAY_ABBREV: // 'a' + case DateTime.NAME_OF_DAY: { // 'A' + int i = t.get(ChronoField.DAY_OF_WEEK) % 7 + 1; + Locale lt = ((l == null) ? Locale.US : l); + DateFormatSymbols dfs = DateFormatSymbols.getInstance(lt); + if (c == DateTime.NAME_OF_DAY) + sb.append(dfs.getWeekdays()[i]); + else + sb.append(dfs.getShortWeekdays()[i]); + break; + } + case DateTime.NAME_OF_MONTH_ABBREV: // 'b' + case DateTime.NAME_OF_MONTH_ABBREV_X: // 'h' -- same b + case DateTime.NAME_OF_MONTH: { // 'B' + int i = t.get(ChronoField.MONTH_OF_YEAR) - 1; + Locale lt = ((l == null) ? Locale.US : l); + DateFormatSymbols dfs = DateFormatSymbols.getInstance(lt); + if (c == DateTime.NAME_OF_MONTH) + sb.append(dfs.getMonths()[i]); + else + sb.append(dfs.getShortMonths()[i]); + break; + } + case DateTime.CENTURY: // 'C' (00 - 99) + case DateTime.YEAR_2: // 'y' (00 - 99) + case DateTime.YEAR_4: { // 'Y' (0000 - 9999) + int i = t.get(ChronoField.YEAR); + int size = 2; + switch (c) { + case DateTime.CENTURY: + i /= 100; + break; + case DateTime.YEAR_2: + i %= 100; + break; + case DateTime.YEAR_4: + size = 4; + break; + } + Flags flags = Flags.ZERO_PAD; + sb.append(localizedMagnitude(null, i, flags, size, l)); + break; + } + case DateTime.DAY_OF_MONTH_0: // 'd' (01 - 31) + case DateTime.DAY_OF_MONTH: { // 'e' (1 - 31) -- like d + int i = t.get(ChronoField.DAY_OF_MONTH); + Flags flags = (c == DateTime.DAY_OF_MONTH_0 + ? Flags.ZERO_PAD + : Flags.NONE); + sb.append(localizedMagnitude(null, i, flags, 2, l)); + break; + } + case DateTime.DAY_OF_YEAR: { // 'j' (001 - 366) + int i = t.get(ChronoField.DAY_OF_YEAR); + Flags flags = Flags.ZERO_PAD; + sb.append(localizedMagnitude(null, i, flags, 3, l)); + break; + } + case DateTime.MONTH: { // 'm' (01 - 12) + int i = t.get(ChronoField.MONTH_OF_YEAR); + Flags flags = Flags.ZERO_PAD; + sb.append(localizedMagnitude(null, i, flags, 2, l)); + break; + } + + // Composites + case DateTime.TIME: // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS) + case DateTime.TIME_24_HOUR: { // 'R' (hh:mm same as %H:%M) + char sep = ':'; + print(sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep); + print(sb, t, DateTime.MINUTE, l); + if (c == DateTime.TIME) { + sb.append(sep); + print(sb, t, DateTime.SECOND, l); + } + break; + } + case DateTime.TIME_12_HOUR: { // 'r' (hh:mm:ss [AP]M) + char sep = ':'; + print(sb, t, DateTime.HOUR_0, l).append(sep); + print(sb, t, DateTime.MINUTE, l).append(sep); + print(sb, t, DateTime.SECOND, l).append(' '); + // this may be in wrong place for some locales + StringBuilder tsb = new StringBuilder(); + print(tsb, t, DateTime.AM_PM, l); + sb.append(tsb.toString().toUpperCase(l != null ? l : Locale.US)); + break; + } + case DateTime.DATE_TIME: { // 'c' (Sat Nov 04 12:02:33 EST 1999) + char sep = ' '; + print(sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep); + print(sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep); + print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep); + print(sb, t, DateTime.TIME, l).append(sep); + print(sb, t, DateTime.ZONE, l).append(sep); + print(sb, t, DateTime.YEAR_4, l); + break; + } + case DateTime.DATE: { // 'D' (mm/dd/yy) + char sep = '/'; + print(sb, t, DateTime.MONTH, l).append(sep); + print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep); + print(sb, t, DateTime.YEAR_2, l); + break; + } + case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d) + char sep = '-'; + print(sb, t, DateTime.YEAR_4, l).append(sep); + print(sb, t, DateTime.MONTH, l).append(sep); + print(sb, t, DateTime.DAY_OF_MONTH_0, l); + break; + } + default: + assert false; + } + } catch (DateTimeException x) { + throw new IllegalFormatConversionException(c, t.getClass()); + } + return sb; + } + // -- Methods to support throwing exceptions -- private void failMismatch(Flags f, char c) { diff --git a/jdk/test/Makefile b/jdk/test/Makefile index 871cc54dc2c..408aaaeed25 100644 --- a/jdk/test/Makefile +++ b/jdk/test/Makefile @@ -498,6 +498,11 @@ JDK_DEFAULT_TARGETS += jdk_math jdk_math: $(call TestDirs, java/math) $(call RunAgentvmBatch) +# Stable agentvm testruns (TestNG) +JDK_DEFAULT_TARGETS += jdk_time +jdk_time: $(call TestDirs, java/time) + $(call RunOthervmBatch) + # Stable agentvm testruns (minus items from PROBLEM_LIST) JDK_ALL_TARGETS += jdk_other JDK_DEFAULT_TARGETS += jdk_other diff --git a/jdk/test/java/time/META-INF/services/java.time.temporal.Chrono b/jdk/test/java/time/META-INF/services/java.time.temporal.Chrono new file mode 100644 index 00000000000..918ad84d621 --- /dev/null +++ b/jdk/test/java/time/META-INF/services/java.time.temporal.Chrono @@ -0,0 +1 @@ +tck.java.time.calendar.CopticChrono diff --git a/jdk/test/java/time/TEST.properties b/jdk/test/java/time/TEST.properties new file mode 100644 index 00000000000..ccf8ed60635 --- /dev/null +++ b/jdk/test/java/time/TEST.properties @@ -0,0 +1,3 @@ +# Threeten test uses TestNG +TestNG.dirs = . + diff --git a/jdk/test/java/time/tck/java/time/AbstractDateTimeTest.java b/jdk/test/java/time/tck/java/time/AbstractDateTimeTest.java new file mode 100644 index 00000000000..d44d2410f28 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/AbstractDateTimeTest.java @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2011-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time; + +import java.time.*; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +import java.util.List; + +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalQuery; +import java.time.temporal.TemporalField; + +import org.testng.annotations.Test; +import test.java.time.AbstractTest; +import test.java.time.temporal.MockFieldNoValue; + +/** + * Base test class for {@code Temporal}. + */ +public abstract class AbstractDateTimeTest extends AbstractTCKTest { + + /** + * Sample {@code Temporal} objects. + * @return the objects, not null + */ + protected abstract List samples(); + + /** + * List of valid supported fields. + * @return the fields, not null + */ + protected abstract List validFields(); + + /** + * List of invalid unsupported fields. + * @return the fields, not null + */ + protected abstract List invalidFields(); + + //----------------------------------------------------------------------- + // isSupported(TemporalField) + //----------------------------------------------------------------------- + @Test(groups = "tck") + public void basicTest_isSupported_TemporalField_supported() { + for (TemporalAccessor sample : samples()) { + for (TemporalField field : validFields()) { + assertEquals(sample.isSupported(field), true, "Failed on " + sample + " " + field); + } + } + } + + @Test(groups = "tck") + public void basicTest_isSupported_TemporalField_unsupported() { + for (TemporalAccessor sample : samples()) { + for (TemporalField field : invalidFields()) { + assertEquals(sample.isSupported(field), false, "Failed on " + sample + " " + field); + } + } + } + + @Test(groups = "tck") + public void basicTest_isSupported_TemporalField_null() { + for (TemporalAccessor sample : samples()) { + assertEquals(sample.isSupported(null), false, "Failed on " + sample); + } + } + + //----------------------------------------------------------------------- + // range(TemporalField) + //----------------------------------------------------------------------- + @Test(groups = "tck") + public void basicTest_range_TemporalField_supported() { + for (TemporalAccessor sample : samples()) { + for (TemporalField field : validFields()) { + sample.range(field); // no exception + } + } + } + + @Test(groups = "tck") + public void basicTest_range_TemporalField_unsupported() { + for (TemporalAccessor sample : samples()) { + for (TemporalField field : invalidFields()) { + try { + sample.range(field); + fail("Failed on " + sample + " " + field); + } catch (DateTimeException ex) { + // expected + } + } + } + } + + @Test(groups = "tck") + public void basicTest_range_TemporalField_null() { + for (TemporalAccessor sample : samples()) { + try { + sample.range(null); + fail("Failed on " + sample); + } catch (NullPointerException ex) { + // expected + } + } + } + + //----------------------------------------------------------------------- + // get(TemporalField) + //----------------------------------------------------------------------- + @Test(groups = "tck") + public void basicTest_get_TemporalField_supported() { + for (TemporalAccessor sample : samples()) { + for (TemporalField field : validFields()) { + if (sample.range(field).isIntValue()) { + sample.get(field); // no exception + } else { + try { + sample.get(field); + fail("Failed on " + sample + " " + field); + } catch (DateTimeException ex) { + // expected + } + } + } + } + } + + @Test(groups = "tck") + public void basicTest_get_TemporalField_unsupported() { + for (TemporalAccessor sample : samples()) { + for (TemporalField field : invalidFields()) { + try { + sample.get(field); + fail("Failed on " + sample + " " + field); + } catch (DateTimeException ex) { + // expected + } + } + } + } + + @Test(expectedExceptions=DateTimeException.class) + public void test_get_TemporalField_invalidField() { + for (TemporalAccessor sample : samples()) { + sample.get(MockFieldNoValue.INSTANCE); + } + } + + @Test(groups = "tck") + public void basicTest_get_TemporalField_null() { + for (TemporalAccessor sample : samples()) { + try { + sample.get(null); + fail("Failed on " + sample); + } catch (NullPointerException ex) { + // expected + } + } + } + + //----------------------------------------------------------------------- + // getLong(TemporalField) + //----------------------------------------------------------------------- + @Test(groups = "tck") + public void basicTest_getLong_TemporalField_supported() { + for (TemporalAccessor sample : samples()) { + for (TemporalField field : validFields()) { + sample.getLong(field); // no exception + } + } + } + + @Test(groups = "tck") + public void basicTest_getLong_TemporalField_unsupported() { + for (TemporalAccessor sample : samples()) { + for (TemporalField field : invalidFields()) { + try { + sample.getLong(field); + fail("Failed on " + sample + " " + field); + } catch (DateTimeException ex) { + // expected + } + } + } + } + + @Test(expectedExceptions=DateTimeException.class) + public void test_getLong_TemporalField_invalidField() { + for (TemporalAccessor sample : samples()) { + sample.getLong(MockFieldNoValue.INSTANCE); + } + } + + @Test(groups = "tck") + public void basicTest_getLong_TemporalField_null() { + for (TemporalAccessor sample : samples()) { + try { + sample.getLong(null); + fail("Failed on " + sample); + } catch (NullPointerException ex) { + // expected + } + } + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void basicTest_query() { + for (TemporalAccessor sample : samples()) { + assertEquals(sample.query(new TemporalQuery() { + @Override + public String queryFrom(TemporalAccessor temporal) { + return "foo"; + } + }), "foo"); + } + } + +} diff --git a/jdk/test/java/time/tck/java/time/AbstractTCKTest.java b/jdk/test/java/time/tck/java/time/AbstractTCKTest.java new file mode 100644 index 00000000000..ce20d6b9386 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/AbstractTCKTest.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2011-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time; + +import static org.testng.Assert.assertEquals; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamConstants; +import java.io.Serializable; +import java.lang.reflect.Field; + +/** + * Base test class. + */ +public abstract class AbstractTCKTest { + + protected static boolean isIsoLeap(long year) { + if (year % 4 != 0) { + return false; + } + if (year % 100 == 0 && year % 400 != 0) { + return false; + } + return true; + } + + protected static void assertSerializable(Object object) throws IOException, ClassNotFoundException { + assertEquals(object instanceof Serializable, true); + Object deserializedObject = writeThenRead(object); + assertEquals(deserializedObject, object); + } + + private static Object writeThenRead(Object object) throws IOException, ClassNotFoundException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (ObjectOutputStream oos = new ObjectOutputStream(baos) ) { + oos.writeObject(object); + } + try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { + return ois.readObject(); + } + } + + protected static void assertSerializedBySer(Object object, byte[] expectedBytes, byte[]... matches) throws Exception { + String serClass = object.getClass().getPackage().getName() + ".Ser"; + Class serCls = Class.forName(serClass); + Field field = serCls.getDeclaredField("serialVersionUID"); + field.setAccessible(true); + long serVer = (Long) field.get(null); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (ObjectOutputStream oos = new ObjectOutputStream(baos) ) { + oos.writeObject(object); + } + byte[] bytes = baos.toByteArray(); + ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + try (DataInputStream dis = new DataInputStream(bais)) { + assertEquals(dis.readShort(), ObjectStreamConstants.STREAM_MAGIC); + assertEquals(dis.readShort(), ObjectStreamConstants.STREAM_VERSION); + assertEquals(dis.readByte(), ObjectStreamConstants.TC_OBJECT); + assertEquals(dis.readByte(), ObjectStreamConstants.TC_CLASSDESC); + assertEquals(dis.readUTF(), serClass); + assertEquals(dis.readLong(), serVer); + assertEquals(dis.readByte(), ObjectStreamConstants.SC_EXTERNALIZABLE | ObjectStreamConstants.SC_BLOCK_DATA); + assertEquals(dis.readShort(), 0); // number of fields + assertEquals(dis.readByte(), ObjectStreamConstants.TC_ENDBLOCKDATA); // end of classdesc + assertEquals(dis.readByte(), ObjectStreamConstants.TC_NULL); // no superclasses + if (expectedBytes.length < 256) { + assertEquals(dis.readByte(), ObjectStreamConstants.TC_BLOCKDATA); + assertEquals(dis.readUnsignedByte(), expectedBytes.length); + } else { + assertEquals(dis.readByte(), ObjectStreamConstants.TC_BLOCKDATALONG); + assertEquals(dis.readInt(), expectedBytes.length); + } + byte[] input = new byte[expectedBytes.length]; + dis.readFully(input); + assertEquals(input, expectedBytes); + if (matches.length > 0) { + for (byte[] match : matches) { + boolean matched = false; + while (matched == false) { + try { + dis.mark(1000); + byte[] possible = new byte[match.length]; + dis.readFully(possible); + assertEquals(possible, match); + matched = true; + } catch (AssertionError ex) { + dis.reset(); + dis.readByte(); // ignore + } + } + } + } else { + assertEquals(dis.readByte(), ObjectStreamConstants.TC_ENDBLOCKDATA); // end of blockdata + assertEquals(dis.read(), -1); + } + } + } + +} diff --git a/jdk/test/java/time/tck/java/time/TCKClock.java b/jdk/test/java/time/tck/java/time/TCKClock.java new file mode 100644 index 00000000000..e4a870e3796 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/TCKClock.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012 Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time; + +import java.time.*; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +/** + * Test Clock. + */ +@Test +public class TCKClock { + + static class MockInstantClock extends Clock { + final long millis; + final ZoneId zone; + MockInstantClock(long millis, ZoneId zone) { + this.millis = millis; + this.zone = zone; + } + @Override + public long millis() { + return millis; + } + @Override + public ZoneId getZone() { + return zone; + } + @Override + public Clock withZone(ZoneId timeZone) { + return new MockInstantClock(millis, timeZone); + } + @Override + public boolean equals(Object obj) { + return false; + } + @Override + public int hashCode() { + return 0; + } + @Override + public String toString() { + return "Mock"; + } + } + + private static final Instant INSTANT = Instant.ofEpochSecond(1873687, 357000000); + private static final ZoneId ZONE = ZoneId.of("Europe/Paris"); + private static final Clock MOCK_INSTANT = new MockInstantClock(INSTANT.toEpochMilli(), ZONE); + + //----------------------------------------------------------------------- + @Test + public void test_mockInstantClock_get() { + assertEquals(MOCK_INSTANT.instant(), INSTANT); + assertEquals(MOCK_INSTANT.millis(), INSTANT.toEpochMilli()); + assertEquals(MOCK_INSTANT.getZone(), ZONE); + } + + @Test + public void test_mockInstantClock_withZone() { + ZoneId london = ZoneId.of("Europe/London"); + Clock changed = MOCK_INSTANT.withZone(london); + assertEquals(MOCK_INSTANT.instant(), INSTANT); + assertEquals(MOCK_INSTANT.millis(), INSTANT.toEpochMilli()); + assertEquals(changed.getZone(), london); + } + +} diff --git a/jdk/test/java/time/tck/java/time/TCKClock_Fixed.java b/jdk/test/java/time/tck/java/time/TCKClock_Fixed.java new file mode 100644 index 00000000000..20e66a2ed96 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/TCKClock_Fixed.java @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012 Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time; + +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.time.Clock; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; + +import org.testng.annotations.Test; + +/** + * Test fixed clock. + */ +@Test +public class TCKClock_Fixed extends AbstractTCKTest { + + private static final ZoneId MOSCOW = ZoneId.of("Europe/Moscow"); + private static final ZoneId PARIS = ZoneId.of("Europe/Paris"); + private static final Instant INSTANT = LocalDateTime.of(2008, 6, 30, 11, 30, 10, 500).atZone(ZoneOffset.ofHours(2)).toInstant(); + + //----------------------------------------------------------------------- + public void test_isSerializable() throws IOException, ClassNotFoundException { + assertSerializable(Clock.fixed(INSTANT, ZoneOffset.UTC)); + assertSerializable(Clock.fixed(INSTANT, PARIS)); + } + + //------------------------------------------------------------------------- + public void test_fixed_InstantZoneId() { + Clock test = Clock.fixed(INSTANT, PARIS); + assertEquals(test.instant(), INSTANT); + assertEquals(test.getZone(), PARIS); + } + + @Test(expectedExceptions = NullPointerException.class) + public void test_fixed_InstantZoneId_nullInstant() { + Clock.fixed(null, PARIS); + } + + @Test(expectedExceptions = NullPointerException.class) + public void test_fixed_InstantZoneId_nullZoneId() { + Clock.fixed(INSTANT, null); + } + + //------------------------------------------------------------------------- + public void test_withZone() { + Clock test = Clock.fixed(INSTANT, PARIS); + Clock changed = test.withZone(MOSCOW); + assertEquals(test.getZone(), PARIS); + assertEquals(changed.getZone(), MOSCOW); + } + + public void test_withZone_equal() { + Clock test = Clock.fixed(INSTANT, PARIS); + Clock changed = test.withZone(PARIS); + assertEquals(changed.getZone(), PARIS); + } + + @Test(expectedExceptions = NullPointerException.class) + public void test_withZone_null() { + Clock.fixed(INSTANT, PARIS).withZone(null); + } + + //----------------------------------------------------------------------- + public void test_equals() { + Clock a = Clock.fixed(INSTANT, ZoneOffset.UTC); + Clock b = Clock.fixed(INSTANT, ZoneOffset.UTC); + assertEquals(a.equals(a), true); + assertEquals(a.equals(b), true); + assertEquals(b.equals(a), true); + assertEquals(b.equals(b), true); + + Clock c = Clock.fixed(INSTANT, PARIS); + assertEquals(a.equals(c), false); + + Clock d = Clock.fixed(INSTANT.minusNanos(1), ZoneOffset.UTC); + assertEquals(a.equals(d), false); + + assertEquals(a.equals(null), false); + assertEquals(a.equals("other type"), false); + assertEquals(a.equals(Clock.systemUTC()), false); + } + + public void test_hashCode() { + Clock a = Clock.fixed(INSTANT, ZoneOffset.UTC); + Clock b = Clock.fixed(INSTANT, ZoneOffset.UTC); + assertEquals(a.hashCode(), a.hashCode()); + assertEquals(a.hashCode(), b.hashCode()); + + Clock c = Clock.fixed(INSTANT, PARIS); + assertEquals(a.hashCode() == c.hashCode(), false); + + Clock d = Clock.fixed(INSTANT.minusNanos(1), ZoneOffset.UTC); + assertEquals(a.hashCode() == d.hashCode(), false); + } + + //----------------------------------------------------------------------- + public void test_toString() { + // spec requires "full state" in toString() + Clock test = Clock.fixed(INSTANT, PARIS); + assertEquals(test.toString().contains("Europe/Paris"), true); + assertEquals(test.toString().contains("2008-06-30T09:30:10.000000500Z"), true); + } + +} diff --git a/jdk/test/java/time/tck/java/time/TCKClock_Offset.java b/jdk/test/java/time/tck/java/time/TCKClock_Offset.java new file mode 100644 index 00000000000..b628c0ce4eb --- /dev/null +++ b/jdk/test/java/time/tck/java/time/TCKClock_Offset.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012 Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertSame; + +import java.io.IOException; +import java.time.Clock; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; + +import org.testng.annotations.Test; + +/** + * Test offset clock. + */ +@Test +public class TCKClock_Offset extends AbstractTCKTest { + + private static final ZoneId MOSCOW = ZoneId.of("Europe/Moscow"); + private static final ZoneId PARIS = ZoneId.of("Europe/Paris"); + private static final Instant INSTANT = LocalDateTime.of(2008, 6, 30, 11, 30, 10, 500).atZone(ZoneOffset.ofHours(2)).toInstant(); + private static final Duration OFFSET = Duration.ofSeconds(2); + + //----------------------------------------------------------------------- + public void test_isSerializable() throws IOException, ClassNotFoundException { + assertSerializable(Clock.offset(Clock.system(PARIS), OFFSET)); + } + + //----------------------------------------------------------------------- + public void test_offset_ClockDuration() { + Clock test = Clock.offset(Clock.fixed(INSTANT, PARIS), OFFSET); + System.out.println(test.instant()); + System.out.println(INSTANT.plus(OFFSET)); + assertEquals(test.instant(), INSTANT.plus(OFFSET)); + assertEquals(test.getZone(), PARIS); + } + + public void test_offset_ClockDuration_zeroDuration() { + Clock underlying = Clock.system(PARIS); + Clock test = Clock.offset(underlying, Duration.ZERO); + assertSame(test, underlying); // spec says same + } + + @Test(expectedExceptions = NullPointerException.class) + public void test_offset_ClockDuration_nullClock() { + Clock.offset(null, Duration.ZERO); + } + + @Test(expectedExceptions = NullPointerException.class) + public void test_offset_ClockDuration_nullDuration() { + Clock.offset(Clock.systemUTC(), null); + } + + //------------------------------------------------------------------------- + public void test_withZone() { + Clock test = Clock.offset(Clock.system(PARIS), OFFSET); + Clock changed = test.withZone(MOSCOW); + assertEquals(test.getZone(), PARIS); + assertEquals(changed.getZone(), MOSCOW); + } + + public void test_withZone_equal() { + Clock test = Clock.offset(Clock.system(PARIS), OFFSET); + Clock changed = test.withZone(PARIS); + assertEquals(test, changed); + } + + @Test(expectedExceptions = NullPointerException.class) + public void test_withZone_null() { + Clock.offset(Clock.system(PARIS), OFFSET).withZone(null); + } + + //----------------------------------------------------------------------- + public void test_equals() { + Clock a = Clock.offset(Clock.system(PARIS), OFFSET); + Clock b = Clock.offset(Clock.system(PARIS), OFFSET); + assertEquals(a.equals(a), true); + assertEquals(a.equals(b), true); + assertEquals(b.equals(a), true); + assertEquals(b.equals(b), true); + + Clock c = Clock.offset(Clock.system(MOSCOW), OFFSET); + assertEquals(a.equals(c), false); + + Clock d = Clock.offset(Clock.system(PARIS), OFFSET.minusNanos(1)); + assertEquals(a.equals(d), false); + + assertEquals(a.equals(null), false); + assertEquals(a.equals("other type"), false); + assertEquals(a.equals(Clock.systemUTC()), false); + } + + public void test_hashCode() { + Clock a = Clock.offset(Clock.system(PARIS), OFFSET); + Clock b = Clock.offset(Clock.system(PARIS), OFFSET); + assertEquals(a.hashCode(), a.hashCode()); + assertEquals(a.hashCode(), b.hashCode()); + + Clock c = Clock.offset(Clock.system(MOSCOW), OFFSET); + assertEquals(a.hashCode() == c.hashCode(), false); + + Clock d = Clock.offset(Clock.system(PARIS), OFFSET.minusNanos(1)); + assertEquals(a.hashCode() == d.hashCode(), false); + } + + //----------------------------------------------------------------------- + public void test_toString() { + // spec requires "full state" in toString() + Clock test = Clock.offset(Clock.system(PARIS), OFFSET); + assertEquals(test.toString().contains("Europe/Paris"), true); + assertEquals(test.toString().contains("PT2S"), true); + } + +} diff --git a/jdk/test/java/time/tck/java/time/TCKClock_System.java b/jdk/test/java/time/tck/java/time/TCKClock_System.java new file mode 100644 index 00000000000..8152a502ffa --- /dev/null +++ b/jdk/test/java/time/tck/java/time/TCKClock_System.java @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012 Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +import java.io.IOException; +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZoneOffset; + +import org.testng.annotations.Test; + +/** + * Test system clock. + */ +@Test +public class TCKClock_System extends AbstractTCKTest { + + private static final ZoneId MOSCOW = ZoneId.of("Europe/Moscow"); + private static final ZoneId PARIS = ZoneId.of("Europe/Paris"); + + //----------------------------------------------------------------------- + public void test_isSerializable() throws IOException, ClassNotFoundException { + assertSerializable(Clock.systemUTC()); + assertSerializable(Clock.systemDefaultZone()); + assertSerializable(Clock.system(PARIS)); + } + + //----------------------------------------------------------------------- + public void test_instant() { + Clock system = Clock.systemUTC(); + assertEquals(system.getZone(), ZoneOffset.UTC); + for (int i = 0; i < 10000; i++) { + // assume can eventually get these within 10 milliseconds + Instant instant = system.instant(); + long systemMillis = System.currentTimeMillis(); + if (systemMillis - instant.toEpochMilli() < 10) { + return; // success + } + } + fail(); + } + + public void test_millis() { + Clock system = Clock.systemUTC(); + assertEquals(system.getZone(), ZoneOffset.UTC); + for (int i = 0; i < 10000; i++) { + // assume can eventually get these within 10 milliseconds + long instant = system.millis(); + long systemMillis = System.currentTimeMillis(); + if (systemMillis - instant < 10) { + return; // success + } + } + fail(); + } + + //------------------------------------------------------------------------- + public void test_systemUTC() { + Clock test = Clock.systemUTC(); + assertEquals(test.getZone(), ZoneOffset.UTC); + assertEquals(test, Clock.system(ZoneOffset.UTC)); + } + + public void test_systemDefaultZone() { + Clock test = Clock.systemDefaultZone(); + assertEquals(test.getZone(), ZoneId.systemDefault()); + assertEquals(test, Clock.system(ZoneId.systemDefault())); + } + + public void test_system_ZoneId() { + Clock test = Clock.system(PARIS); + assertEquals(test.getZone(), PARIS); + } + + @Test(expectedExceptions = NullPointerException.class) + public void test_zoneId_nullZoneId() { + Clock.system(null); + } + + //------------------------------------------------------------------------- + public void test_withZone() { + Clock test = Clock.system(PARIS); + Clock changed = test.withZone(MOSCOW); + assertEquals(test.getZone(), PARIS); + assertEquals(changed.getZone(), MOSCOW); + } + + public void test_withZone_equal() { + Clock test = Clock.system(PARIS); + Clock changed = test.withZone(PARIS); + assertEquals(changed.getZone(), PARIS); + } + + public void test_withZone_fromUTC() { + Clock test = Clock.systemUTC(); + Clock changed = test.withZone(PARIS); + assertEquals(changed.getZone(), PARIS); + } + + @Test(expectedExceptions = NullPointerException.class) + public void test_withZone_null() { + Clock.systemUTC().withZone(null); + } + + //----------------------------------------------------------------------- + public void test_equals() { + Clock a = Clock.systemUTC(); + Clock b = Clock.systemUTC(); + assertEquals(a.equals(a), true); + assertEquals(a.equals(b), true); + assertEquals(b.equals(a), true); + assertEquals(b.equals(b), true); + + Clock c = Clock.system(PARIS); + Clock d = Clock.system(PARIS); + assertEquals(c.equals(c), true); + assertEquals(c.equals(d), true); + assertEquals(d.equals(c), true); + assertEquals(d.equals(d), true); + + assertEquals(a.equals(c), false); + assertEquals(c.equals(a), false); + + assertEquals(a.equals(null), false); + assertEquals(a.equals("other type"), false); + assertEquals(a.equals(Clock.fixed(Instant.now(), ZoneOffset.UTC)), false); + } + + public void test_hashCode() { + Clock a = Clock.system(ZoneOffset.UTC); + Clock b = Clock.system(ZoneOffset.UTC); + assertEquals(a.hashCode(), a.hashCode()); + assertEquals(a.hashCode(), b.hashCode()); + + Clock c = Clock.system(PARIS); + assertEquals(a.hashCode() == c.hashCode(), false); + } + + //----------------------------------------------------------------------- + public void test_toString() { + // spec requires "full state" in toString() + Clock test = Clock.system(PARIS); + assertEquals(test.toString().contains("Europe/Paris"), true); + } + +} diff --git a/jdk/test/java/time/tck/java/time/TCKClock_Tick.java b/jdk/test/java/time/tck/java/time/TCKClock_Tick.java new file mode 100644 index 00000000000..3022d77b747 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/TCKClock_Tick.java @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012 Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertSame; + +import java.io.IOException; +import java.time.Clock; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; + +import org.testng.annotations.Test; + +/** + * Test tick clock. + */ +@Test +public class TCKClock_Tick extends AbstractTCKTest { + + private static final ZoneId MOSCOW = ZoneId.of("Europe/Moscow"); + private static final ZoneId PARIS = ZoneId.of("Europe/Paris"); + private static final Duration AMOUNT = Duration.ofSeconds(2); + private static final ZonedDateTime ZDT = LocalDateTime.of(2008, 6, 30, 11, 30, 10, 500).atZone(ZoneOffset.ofHours(2)); + private static final Instant INSTANT = ZDT.toInstant(); + + //----------------------------------------------------------------------- + public void test_isSerializable() throws IOException, ClassNotFoundException { + assertSerializable(Clock.tickSeconds(PARIS)); + assertSerializable(Clock.tickMinutes(MOSCOW)); + assertSerializable(Clock.tick(Clock.fixed(INSTANT, PARIS), AMOUNT)); + } + + //----------------------------------------------------------------------- + public void test_tick_ClockDuration_250millis() { + for (int i = 0; i < 1000; i++) { + Clock test = Clock.tick(Clock.fixed(ZDT.withNano(i * 1000_000).toInstant(), PARIS), Duration.ofMillis(250)); + assertEquals(test.instant(), ZDT.withNano((i / 250) * 250_000_000).toInstant()); + assertEquals(test.getZone(), PARIS); + } + } + + public void test_tick_ClockDuration_250micros() { + for (int i = 0; i < 1000; i++) { + Clock test = Clock.tick(Clock.fixed(ZDT.withNano(i * 1000).toInstant(), PARIS), Duration.ofNanos(250_000)); + assertEquals(test.instant(), ZDT.withNano((i / 250) * 250_000).toInstant()); + assertEquals(test.getZone(), PARIS); + } + } + + public void test_tick_ClockDuration_20nanos() { + for (int i = 0; i < 1000; i++) { + Clock test = Clock.tick(Clock.fixed(ZDT.withNano(i).toInstant(), PARIS), Duration.ofNanos(20)); + assertEquals(test.instant(), ZDT.withNano((i / 20) * 20).toInstant()); + assertEquals(test.getZone(), PARIS); + } + } + + public void test_tick_ClockDuration_zeroDuration() { + Clock underlying = Clock.system(PARIS); + Clock test = Clock.tick(underlying, Duration.ZERO); + assertSame(test, underlying); // spec says same + } + + public void test_tick_ClockDuration_1nsDuration() { + Clock underlying = Clock.system(PARIS); + Clock test = Clock.tick(underlying, Duration.ofNanos(1)); + assertSame(test, underlying); // spec says same + } + + @Test(expectedExceptions = ArithmeticException.class) + public void test_tick_ClockDuration_maxDuration() { + Clock.tick(Clock.systemUTC(), Duration.ofSeconds(Long.MAX_VALUE)); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void test_tick_ClockDuration_subMilliNotDivisible_123ns() { + Clock.tick(Clock.systemUTC(), Duration.ofSeconds(0, 123)); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void test_tick_ClockDuration_subMilliNotDivisible_999ns() { + Clock.tick(Clock.systemUTC(), Duration.ofSeconds(0, 999)); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void test_tick_ClockDuration_subMilliNotDivisible_999_999_999ns() { + Clock.tick(Clock.systemUTC(), Duration.ofSeconds(0, 999_999_999)); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void test_tick_ClockDuration_negative1ns() { + Clock.tick(Clock.systemUTC(), Duration.ofSeconds(0, -1)); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void test_tick_ClockDuration_negative1s() { + Clock.tick(Clock.systemUTC(), Duration.ofSeconds(-1)); + } + + @Test(expectedExceptions = NullPointerException.class) + public void test_tick_ClockDuration_nullClock() { + Clock.tick(null, Duration.ZERO); + } + + @Test(expectedExceptions = NullPointerException.class) + public void test_tick_ClockDuration_nullDuration() { + Clock.tick(Clock.systemUTC(), null); + } + + //----------------------------------------------------------------------- + public void test_tickSeconds_ZoneId() throws Exception { + Clock test = Clock.tickSeconds(PARIS); + assertEquals(test.getZone(), PARIS); + assertEquals(test.instant().getNano(), 0); + Thread.sleep(100); + assertEquals(test.instant().getNano(), 0); + } + + @Test(expectedExceptions = NullPointerException.class) + public void test_tickSeconds_ZoneId_nullZoneId() { + Clock.tickSeconds(null); + } + + //----------------------------------------------------------------------- + public void test_tickMinutes_ZoneId() { + Clock test = Clock.tickMinutes(PARIS); + assertEquals(test.getZone(), PARIS); + Instant instant = test.instant(); + assertEquals(instant.getEpochSecond() % 60, 0); + assertEquals(instant.getNano(), 0); + } + + @Test(expectedExceptions = NullPointerException.class) + public void test_tickMinutes_ZoneId_nullZoneId() { + Clock.tickMinutes(null); + } + + //------------------------------------------------------------------------- + public void test_withZone() { + Clock test = Clock.tick(Clock.system(PARIS), Duration.ofMillis(500)); + Clock changed = test.withZone(MOSCOW); + assertEquals(test.getZone(), PARIS); + assertEquals(changed.getZone(), MOSCOW); + } + + public void test_withZone_equal() { + Clock test = Clock.tick(Clock.system(PARIS), Duration.ofMillis(500)); + Clock changed = test.withZone(PARIS); + assertEquals(test, changed); + } + + @Test(expectedExceptions = NullPointerException.class) + public void test_withZone_null() { + Clock.tick(Clock.system(PARIS), Duration.ofMillis(500)).withZone(null); + } + + //----------------------------------------------------------------------- + public void test__equals() { + Clock a = Clock.tick(Clock.system(PARIS), Duration.ofMillis(500)); + Clock b = Clock.tick(Clock.system(PARIS), Duration.ofMillis(500)); + assertEquals(a.equals(a), true); + assertEquals(a.equals(b), true); + assertEquals(b.equals(a), true); + assertEquals(b.equals(b), true); + + Clock c = Clock.tick(Clock.system(MOSCOW), Duration.ofMillis(500)); + assertEquals(a.equals(c), false); + + Clock d = Clock.tick(Clock.system(PARIS), Duration.ofMillis(499)); + assertEquals(a.equals(d), false); + + assertEquals(a.equals(null), false); + assertEquals(a.equals("other type"), false); + assertEquals(a.equals(Clock.systemUTC()), false); + } + + public void test_hashCode() { + Clock a = Clock.tick(Clock.system(PARIS), Duration.ofMillis(500)); + Clock b = Clock.tick(Clock.system(PARIS), Duration.ofMillis(500)); + assertEquals(a.hashCode(), a.hashCode()); + assertEquals(a.hashCode(), b.hashCode()); + + Clock c = Clock.tick(Clock.system(MOSCOW), Duration.ofMillis(500)); + assertEquals(a.hashCode() == c.hashCode(), false); + + Clock d = Clock.tick(Clock.system(PARIS), Duration.ofMillis(499)); + assertEquals(a.hashCode() == d.hashCode(), false); + } + + //----------------------------------------------------------------------- + public void test_toString() { + // spec requires "full state" in toString() + Clock test = Clock.tick(Clock.system(PARIS), AMOUNT); + assertEquals(test.toString().contains("Europe/Paris"), true); + assertEquals(test.toString().contains("PT2S"), true); + } + +} diff --git a/jdk/test/java/time/tck/java/time/TCKDayOfWeek.java b/jdk/test/java/time/tck/java/time/TCKDayOfWeek.java new file mode 100644 index 00000000000..a2460898039 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/TCKDayOfWeek.java @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time; + +import static java.time.DayOfWeek.MONDAY; +import static java.time.DayOfWeek.SUNDAY; +import static java.time.DayOfWeek.WEDNESDAY; +import static java.time.temporal.ChronoField.DAY_OF_WEEK; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertSame; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +import java.time.DateTimeException; +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.Month; +import java.time.format.TextStyle; +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoUnit; +import java.time.temporal.ISOChrono; +import java.time.temporal.JulianFields; +import java.time.temporal.Queries; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalField; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test DayOfWeek. + */ +@Test +public class TCKDayOfWeek extends AbstractDateTimeTest { + + @BeforeMethod + public void setUp() { + } + + //----------------------------------------------------------------------- + @Override + protected List samples() { + TemporalAccessor[] array = {MONDAY, WEDNESDAY, SUNDAY, }; + return Arrays.asList(array); + } + + @Override + protected List validFields() { + TemporalField[] array = { + DAY_OF_WEEK, + }; + return Arrays.asList(array); + } + + @Override + protected List invalidFields() { + List list = new ArrayList<>(Arrays.asList(ChronoField.values())); + list.removeAll(validFields()); + list.add(JulianFields.JULIAN_DAY); + list.add(JulianFields.MODIFIED_JULIAN_DAY); + list.add(JulianFields.RATA_DIE); + return list; + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_factory_int_singleton() { + for (int i = 1; i <= 7; i++) { + DayOfWeek test = DayOfWeek.of(i); + assertEquals(test.getValue(), i); + assertSame(DayOfWeek.of(i), test); + } + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_int_valueTooLow() { + DayOfWeek.of(0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_int_valueTooHigh() { + DayOfWeek.of(8); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_factory_CalendricalObject() { + assertEquals(DayOfWeek.from(LocalDate.of(2011, 6, 6)), DayOfWeek.MONDAY); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_CalendricalObject_invalid_noDerive() { + DayOfWeek.from(LocalTime.of(12, 30)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_factory_CalendricalObject_null() { + DayOfWeek.from((TemporalAccessor) null); + } + + //----------------------------------------------------------------------- + // get(TemporalField) + //----------------------------------------------------------------------- + @Test + public void test_get_TemporalField() { + assertEquals(DayOfWeek.WEDNESDAY.getLong(ChronoField.DAY_OF_WEEK), 3); + } + + @Test + public void test_getLong_TemporalField() { + assertEquals(DayOfWeek.WEDNESDAY.getLong(ChronoField.DAY_OF_WEEK), 3); + } + + //----------------------------------------------------------------------- + // query(TemporalQuery) + //----------------------------------------------------------------------- + @Test + public void test_query_chrono() { + assertEquals(DayOfWeek.FRIDAY.query(Queries.chrono()), null); + assertEquals(Queries.chrono().queryFrom(DayOfWeek.FRIDAY), null); + } + + @Test + public void test_query_zoneId() { + assertEquals(DayOfWeek.FRIDAY.query(Queries.zoneId()), null); + assertEquals(Queries.zoneId().queryFrom(DayOfWeek.FRIDAY), null); + } + + @Test + public void test_query_precision() { + assertEquals(DayOfWeek.FRIDAY.query(Queries.precision()), ChronoUnit.DAYS); + assertEquals(Queries.precision().queryFrom(DayOfWeek.FRIDAY), ChronoUnit.DAYS); + } + + @Test + public void test_query_offset() { + assertEquals(DayOfWeek.FRIDAY.query(Queries.offset()), null); + assertEquals(Queries.offset().queryFrom(DayOfWeek.FRIDAY), null); + } + + @Test + public void test_query_zone() { + assertEquals(DayOfWeek.FRIDAY.query(Queries.zone()), null); + assertEquals(Queries.zone().queryFrom(DayOfWeek.FRIDAY), null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_query_null() { + DayOfWeek.FRIDAY.query(null); + } + + //----------------------------------------------------------------------- + // getText() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_getText() { + assertEquals(DayOfWeek.MONDAY.getText(TextStyle.SHORT, Locale.US), "Mon"); + } + + @Test(expectedExceptions = NullPointerException.class, groups={"tck"}) + public void test_getText_nullStyle() { + DayOfWeek.MONDAY.getText(null, Locale.US); + } + + @Test(expectedExceptions = NullPointerException.class, groups={"tck"}) + public void test_getText_nullLocale() { + DayOfWeek.MONDAY.getText(TextStyle.FULL, null); + } + + //----------------------------------------------------------------------- + // plus(long), plus(long,unit) + //----------------------------------------------------------------------- + @DataProvider(name="plus") + Object[][] data_plus() { + return new Object[][] { + {1, -8, 7}, + {1, -7, 1}, + {1, -6, 2}, + {1, -5, 3}, + {1, -4, 4}, + {1, -3, 5}, + {1, -2, 6}, + {1, -1, 7}, + {1, 0, 1}, + {1, 1, 2}, + {1, 2, 3}, + {1, 3, 4}, + {1, 4, 5}, + {1, 5, 6}, + {1, 6, 7}, + {1, 7, 1}, + {1, 8, 2}, + + {1, 1, 2}, + {2, 1, 3}, + {3, 1, 4}, + {4, 1, 5}, + {5, 1, 6}, + {6, 1, 7}, + {7, 1, 1}, + + {1, -1, 7}, + {2, -1, 1}, + {3, -1, 2}, + {4, -1, 3}, + {5, -1, 4}, + {6, -1, 5}, + {7, -1, 6}, + }; + } + + @Test(dataProvider="plus", groups={"tck"}) + public void test_plus_long(int base, long amount, int expected) { + assertEquals(DayOfWeek.of(base).plus(amount), DayOfWeek.of(expected)); + } + + //----------------------------------------------------------------------- + // minus(long), minus(long,unit) + //----------------------------------------------------------------------- + @DataProvider(name="minus") + Object[][] data_minus() { + return new Object[][] { + {1, -8, 2}, + {1, -7, 1}, + {1, -6, 7}, + {1, -5, 6}, + {1, -4, 5}, + {1, -3, 4}, + {1, -2, 3}, + {1, -1, 2}, + {1, 0, 1}, + {1, 1, 7}, + {1, 2, 6}, + {1, 3, 5}, + {1, 4, 4}, + {1, 5, 3}, + {1, 6, 2}, + {1, 7, 1}, + {1, 8, 7}, + }; + } + + @Test(dataProvider="minus", groups={"tck"}) + public void test_minus_long(int base, long amount, int expected) { + assertEquals(DayOfWeek.of(base).minus(amount), DayOfWeek.of(expected)); + } + + //----------------------------------------------------------------------- + // adjustInto() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_adjustInto() { + assertEquals(DayOfWeek.MONDAY.adjustInto(LocalDate.of(2012, 9, 2)), LocalDate.of(2012, 8, 27)); + assertEquals(DayOfWeek.MONDAY.adjustInto(LocalDate.of(2012, 9, 3)), LocalDate.of(2012, 9, 3)); + assertEquals(DayOfWeek.MONDAY.adjustInto(LocalDate.of(2012, 9, 4)), LocalDate.of(2012, 9, 3)); + assertEquals(DayOfWeek.MONDAY.adjustInto(LocalDate.of(2012, 9, 10)), LocalDate.of(2012, 9, 10)); + assertEquals(DayOfWeek.MONDAY.adjustInto(LocalDate.of(2012, 9, 11)), LocalDate.of(2012, 9, 10)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_adjustInto_null() { + DayOfWeek.MONDAY.adjustInto((Temporal) null); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toString() { + assertEquals(DayOfWeek.MONDAY.toString(), "MONDAY"); + assertEquals(DayOfWeek.TUESDAY.toString(), "TUESDAY"); + assertEquals(DayOfWeek.WEDNESDAY.toString(), "WEDNESDAY"); + assertEquals(DayOfWeek.THURSDAY.toString(), "THURSDAY"); + assertEquals(DayOfWeek.FRIDAY.toString(), "FRIDAY"); + assertEquals(DayOfWeek.SATURDAY.toString(), "SATURDAY"); + assertEquals(DayOfWeek.SUNDAY.toString(), "SUNDAY"); + } + + //----------------------------------------------------------------------- + // generated methods + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_enum() { + assertEquals(DayOfWeek.valueOf("MONDAY"), DayOfWeek.MONDAY); + assertEquals(DayOfWeek.values()[0], DayOfWeek.MONDAY); + } + +} diff --git a/jdk/test/java/time/tck/java/time/TCKDuration.java b/jdk/test/java/time/tck/java/time/TCKDuration.java new file mode 100644 index 00000000000..aeba804291d --- /dev/null +++ b/jdk/test/java/time/tck/java/time/TCKDuration.java @@ -0,0 +1,2135 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time; + +import static java.time.temporal.ChronoUnit.DAYS; +import static java.time.temporal.ChronoUnit.HALF_DAYS; +import static java.time.temporal.ChronoUnit.HOURS; +import static java.time.temporal.ChronoUnit.MICROS; +import static java.time.temporal.ChronoUnit.MILLIS; +import static java.time.temporal.ChronoUnit.MINUTES; +import static java.time.temporal.ChronoUnit.NANOS; +import static java.time.temporal.ChronoUnit.SECONDS; +import static java.time.temporal.ChronoUnit.WEEKS; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import java.time.DateTimeException; +import java.time.Duration; +import java.time.Instant; +import java.time.format.DateTimeParseException; +import java.time.temporal.TemporalUnit; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test Duration. + */ +@Test +public class TCKDuration extends AbstractTCKTest { + + //----------------------------------------------------------------------- + @Test + public void test_serialization() throws Exception { + assertSerializable(Duration.ofHours(5)); + assertSerializable(Duration.ofHours(0)); + assertSerializable(Duration.ofHours(-5)); + } + + @Test + public void test_serialization_format() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baos) ) { + dos.writeByte(1); + dos.writeLong(654321); + dos.writeInt(123456789); + } + byte[] bytes = baos.toByteArray(); + assertSerializedBySer(Duration.ofSeconds(654321, 123456789), bytes); + } + + //----------------------------------------------------------------------- + // constants + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_zero() { + assertEquals(Duration.ZERO.getSeconds(), 0L); + assertEquals(Duration.ZERO.getNano(), 0); + } + + //----------------------------------------------------------------------- + // ofSeconds(long) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_seconds_long() { + for (long i = -2; i <= 2; i++) { + Duration t = Duration.ofSeconds(i); + assertEquals(t.getSeconds(), i); + assertEquals(t.getNano(), 0); + } + } + + //----------------------------------------------------------------------- + // ofSeconds(long,long) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_seconds_long_long() { + for (long i = -2; i <= 2; i++) { + for (int j = 0; j < 10; j++) { + Duration t = Duration.ofSeconds(i, j); + assertEquals(t.getSeconds(), i); + assertEquals(t.getNano(), j); + } + for (int j = -10; j < 0; j++) { + Duration t = Duration.ofSeconds(i, j); + assertEquals(t.getSeconds(), i - 1); + assertEquals(t.getNano(), j + 1000000000); + } + for (int j = 999999990; j < 1000000000; j++) { + Duration t = Duration.ofSeconds(i, j); + assertEquals(t.getSeconds(), i); + assertEquals(t.getNano(), j); + } + } + } + + @Test(groups={"tck"}) + public void factory_seconds_long_long_nanosNegativeAdjusted() { + Duration test = Duration.ofSeconds(2L, -1); + assertEquals(test.getSeconds(), 1); + assertEquals(test.getNano(), 999999999); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void factory_seconds_long_long_tooBig() { + Duration.ofSeconds(Long.MAX_VALUE, 1000000000); + } + + //----------------------------------------------------------------------- + // ofMillis(long) + //----------------------------------------------------------------------- + @DataProvider(name="MillisDurationNoNanos") + Object[][] provider_factory_millis_long() { + return new Object[][] { + {0, 0, 0}, + {1, 0, 1000000}, + {2, 0, 2000000}, + {999, 0, 999000000}, + {1000, 1, 0}, + {1001, 1, 1000000}, + {-1, -1, 999000000}, + {-2, -1, 998000000}, + {-999, -1, 1000000}, + {-1000, -1, 0}, + {-1001, -2, 999000000}, + }; + } + + @Test(dataProvider="MillisDurationNoNanos", groups={"tck"}) + public void factory_millis_long(long millis, long expectedSeconds, int expectedNanoOfSecond) { + Duration test = Duration.ofMillis(millis); + assertEquals(test.getSeconds(), expectedSeconds); + assertEquals(test.getNano(), expectedNanoOfSecond); + } + + //----------------------------------------------------------------------- + // ofNanos(long) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_nanos_nanos() { + Duration test = Duration.ofNanos(1); + assertEquals(test.getSeconds(), 0); + assertEquals(test.getNano(), 1); + } + + @Test(groups={"tck"}) + public void factory_nanos_nanosSecs() { + Duration test = Duration.ofNanos(1000000002); + assertEquals(test.getSeconds(), 1); + assertEquals(test.getNano(), 2); + } + + @Test(groups={"tck"}) + public void factory_nanos_negative() { + Duration test = Duration.ofNanos(-2000000001); + assertEquals(test.getSeconds(), -3); + assertEquals(test.getNano(), 999999999); + } + + @Test(groups={"tck"}) + public void factory_nanos_max() { + Duration test = Duration.ofNanos(Long.MAX_VALUE); + assertEquals(test.getSeconds(), Long.MAX_VALUE / 1000000000); + assertEquals(test.getNano(), Long.MAX_VALUE % 1000000000); + } + + @Test(groups={"tck"}) + public void factory_nanos_min() { + Duration test = Duration.ofNanos(Long.MIN_VALUE); + assertEquals(test.getSeconds(), Long.MIN_VALUE / 1000000000 - 1); + assertEquals(test.getNano(), Long.MIN_VALUE % 1000000000 + 1000000000); + } + + //----------------------------------------------------------------------- + // ofMinutes() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_minutes() { + Duration test = Duration.ofMinutes(2); + assertEquals(test.getSeconds(), 120); + assertEquals(test.getNano(), 0); + } + + @Test(groups={"tck"}) + public void factory_minutes_max() { + Duration test = Duration.ofMinutes(Long.MAX_VALUE / 60); + assertEquals(test.getSeconds(), (Long.MAX_VALUE / 60) * 60); + assertEquals(test.getNano(), 0); + } + + @Test(groups={"tck"}) + public void factory_minutes_min() { + Duration test = Duration.ofMinutes(Long.MIN_VALUE / 60); + assertEquals(test.getSeconds(), (Long.MIN_VALUE / 60) * 60); + assertEquals(test.getNano(), 0); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void factory_minutes_tooBig() { + Duration.ofMinutes(Long.MAX_VALUE / 60 + 1); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void factory_minutes_tooSmall() { + Duration.ofMinutes(Long.MIN_VALUE / 60 - 1); + } + + //----------------------------------------------------------------------- + // ofHours() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_hours() { + Duration test = Duration.ofHours(2); + assertEquals(test.getSeconds(), 2 * 3600); + assertEquals(test.getNano(), 0); + } + + @Test(groups={"tck"}) + public void factory_hours_max() { + Duration test = Duration.ofHours(Long.MAX_VALUE / 3600); + assertEquals(test.getSeconds(), (Long.MAX_VALUE / 3600) * 3600); + assertEquals(test.getNano(), 0); + } + + @Test(groups={"tck"}) + public void factory_hours_min() { + Duration test = Duration.ofHours(Long.MIN_VALUE / 3600); + assertEquals(test.getSeconds(), (Long.MIN_VALUE / 3600) * 3600); + assertEquals(test.getNano(), 0); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void factory_hours_tooBig() { + Duration.ofHours(Long.MAX_VALUE / 3600 + 1); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void factory_hours_tooSmall() { + Duration.ofHours(Long.MIN_VALUE / 3600 - 1); + } + + //----------------------------------------------------------------------- + // ofDays() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_days() { + Duration test = Duration.ofDays(2); + assertEquals(test.getSeconds(), 2 * 86400); + assertEquals(test.getNano(), 0); + } + + @Test(groups={"tck"}) + public void factory_days_max() { + Duration test = Duration.ofDays(Long.MAX_VALUE / 86400); + assertEquals(test.getSeconds(), (Long.MAX_VALUE / 86400) * 86400); + assertEquals(test.getNano(), 0); + } + + @Test(groups={"tck"}) + public void factory_days_min() { + Duration test = Duration.ofDays(Long.MIN_VALUE / 86400); + assertEquals(test.getSeconds(), (Long.MIN_VALUE / 86400) * 86400); + assertEquals(test.getNano(), 0); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void factory_days_tooBig() { + Duration.ofDays(Long.MAX_VALUE / 86400 + 1); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void factory_days_tooSmall() { + Duration.ofDays(Long.MIN_VALUE / 86400 - 1); + } + + //----------------------------------------------------------------------- + // of(long,TemporalUnit) + //----------------------------------------------------------------------- + @DataProvider(name="OfTemporalUnit") + Object[][] provider_factory_of_longTemporalUnit() { + return new Object[][] { + {0, NANOS, 0, 0}, + {0, MICROS, 0, 0}, + {0, MILLIS, 0, 0}, + {0, SECONDS, 0, 0}, + {0, MINUTES, 0, 0}, + {0, HOURS, 0, 0}, + {0, HALF_DAYS, 0, 0}, + {0, DAYS, 0, 0}, + {1, NANOS, 0, 1}, + {1, MICROS, 0, 1000}, + {1, MILLIS, 0, 1000000}, + {1, SECONDS, 1, 0}, + {1, MINUTES, 60, 0}, + {1, HOURS, 3600, 0}, + {1, HALF_DAYS, 43200, 0}, + {1, DAYS, 86400, 0}, + {3, NANOS, 0, 3}, + {3, MICROS, 0, 3000}, + {3, MILLIS, 0, 3000000}, + {3, SECONDS, 3, 0}, + {3, MINUTES, 3 * 60, 0}, + {3, HOURS, 3 * 3600, 0}, + {3, HALF_DAYS, 3 * 43200, 0}, + {3, DAYS, 3 * 86400, 0}, + {-1, NANOS, -1, 999999999}, + {-1, MICROS, -1, 999999000}, + {-1, MILLIS, -1, 999000000}, + {-1, SECONDS, -1, 0}, + {-1, MINUTES, -60, 0}, + {-1, HOURS, -3600, 0}, + {-1, HALF_DAYS, -43200, 0}, + {-1, DAYS, -86400, 0}, + {-3, NANOS, -1, 999999997}, + {-3, MICROS, -1, 999997000}, + {-3, MILLIS, -1, 997000000}, + {-3, SECONDS, -3, 0}, + {-3, MINUTES, -3 * 60, 0}, + {-3, HOURS, -3 * 3600, 0}, + {-3, HALF_DAYS, -3 * 43200, 0}, + {-3, DAYS, -3 * 86400, 0}, + {Long.MAX_VALUE, NANOS, Long.MAX_VALUE / 1000000000, (int) (Long.MAX_VALUE % 1000000000)}, + {Long.MIN_VALUE, NANOS, Long.MIN_VALUE / 1000000000 - 1, (int) (Long.MIN_VALUE % 1000000000 + 1000000000)}, + {Long.MAX_VALUE, MICROS, Long.MAX_VALUE / 1000000, (int) ((Long.MAX_VALUE % 1000000) * 1000)}, + {Long.MIN_VALUE, MICROS, Long.MIN_VALUE / 1000000 - 1, (int) ((Long.MIN_VALUE % 1000000 + 1000000) * 1000)}, + {Long.MAX_VALUE, MILLIS, Long.MAX_VALUE / 1000, (int) ((Long.MAX_VALUE % 1000) * 1000000)}, + {Long.MIN_VALUE, MILLIS, Long.MIN_VALUE / 1000 - 1, (int) ((Long.MIN_VALUE % 1000 + 1000) * 1000000)}, + {Long.MAX_VALUE, SECONDS, Long.MAX_VALUE, 0}, + {Long.MIN_VALUE, SECONDS, Long.MIN_VALUE, 0}, + {Long.MAX_VALUE / 60, MINUTES, (Long.MAX_VALUE / 60) * 60, 0}, + {Long.MIN_VALUE / 60, MINUTES, (Long.MIN_VALUE / 60) * 60, 0}, + {Long.MAX_VALUE / 3600, HOURS, (Long.MAX_VALUE / 3600) * 3600, 0}, + {Long.MIN_VALUE / 3600, HOURS, (Long.MIN_VALUE / 3600) * 3600, 0}, + {Long.MAX_VALUE / 43200, HALF_DAYS, (Long.MAX_VALUE / 43200) * 43200, 0}, + {Long.MIN_VALUE / 43200, HALF_DAYS, (Long.MIN_VALUE / 43200) * 43200, 0}, + }; + } + + @Test(dataProvider="OfTemporalUnit", groups={"tck"}) + public void factory_of_longTemporalUnit(long amount, TemporalUnit unit, long expectedSeconds, int expectedNanoOfSecond) { + Duration t = Duration.of(amount, unit); + assertEquals(t.getSeconds(), expectedSeconds); + assertEquals(t.getNano(), expectedNanoOfSecond); + } + + @DataProvider(name="OfTemporalUnitOutOfRange") + Object[][] provider_factory_of_longTemporalUnit_outOfRange() { + return new Object[][] { + {Long.MAX_VALUE / 60 + 1, MINUTES}, + {Long.MIN_VALUE / 60 - 1, MINUTES}, + {Long.MAX_VALUE / 3600 + 1, HOURS}, + {Long.MIN_VALUE / 3600 - 1, HOURS}, + {Long.MAX_VALUE / 43200 + 1, HALF_DAYS}, + {Long.MIN_VALUE / 43200 - 1, HALF_DAYS}, + }; + } + + @Test(dataProvider="OfTemporalUnitOutOfRange", expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void factory_of_longTemporalUnit_outOfRange(long amount, TemporalUnit unit) { + Duration.of(amount, unit); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_longTemporalUnit_estimatedUnit() { + Duration.of(2, WEEKS); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_of_longTemporalUnit_null() { + Duration.of(1, (TemporalUnit) null); + } + + //----------------------------------------------------------------------- + // between() + //----------------------------------------------------------------------- + @DataProvider(name="DurationBetween") + Object[][] provider_factory_between_Instant_Instant() { + return new Object[][] { + {0, 0, 0, 0, 0, 0}, + {3, 0, 7, 0, 4, 0}, + {3, 20, 7, 50, 4, 30}, + {3, 80, 7, 50, 3, 999999970}, + {7, 0, 3, 0, -4, 0}, + }; + } + + @Test(dataProvider="DurationBetween", groups={"tck"}) + public void factory_between_Instant_Instant(long secs1, int nanos1, long secs2, int nanos2, long expectedSeconds, int expectedNanoOfSecond) { + Instant start = Instant.ofEpochSecond(secs1, nanos1); + Instant end = Instant.ofEpochSecond(secs2, nanos2); + Duration t = Duration.between(start, end); + assertEquals(t.getSeconds(), expectedSeconds); + assertEquals(t.getNano(), expectedNanoOfSecond); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_between_Instant_Instant_startNull() { + Instant end = Instant.ofEpochSecond(1); + Duration.between(null, end); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_between_Instant_Instant_endNull() { + Instant start = Instant.ofEpochSecond(1); + Duration.between(start, null); + } + + //----------------------------------------------------------------------- + // parse(String) + //----------------------------------------------------------------------- + @DataProvider(name="Parse") + Object[][] provider_factory_parse() { + return new Object[][] { + {"PT0S", 0, 0}, + {"pT0S", 0, 0}, + {"Pt0S", 0, 0}, + {"PT0s", 0, 0}, + + {"PT1S", 1, 0}, + {"PT12S", 12, 0}, + {"PT123456789S", 123456789, 0}, + {"PT" + Long.MAX_VALUE + "S", Long.MAX_VALUE, 0}, + + {"PT-1S", -1, 0}, + {"PT-12S", -12, 0}, + {"PT-123456789S", -123456789, 0}, + {"PT" + Long.MIN_VALUE + "S", Long.MIN_VALUE, 0}, + + {"PT1.1S", 1, 100000000}, + {"PT1.12S", 1, 120000000}, + {"PT1.123S", 1, 123000000}, + {"PT1.1234S", 1, 123400000}, + {"PT1.12345S", 1, 123450000}, + {"PT1.123456S", 1, 123456000}, + {"PT1.1234567S", 1, 123456700}, + {"PT1.12345678S", 1, 123456780}, + {"PT1.123456789S", 1, 123456789}, + + {"PT-1.1S", -2, 1000000000 - 100000000}, + {"PT-1.12S", -2, 1000000000 - 120000000}, + {"PT-1.123S", -2, 1000000000 - 123000000}, + {"PT-1.1234S", -2, 1000000000 - 123400000}, + {"PT-1.12345S", -2, 1000000000 - 123450000}, + {"PT-1.123456S", -2, 1000000000 - 123456000}, + {"PT-1.1234567S", -2, 1000000000 - 123456700}, + {"PT-1.12345678S", -2, 1000000000 - 123456780}, + {"PT-1.123456789S", -2, 1000000000 - 123456789}, + + {"PT" + Long.MAX_VALUE + ".123456789S", Long.MAX_VALUE, 123456789}, + {"PT" + Long.MIN_VALUE + ".000000000S", Long.MIN_VALUE, 0}, + }; + } + + @Test(dataProvider="Parse", groups={"tck"}) + public void factory_parse(String text, long expectedSeconds, int expectedNanoOfSecond) { + Duration t = Duration.parse(text); + assertEquals(t.getSeconds(), expectedSeconds); + assertEquals(t.getNano(), expectedNanoOfSecond); + } + + @Test(dataProvider="Parse", groups={"tck"}) + public void factory_parse_comma(String text, long expectedSeconds, int expectedNanoOfSecond) { + text = text.replace('.', ','); + Duration t = Duration.parse(text); + assertEquals(t.getSeconds(), expectedSeconds); + assertEquals(t.getNano(), expectedNanoOfSecond); + } + + @DataProvider(name="ParseFailures") + Object[][] provider_factory_parseFailures() { + return new Object[][] { + {""}, + {"PTS"}, + {"AT0S"}, + {"PA0S"}, + {"PT0A"}, + + {"PT+S"}, + {"PT-S"}, + {"PT.S"}, + {"PTAS"}, + + {"PT+0S"}, + {"PT+00S"}, + {"PT+000S"}, + {"PT-0S"}, + {"PT-00S"}, + {"PT-000S"}, + {"PT+1S"}, + {"PT-.S"}, + {"PT+.S"}, + + {"PT1ABC2S"}, + {"PT1.1ABC2S"}, + + {"PT123456789123456789123456789S"}, + {"PT0.1234567891S"}, + {"PT1.S"}, + {"PT.1S"}, + + {"PT2.-3"}, + {"PT-2.-3"}, + {"PT2.+3"}, + {"PT-2.+3"}, + }; + } + + @Test(dataProvider="ParseFailures", expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parseFailures(String text) { + Duration.parse(text); + } + + @Test(dataProvider="ParseFailures", expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parseFailures_comma(String text) { + text = text.replace('.', ','); + Duration.parse(text); + } + + @Test(expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_tooBig() { + Duration.parse("PT" + Long.MAX_VALUE + "1S"); + } + + @Test(expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_tooBig_decimal() { + Duration.parse("PT" + Long.MAX_VALUE + "1.1S"); + } + + @Test(expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_tooSmall() { + Duration.parse("PT" + Long.MIN_VALUE + "1S"); + } + + @Test(expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_tooSmall_decimal() { + Duration.parse("PT" + Long.MIN_VALUE + ".1S"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_nullText() { + Duration.parse((String) null); + } + + @Test(groups={"tck"}) + public void test_deserialization() throws Exception { + Duration orginal = Duration.ofSeconds(2); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(baos); + out.writeObject(orginal); + out.close(); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream in = new ObjectInputStream(bais); + Duration ser = (Duration) in.readObject(); + assertEquals(Duration.ofSeconds(2), ser); + } + + //----------------------------------------------------------------------- + // isZero(), isPositive(), isPositiveOrZero(), isNegative(), isNegativeOrZero() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_isZero() { + assertEquals(Duration.ofNanos(0).isZero(), true); + assertEquals(Duration.ofSeconds(0).isZero(), true); + assertEquals(Duration.ofNanos(1).isZero(), false); + assertEquals(Duration.ofSeconds(1).isZero(), false); + assertEquals(Duration.ofSeconds(1, 1).isZero(), false); + assertEquals(Duration.ofNanos(-1).isZero(), false); + assertEquals(Duration.ofSeconds(-1).isZero(), false); + assertEquals(Duration.ofSeconds(-1, -1).isZero(), false); + } + + @Test(groups={"tck"}) + public void test_isPositive() { + assertEquals(Duration.ofNanos(0).isPositive(), false); + assertEquals(Duration.ofSeconds(0).isPositive(), false); + assertEquals(Duration.ofNanos(1).isPositive(), true); + assertEquals(Duration.ofSeconds(1).isPositive(), true); + assertEquals(Duration.ofSeconds(1, 1).isPositive(), true); + assertEquals(Duration.ofNanos(-1).isPositive(), false); + assertEquals(Duration.ofSeconds(-1).isPositive(), false); + assertEquals(Duration.ofSeconds(-1, -1).isPositive(), false); + } + + @Test(groups={"tck"}) + public void test_isNegative() { + assertEquals(Duration.ofNanos(0).isNegative(), false); + assertEquals(Duration.ofSeconds(0).isNegative(), false); + assertEquals(Duration.ofNanos(1).isNegative(), false); + assertEquals(Duration.ofSeconds(1).isNegative(), false); + assertEquals(Duration.ofSeconds(1, 1).isNegative(), false); + assertEquals(Duration.ofNanos(-1).isNegative(), true); + assertEquals(Duration.ofSeconds(-1).isNegative(), true); + assertEquals(Duration.ofSeconds(-1, -1).isNegative(), true); + } + + //----------------------------------------------------------------------- + // plus() + //----------------------------------------------------------------------- + @DataProvider(name="Plus") + Object[][] provider_plus() { + return new Object[][] { + {Long.MIN_VALUE, 0, Long.MAX_VALUE, 0, -1, 0}, + + {-4, 666666667, -4, 666666667, -7, 333333334}, + {-4, 666666667, -3, 0, -7, 666666667}, + {-4, 666666667, -2, 0, -6, 666666667}, + {-4, 666666667, -1, 0, -5, 666666667}, + {-4, 666666667, -1, 333333334, -4, 1}, + {-4, 666666667, -1, 666666667, -4, 333333334}, + {-4, 666666667, -1, 999999999, -4, 666666666}, + {-4, 666666667, 0, 0, -4, 666666667}, + {-4, 666666667, 0, 1, -4, 666666668}, + {-4, 666666667, 0, 333333333, -3, 0}, + {-4, 666666667, 0, 666666666, -3, 333333333}, + {-4, 666666667, 1, 0, -3, 666666667}, + {-4, 666666667, 2, 0, -2, 666666667}, + {-4, 666666667, 3, 0, -1, 666666667}, + {-4, 666666667, 3, 333333333, 0, 0}, + + {-3, 0, -4, 666666667, -7, 666666667}, + {-3, 0, -3, 0, -6, 0}, + {-3, 0, -2, 0, -5, 0}, + {-3, 0, -1, 0, -4, 0}, + {-3, 0, -1, 333333334, -4, 333333334}, + {-3, 0, -1, 666666667, -4, 666666667}, + {-3, 0, -1, 999999999, -4, 999999999}, + {-3, 0, 0, 0, -3, 0}, + {-3, 0, 0, 1, -3, 1}, + {-3, 0, 0, 333333333, -3, 333333333}, + {-3, 0, 0, 666666666, -3, 666666666}, + {-3, 0, 1, 0, -2, 0}, + {-3, 0, 2, 0, -1, 0}, + {-3, 0, 3, 0, 0, 0}, + {-3, 0, 3, 333333333, 0, 333333333}, + + {-2, 0, -4, 666666667, -6, 666666667}, + {-2, 0, -3, 0, -5, 0}, + {-2, 0, -2, 0, -4, 0}, + {-2, 0, -1, 0, -3, 0}, + {-2, 0, -1, 333333334, -3, 333333334}, + {-2, 0, -1, 666666667, -3, 666666667}, + {-2, 0, -1, 999999999, -3, 999999999}, + {-2, 0, 0, 0, -2, 0}, + {-2, 0, 0, 1, -2, 1}, + {-2, 0, 0, 333333333, -2, 333333333}, + {-2, 0, 0, 666666666, -2, 666666666}, + {-2, 0, 1, 0, -1, 0}, + {-2, 0, 2, 0, 0, 0}, + {-2, 0, 3, 0, 1, 0}, + {-2, 0, 3, 333333333, 1, 333333333}, + + {-1, 0, -4, 666666667, -5, 666666667}, + {-1, 0, -3, 0, -4, 0}, + {-1, 0, -2, 0, -3, 0}, + {-1, 0, -1, 0, -2, 0}, + {-1, 0, -1, 333333334, -2, 333333334}, + {-1, 0, -1, 666666667, -2, 666666667}, + {-1, 0, -1, 999999999, -2, 999999999}, + {-1, 0, 0, 0, -1, 0}, + {-1, 0, 0, 1, -1, 1}, + {-1, 0, 0, 333333333, -1, 333333333}, + {-1, 0, 0, 666666666, -1, 666666666}, + {-1, 0, 1, 0, 0, 0}, + {-1, 0, 2, 0, 1, 0}, + {-1, 0, 3, 0, 2, 0}, + {-1, 0, 3, 333333333, 2, 333333333}, + + {-1, 666666667, -4, 666666667, -4, 333333334}, + {-1, 666666667, -3, 0, -4, 666666667}, + {-1, 666666667, -2, 0, -3, 666666667}, + {-1, 666666667, -1, 0, -2, 666666667}, + {-1, 666666667, -1, 333333334, -1, 1}, + {-1, 666666667, -1, 666666667, -1, 333333334}, + {-1, 666666667, -1, 999999999, -1, 666666666}, + {-1, 666666667, 0, 0, -1, 666666667}, + {-1, 666666667, 0, 1, -1, 666666668}, + {-1, 666666667, 0, 333333333, 0, 0}, + {-1, 666666667, 0, 666666666, 0, 333333333}, + {-1, 666666667, 1, 0, 0, 666666667}, + {-1, 666666667, 2, 0, 1, 666666667}, + {-1, 666666667, 3, 0, 2, 666666667}, + {-1, 666666667, 3, 333333333, 3, 0}, + + {0, 0, -4, 666666667, -4, 666666667}, + {0, 0, -3, 0, -3, 0}, + {0, 0, -2, 0, -2, 0}, + {0, 0, -1, 0, -1, 0}, + {0, 0, -1, 333333334, -1, 333333334}, + {0, 0, -1, 666666667, -1, 666666667}, + {0, 0, -1, 999999999, -1, 999999999}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 1, 0, 1}, + {0, 0, 0, 333333333, 0, 333333333}, + {0, 0, 0, 666666666, 0, 666666666}, + {0, 0, 1, 0, 1, 0}, + {0, 0, 2, 0, 2, 0}, + {0, 0, 3, 0, 3, 0}, + {0, 0, 3, 333333333, 3, 333333333}, + + {0, 333333333, -4, 666666667, -3, 0}, + {0, 333333333, -3, 0, -3, 333333333}, + {0, 333333333, -2, 0, -2, 333333333}, + {0, 333333333, -1, 0, -1, 333333333}, + {0, 333333333, -1, 333333334, -1, 666666667}, + {0, 333333333, -1, 666666667, 0, 0}, + {0, 333333333, -1, 999999999, 0, 333333332}, + {0, 333333333, 0, 0, 0, 333333333}, + {0, 333333333, 0, 1, 0, 333333334}, + {0, 333333333, 0, 333333333, 0, 666666666}, + {0, 333333333, 0, 666666666, 0, 999999999}, + {0, 333333333, 1, 0, 1, 333333333}, + {0, 333333333, 2, 0, 2, 333333333}, + {0, 333333333, 3, 0, 3, 333333333}, + {0, 333333333, 3, 333333333, 3, 666666666}, + + {1, 0, -4, 666666667, -3, 666666667}, + {1, 0, -3, 0, -2, 0}, + {1, 0, -2, 0, -1, 0}, + {1, 0, -1, 0, 0, 0}, + {1, 0, -1, 333333334, 0, 333333334}, + {1, 0, -1, 666666667, 0, 666666667}, + {1, 0, -1, 999999999, 0, 999999999}, + {1, 0, 0, 0, 1, 0}, + {1, 0, 0, 1, 1, 1}, + {1, 0, 0, 333333333, 1, 333333333}, + {1, 0, 0, 666666666, 1, 666666666}, + {1, 0, 1, 0, 2, 0}, + {1, 0, 2, 0, 3, 0}, + {1, 0, 3, 0, 4, 0}, + {1, 0, 3, 333333333, 4, 333333333}, + + {2, 0, -4, 666666667, -2, 666666667}, + {2, 0, -3, 0, -1, 0}, + {2, 0, -2, 0, 0, 0}, + {2, 0, -1, 0, 1, 0}, + {2, 0, -1, 333333334, 1, 333333334}, + {2, 0, -1, 666666667, 1, 666666667}, + {2, 0, -1, 999999999, 1, 999999999}, + {2, 0, 0, 0, 2, 0}, + {2, 0, 0, 1, 2, 1}, + {2, 0, 0, 333333333, 2, 333333333}, + {2, 0, 0, 666666666, 2, 666666666}, + {2, 0, 1, 0, 3, 0}, + {2, 0, 2, 0, 4, 0}, + {2, 0, 3, 0, 5, 0}, + {2, 0, 3, 333333333, 5, 333333333}, + + {3, 0, -4, 666666667, -1, 666666667}, + {3, 0, -3, 0, 0, 0}, + {3, 0, -2, 0, 1, 0}, + {3, 0, -1, 0, 2, 0}, + {3, 0, -1, 333333334, 2, 333333334}, + {3, 0, -1, 666666667, 2, 666666667}, + {3, 0, -1, 999999999, 2, 999999999}, + {3, 0, 0, 0, 3, 0}, + {3, 0, 0, 1, 3, 1}, + {3, 0, 0, 333333333, 3, 333333333}, + {3, 0, 0, 666666666, 3, 666666666}, + {3, 0, 1, 0, 4, 0}, + {3, 0, 2, 0, 5, 0}, + {3, 0, 3, 0, 6, 0}, + {3, 0, 3, 333333333, 6, 333333333}, + + {3, 333333333, -4, 666666667, 0, 0}, + {3, 333333333, -3, 0, 0, 333333333}, + {3, 333333333, -2, 0, 1, 333333333}, + {3, 333333333, -1, 0, 2, 333333333}, + {3, 333333333, -1, 333333334, 2, 666666667}, + {3, 333333333, -1, 666666667, 3, 0}, + {3, 333333333, -1, 999999999, 3, 333333332}, + {3, 333333333, 0, 0, 3, 333333333}, + {3, 333333333, 0, 1, 3, 333333334}, + {3, 333333333, 0, 333333333, 3, 666666666}, + {3, 333333333, 0, 666666666, 3, 999999999}, + {3, 333333333, 1, 0, 4, 333333333}, + {3, 333333333, 2, 0, 5, 333333333}, + {3, 333333333, 3, 0, 6, 333333333}, + {3, 333333333, 3, 333333333, 6, 666666666}, + + {Long.MAX_VALUE, 0, Long.MIN_VALUE, 0, -1, 0}, + }; + } + + @Test(dataProvider="Plus", groups={"tck"}) + public void plus(long seconds, int nanos, long otherSeconds, int otherNanos, long expectedSeconds, int expectedNanoOfSecond) { + Duration t = Duration.ofSeconds(seconds, nanos).plus(Duration.ofSeconds(otherSeconds, otherNanos)); + assertEquals(t.getSeconds(), expectedSeconds); + assertEquals(t.getNano(), expectedNanoOfSecond); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void plusOverflowTooBig() { + Duration t = Duration.ofSeconds(Long.MAX_VALUE, 999999999); + t.plus(Duration.ofSeconds(0, 1)); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void plusOverflowTooSmall() { + Duration t = Duration.ofSeconds(Long.MIN_VALUE); + t.plus(Duration.ofSeconds(-1, 999999999)); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void plus_longTemporalUnit_seconds() { + Duration t = Duration.ofSeconds(1); + t = t.plus(1, SECONDS); + assertEquals(2, t.getSeconds()); + assertEquals(0, t.getNano()); + } + + @Test(groups={"tck"}) + public void plus_longTemporalUnit_millis() { + Duration t = Duration.ofSeconds(1); + t = t.plus(1, MILLIS); + assertEquals(1, t.getSeconds()); + assertEquals(1000000, t.getNano()); + } + + @Test(groups={"tck"}) + public void plus_longTemporalUnit_micros() { + Duration t = Duration.ofSeconds(1); + t = t.plus(1, MICROS); + assertEquals(1, t.getSeconds()); + assertEquals(1000, t.getNano()); + } + + @Test(groups={"tck"}) + public void plus_longTemporalUnit_nanos() { + Duration t = Duration.ofSeconds(1); + t = t.plus(1, NANOS); + assertEquals(1, t.getSeconds()); + assertEquals(1, t.getNano()); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void plus_longTemporalUnit_null() { + Duration t = Duration.ofSeconds(1); + t.plus(1, (TemporalUnit) null); + } + + //----------------------------------------------------------------------- + @DataProvider(name="PlusSeconds") + Object[][] provider_plusSeconds_long() { + return new Object[][] { + {0, 0, 0, 0, 0}, + {0, 0, 1, 1, 0}, + {0, 0, -1, -1, 0}, + {0, 0, Long.MAX_VALUE, Long.MAX_VALUE, 0}, + {0, 0, Long.MIN_VALUE, Long.MIN_VALUE, 0}, + {1, 0, 0, 1, 0}, + {1, 0, 1, 2, 0}, + {1, 0, -1, 0, 0}, + {1, 0, Long.MAX_VALUE - 1, Long.MAX_VALUE, 0}, + {1, 0, Long.MIN_VALUE, Long.MIN_VALUE + 1, 0}, + {1, 1, 0, 1, 1}, + {1, 1, 1, 2, 1}, + {1, 1, -1, 0, 1}, + {1, 1, Long.MAX_VALUE - 1, Long.MAX_VALUE, 1}, + {1, 1, Long.MIN_VALUE, Long.MIN_VALUE + 1, 1}, + {-1, 1, 0, -1, 1}, + {-1, 1, 1, 0, 1}, + {-1, 1, -1, -2, 1}, + {-1, 1, Long.MAX_VALUE, Long.MAX_VALUE - 1, 1}, + {-1, 1, Long.MIN_VALUE + 1, Long.MIN_VALUE, 1}, + }; + } + + @Test(dataProvider="PlusSeconds", groups={"tck"}) + public void plusSeconds_long(long seconds, int nanos, long amount, long expectedSeconds, int expectedNanoOfSecond) { + Duration t = Duration.ofSeconds(seconds, nanos); + t = t.plusSeconds(amount); + assertEquals(t.getSeconds(), expectedSeconds); + assertEquals(t.getNano(), expectedNanoOfSecond); + } + + @Test(expectedExceptions = {ArithmeticException.class}, groups={"tck"}) + public void plusSeconds_long_overflowTooBig() { + Duration t = Duration.ofSeconds(1, 0); + t.plusSeconds(Long.MAX_VALUE); + } + + @Test(expectedExceptions = {ArithmeticException.class}, groups={"tck"}) + public void plusSeconds_long_overflowTooSmall() { + Duration t = Duration.ofSeconds(-1, 0); + t.plusSeconds(Long.MIN_VALUE); + } + + //----------------------------------------------------------------------- + @DataProvider(name="PlusMillis") + Object[][] provider_plusMillis_long() { + return new Object[][] { + {0, 0, 0, 0, 0}, + {0, 0, 1, 0, 1000000}, + {0, 0, 999, 0, 999000000}, + {0, 0, 1000, 1, 0}, + {0, 0, 1001, 1, 1000000}, + {0, 0, 1999, 1, 999000000}, + {0, 0, 2000, 2, 0}, + {0, 0, -1, -1, 999000000}, + {0, 0, -999, -1, 1000000}, + {0, 0, -1000, -1, 0}, + {0, 0, -1001, -2, 999000000}, + {0, 0, -1999, -2, 1000000}, + + {0, 1, 0, 0, 1}, + {0, 1, 1, 0, 1000001}, + {0, 1, 998, 0, 998000001}, + {0, 1, 999, 0, 999000001}, + {0, 1, 1000, 1, 1}, + {0, 1, 1998, 1, 998000001}, + {0, 1, 1999, 1, 999000001}, + {0, 1, 2000, 2, 1}, + {0, 1, -1, -1, 999000001}, + {0, 1, -2, -1, 998000001}, + {0, 1, -1000, -1, 1}, + {0, 1, -1001, -2, 999000001}, + + {0, 1000000, 0, 0, 1000000}, + {0, 1000000, 1, 0, 2000000}, + {0, 1000000, 998, 0, 999000000}, + {0, 1000000, 999, 1, 0}, + {0, 1000000, 1000, 1, 1000000}, + {0, 1000000, 1998, 1, 999000000}, + {0, 1000000, 1999, 2, 0}, + {0, 1000000, 2000, 2, 1000000}, + {0, 1000000, -1, 0, 0}, + {0, 1000000, -2, -1, 999000000}, + {0, 1000000, -999, -1, 2000000}, + {0, 1000000, -1000, -1, 1000000}, + {0, 1000000, -1001, -1, 0}, + {0, 1000000, -1002, -2, 999000000}, + + {0, 999999999, 0, 0, 999999999}, + {0, 999999999, 1, 1, 999999}, + {0, 999999999, 999, 1, 998999999}, + {0, 999999999, 1000, 1, 999999999}, + {0, 999999999, 1001, 2, 999999}, + {0, 999999999, -1, 0, 998999999}, + {0, 999999999, -1000, -1, 999999999}, + {0, 999999999, -1001, -1, 998999999}, + }; + } + + @Test(dataProvider="PlusMillis", groups={"tck"}) + public void plusMillis_long(long seconds, int nanos, long amount, long expectedSeconds, int expectedNanoOfSecond) { + Duration t = Duration.ofSeconds(seconds, nanos); + t = t.plusMillis(amount); + assertEquals(t.getSeconds(), expectedSeconds); + assertEquals(t.getNano(), expectedNanoOfSecond); + } + @Test(dataProvider="PlusMillis", groups={"tck"}) + public void plusMillis_long_oneMore(long seconds, int nanos, long amount, long expectedSeconds, int expectedNanoOfSecond) { + Duration t = Duration.ofSeconds(seconds + 1, nanos); + t = t.plusMillis(amount); + assertEquals(t.getSeconds(), expectedSeconds + 1); + assertEquals(t.getNano(), expectedNanoOfSecond); + } + @Test(dataProvider="PlusMillis", groups={"tck"}) + public void plusMillis_long_minusOneLess(long seconds, int nanos, long amount, long expectedSeconds, int expectedNanoOfSecond) { + Duration t = Duration.ofSeconds(seconds - 1, nanos); + t = t.plusMillis(amount); + assertEquals(t.getSeconds(), expectedSeconds - 1); + assertEquals(t.getNano(), expectedNanoOfSecond); + } + + @Test(groups={"tck"}) + public void plusMillis_long_max() { + Duration t = Duration.ofSeconds(Long.MAX_VALUE, 998999999); + t = t.plusMillis(1); + assertEquals(t.getSeconds(), Long.MAX_VALUE); + assertEquals(t.getNano(), 999999999); + } + + @Test(expectedExceptions = {ArithmeticException.class}, groups={"tck"}) + public void plusMillis_long_overflowTooBig() { + Duration t = Duration.ofSeconds(Long.MAX_VALUE, 999000000); + t.plusMillis(1); + } + + @Test(groups={"tck"}) + public void plusMillis_long_min() { + Duration t = Duration.ofSeconds(Long.MIN_VALUE, 1000000); + t = t.plusMillis(-1); + assertEquals(t.getSeconds(), Long.MIN_VALUE); + assertEquals(t.getNano(), 0); + } + + @Test(expectedExceptions = {ArithmeticException.class}, groups={"tck"}) + public void plusMillis_long_overflowTooSmall() { + Duration t = Duration.ofSeconds(Long.MIN_VALUE, 0); + t.plusMillis(-1); + } + + //----------------------------------------------------------------------- + @DataProvider(name="PlusNanos") + Object[][] provider_plusNanos_long() { + return new Object[][] { + {0, 0, 0, 0, 0}, + {0, 0, 1, 0, 1}, + {0, 0, 999999999, 0, 999999999}, + {0, 0, 1000000000, 1, 0}, + {0, 0, 1000000001, 1, 1}, + {0, 0, 1999999999, 1, 999999999}, + {0, 0, 2000000000, 2, 0}, + {0, 0, -1, -1, 999999999}, + {0, 0, -999999999, -1, 1}, + {0, 0, -1000000000, -1, 0}, + {0, 0, -1000000001, -2, 999999999}, + {0, 0, -1999999999, -2, 1}, + + {1, 0, 0, 1, 0}, + {1, 0, 1, 1, 1}, + {1, 0, 999999999, 1, 999999999}, + {1, 0, 1000000000, 2, 0}, + {1, 0, 1000000001, 2, 1}, + {1, 0, 1999999999, 2, 999999999}, + {1, 0, 2000000000, 3, 0}, + {1, 0, -1, 0, 999999999}, + {1, 0, -999999999, 0, 1}, + {1, 0, -1000000000, 0, 0}, + {1, 0, -1000000001, -1, 999999999}, + {1, 0, -1999999999, -1, 1}, + + {-1, 0, 0, -1, 0}, + {-1, 0, 1, -1, 1}, + {-1, 0, 999999999, -1, 999999999}, + {-1, 0, 1000000000, 0, 0}, + {-1, 0, 1000000001, 0, 1}, + {-1, 0, 1999999999, 0, 999999999}, + {-1, 0, 2000000000, 1, 0}, + {-1, 0, -1, -2, 999999999}, + {-1, 0, -999999999, -2, 1}, + {-1, 0, -1000000000, -2, 0}, + {-1, 0, -1000000001, -3, 999999999}, + {-1, 0, -1999999999, -3, 1}, + + {1, 1, 0, 1, 1}, + {1, 1, 1, 1, 2}, + {1, 1, 999999998, 1, 999999999}, + {1, 1, 999999999, 2, 0}, + {1, 1, 1000000000, 2, 1}, + {1, 1, 1999999998, 2, 999999999}, + {1, 1, 1999999999, 3, 0}, + {1, 1, 2000000000, 3, 1}, + {1, 1, -1, 1, 0}, + {1, 1, -2, 0, 999999999}, + {1, 1, -1000000000, 0, 1}, + {1, 1, -1000000001, 0, 0}, + {1, 1, -1000000002, -1, 999999999}, + {1, 1, -2000000000, -1, 1}, + + {1, 999999999, 0, 1, 999999999}, + {1, 999999999, 1, 2, 0}, + {1, 999999999, 999999999, 2, 999999998}, + {1, 999999999, 1000000000, 2, 999999999}, + {1, 999999999, 1000000001, 3, 0}, + {1, 999999999, -1, 1, 999999998}, + {1, 999999999, -1000000000, 0, 999999999}, + {1, 999999999, -1000000001, 0, 999999998}, + {1, 999999999, -1999999999, 0, 0}, + {1, 999999999, -2000000000, -1, 999999999}, + + {Long.MAX_VALUE, 0, 999999999, Long.MAX_VALUE, 999999999}, + {Long.MAX_VALUE - 1, 0, 1999999999, Long.MAX_VALUE, 999999999}, + {Long.MIN_VALUE, 1, -1, Long.MIN_VALUE, 0}, + {Long.MIN_VALUE + 1, 1, -1000000001, Long.MIN_VALUE, 0}, + }; + } + + @Test(dataProvider="PlusNanos", groups={"tck"}) + public void plusNanos_long(long seconds, int nanos, long amount, long expectedSeconds, int expectedNanoOfSecond) { + Duration t = Duration.ofSeconds(seconds, nanos); + t = t.plusNanos(amount); + assertEquals(t.getSeconds(), expectedSeconds); + assertEquals(t.getNano(), expectedNanoOfSecond); + } + + @Test(expectedExceptions = {ArithmeticException.class}, groups={"tck"}) + public void plusNanos_long_overflowTooBig() { + Duration t = Duration.ofSeconds(Long.MAX_VALUE, 999999999); + t.plusNanos(1); + } + + @Test(expectedExceptions = {ArithmeticException.class}, groups={"tck"}) + public void plusNanos_long_overflowTooSmall() { + Duration t = Duration.ofSeconds(Long.MIN_VALUE, 0); + t.plusNanos(-1); + } + + //----------------------------------------------------------------------- + @DataProvider(name="Minus") + Object[][] provider_minus() { + return new Object[][] { + {Long.MIN_VALUE, 0, Long.MIN_VALUE + 1, 0, -1, 0}, + + {-4, 666666667, -4, 666666667, 0, 0}, + {-4, 666666667, -3, 0, -1, 666666667}, + {-4, 666666667, -2, 0, -2, 666666667}, + {-4, 666666667, -1, 0, -3, 666666667}, + {-4, 666666667, -1, 333333334, -3, 333333333}, + {-4, 666666667, -1, 666666667, -3, 0}, + {-4, 666666667, -1, 999999999, -4, 666666668}, + {-4, 666666667, 0, 0, -4, 666666667}, + {-4, 666666667, 0, 1, -4, 666666666}, + {-4, 666666667, 0, 333333333, -4, 333333334}, + {-4, 666666667, 0, 666666666, -4, 1}, + {-4, 666666667, 1, 0, -5, 666666667}, + {-4, 666666667, 2, 0, -6, 666666667}, + {-4, 666666667, 3, 0, -7, 666666667}, + {-4, 666666667, 3, 333333333, -7, 333333334}, + + {-3, 0, -4, 666666667, 0, 333333333}, + {-3, 0, -3, 0, 0, 0}, + {-3, 0, -2, 0, -1, 0}, + {-3, 0, -1, 0, -2, 0}, + {-3, 0, -1, 333333334, -3, 666666666}, + {-3, 0, -1, 666666667, -3, 333333333}, + {-3, 0, -1, 999999999, -3, 1}, + {-3, 0, 0, 0, -3, 0}, + {-3, 0, 0, 1, -4, 999999999}, + {-3, 0, 0, 333333333, -4, 666666667}, + {-3, 0, 0, 666666666, -4, 333333334}, + {-3, 0, 1, 0, -4, 0}, + {-3, 0, 2, 0, -5, 0}, + {-3, 0, 3, 0, -6, 0}, + {-3, 0, 3, 333333333, -7, 666666667}, + + {-2, 0, -4, 666666667, 1, 333333333}, + {-2, 0, -3, 0, 1, 0}, + {-2, 0, -2, 0, 0, 0}, + {-2, 0, -1, 0, -1, 0}, + {-2, 0, -1, 333333334, -2, 666666666}, + {-2, 0, -1, 666666667, -2, 333333333}, + {-2, 0, -1, 999999999, -2, 1}, + {-2, 0, 0, 0, -2, 0}, + {-2, 0, 0, 1, -3, 999999999}, + {-2, 0, 0, 333333333, -3, 666666667}, + {-2, 0, 0, 666666666, -3, 333333334}, + {-2, 0, 1, 0, -3, 0}, + {-2, 0, 2, 0, -4, 0}, + {-2, 0, 3, 0, -5, 0}, + {-2, 0, 3, 333333333, -6, 666666667}, + + {-1, 0, -4, 666666667, 2, 333333333}, + {-1, 0, -3, 0, 2, 0}, + {-1, 0, -2, 0, 1, 0}, + {-1, 0, -1, 0, 0, 0}, + {-1, 0, -1, 333333334, -1, 666666666}, + {-1, 0, -1, 666666667, -1, 333333333}, + {-1, 0, -1, 999999999, -1, 1}, + {-1, 0, 0, 0, -1, 0}, + {-1, 0, 0, 1, -2, 999999999}, + {-1, 0, 0, 333333333, -2, 666666667}, + {-1, 0, 0, 666666666, -2, 333333334}, + {-1, 0, 1, 0, -2, 0}, + {-1, 0, 2, 0, -3, 0}, + {-1, 0, 3, 0, -4, 0}, + {-1, 0, 3, 333333333, -5, 666666667}, + + {-1, 666666667, -4, 666666667, 3, 0}, + {-1, 666666667, -3, 0, 2, 666666667}, + {-1, 666666667, -2, 0, 1, 666666667}, + {-1, 666666667, -1, 0, 0, 666666667}, + {-1, 666666667, -1, 333333334, 0, 333333333}, + {-1, 666666667, -1, 666666667, 0, 0}, + {-1, 666666667, -1, 999999999, -1, 666666668}, + {-1, 666666667, 0, 0, -1, 666666667}, + {-1, 666666667, 0, 1, -1, 666666666}, + {-1, 666666667, 0, 333333333, -1, 333333334}, + {-1, 666666667, 0, 666666666, -1, 1}, + {-1, 666666667, 1, 0, -2, 666666667}, + {-1, 666666667, 2, 0, -3, 666666667}, + {-1, 666666667, 3, 0, -4, 666666667}, + {-1, 666666667, 3, 333333333, -4, 333333334}, + + {0, 0, -4, 666666667, 3, 333333333}, + {0, 0, -3, 0, 3, 0}, + {0, 0, -2, 0, 2, 0}, + {0, 0, -1, 0, 1, 0}, + {0, 0, -1, 333333334, 0, 666666666}, + {0, 0, -1, 666666667, 0, 333333333}, + {0, 0, -1, 999999999, 0, 1}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 1, -1, 999999999}, + {0, 0, 0, 333333333, -1, 666666667}, + {0, 0, 0, 666666666, -1, 333333334}, + {0, 0, 1, 0, -1, 0}, + {0, 0, 2, 0, -2, 0}, + {0, 0, 3, 0, -3, 0}, + {0, 0, 3, 333333333, -4, 666666667}, + + {0, 333333333, -4, 666666667, 3, 666666666}, + {0, 333333333, -3, 0, 3, 333333333}, + {0, 333333333, -2, 0, 2, 333333333}, + {0, 333333333, -1, 0, 1, 333333333}, + {0, 333333333, -1, 333333334, 0, 999999999}, + {0, 333333333, -1, 666666667, 0, 666666666}, + {0, 333333333, -1, 999999999, 0, 333333334}, + {0, 333333333, 0, 0, 0, 333333333}, + {0, 333333333, 0, 1, 0, 333333332}, + {0, 333333333, 0, 333333333, 0, 0}, + {0, 333333333, 0, 666666666, -1, 666666667}, + {0, 333333333, 1, 0, -1, 333333333}, + {0, 333333333, 2, 0, -2, 333333333}, + {0, 333333333, 3, 0, -3, 333333333}, + {0, 333333333, 3, 333333333, -3, 0}, + + {1, 0, -4, 666666667, 4, 333333333}, + {1, 0, -3, 0, 4, 0}, + {1, 0, -2, 0, 3, 0}, + {1, 0, -1, 0, 2, 0}, + {1, 0, -1, 333333334, 1, 666666666}, + {1, 0, -1, 666666667, 1, 333333333}, + {1, 0, -1, 999999999, 1, 1}, + {1, 0, 0, 0, 1, 0}, + {1, 0, 0, 1, 0, 999999999}, + {1, 0, 0, 333333333, 0, 666666667}, + {1, 0, 0, 666666666, 0, 333333334}, + {1, 0, 1, 0, 0, 0}, + {1, 0, 2, 0, -1, 0}, + {1, 0, 3, 0, -2, 0}, + {1, 0, 3, 333333333, -3, 666666667}, + + {2, 0, -4, 666666667, 5, 333333333}, + {2, 0, -3, 0, 5, 0}, + {2, 0, -2, 0, 4, 0}, + {2, 0, -1, 0, 3, 0}, + {2, 0, -1, 333333334, 2, 666666666}, + {2, 0, -1, 666666667, 2, 333333333}, + {2, 0, -1, 999999999, 2, 1}, + {2, 0, 0, 0, 2, 0}, + {2, 0, 0, 1, 1, 999999999}, + {2, 0, 0, 333333333, 1, 666666667}, + {2, 0, 0, 666666666, 1, 333333334}, + {2, 0, 1, 0, 1, 0}, + {2, 0, 2, 0, 0, 0}, + {2, 0, 3, 0, -1, 0}, + {2, 0, 3, 333333333, -2, 666666667}, + + {3, 0, -4, 666666667, 6, 333333333}, + {3, 0, -3, 0, 6, 0}, + {3, 0, -2, 0, 5, 0}, + {3, 0, -1, 0, 4, 0}, + {3, 0, -1, 333333334, 3, 666666666}, + {3, 0, -1, 666666667, 3, 333333333}, + {3, 0, -1, 999999999, 3, 1}, + {3, 0, 0, 0, 3, 0}, + {3, 0, 0, 1, 2, 999999999}, + {3, 0, 0, 333333333, 2, 666666667}, + {3, 0, 0, 666666666, 2, 333333334}, + {3, 0, 1, 0, 2, 0}, + {3, 0, 2, 0, 1, 0}, + {3, 0, 3, 0, 0, 0}, + {3, 0, 3, 333333333, -1, 666666667}, + + {3, 333333333, -4, 666666667, 6, 666666666}, + {3, 333333333, -3, 0, 6, 333333333}, + {3, 333333333, -2, 0, 5, 333333333}, + {3, 333333333, -1, 0, 4, 333333333}, + {3, 333333333, -1, 333333334, 3, 999999999}, + {3, 333333333, -1, 666666667, 3, 666666666}, + {3, 333333333, -1, 999999999, 3, 333333334}, + {3, 333333333, 0, 0, 3, 333333333}, + {3, 333333333, 0, 1, 3, 333333332}, + {3, 333333333, 0, 333333333, 3, 0}, + {3, 333333333, 0, 666666666, 2, 666666667}, + {3, 333333333, 1, 0, 2, 333333333}, + {3, 333333333, 2, 0, 1, 333333333}, + {3, 333333333, 3, 0, 0, 333333333}, + {3, 333333333, 3, 333333333, 0, 0}, + + {Long.MAX_VALUE, 0, Long.MAX_VALUE, 0, 0, 0}, + }; + } + + @Test(dataProvider="Minus", groups={"tck"}) + public void minus(long seconds, int nanos, long otherSeconds, int otherNanos, long expectedSeconds, int expectedNanoOfSecond) { + Duration t = Duration.ofSeconds(seconds, nanos).minus(Duration.ofSeconds(otherSeconds, otherNanos)); + assertEquals(t.getSeconds(), expectedSeconds); + assertEquals(t.getNano(), expectedNanoOfSecond); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void minusOverflowTooSmall() { + Duration t = Duration.ofSeconds(Long.MIN_VALUE); + t.minus(Duration.ofSeconds(0, 1)); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void minusOverflowTooBig() { + Duration t = Duration.ofSeconds(Long.MAX_VALUE, 999999999); + t.minus(Duration.ofSeconds(-1, 999999999)); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void minus_longTemporalUnit_seconds() { + Duration t = Duration.ofSeconds(1); + t = t.minus(1, SECONDS); + assertEquals(0, t.getSeconds()); + assertEquals(0, t.getNano()); + } + + @Test(groups={"tck"}) + public void minus_longTemporalUnit_millis() { + Duration t = Duration.ofSeconds(1); + t = t.minus(1, MILLIS); + assertEquals(0, t.getSeconds()); + assertEquals(999000000, t.getNano()); + } + + @Test(groups={"tck"}) + public void minus_longTemporalUnit_micros() { + Duration t = Duration.ofSeconds(1); + t = t.minus(1, MICROS); + assertEquals(0, t.getSeconds()); + assertEquals(999999000, t.getNano()); + } + + @Test(groups={"tck"}) + public void minus_longTemporalUnit_nanos() { + Duration t = Duration.ofSeconds(1); + t = t.minus(1, NANOS); + assertEquals(0, t.getSeconds()); + assertEquals(999999999, t.getNano()); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void minus_longTemporalUnit_null() { + Duration t = Duration.ofSeconds(1); + t.minus(1, (TemporalUnit) null); + } + + //----------------------------------------------------------------------- + @DataProvider(name="MinusSeconds") + Object[][] provider_minusSeconds_long() { + return new Object[][] { + {0, 0, 0, 0, 0}, + {0, 0, 1, -1, 0}, + {0, 0, -1, 1, 0}, + {0, 0, Long.MAX_VALUE, -Long.MAX_VALUE, 0}, + {0, 0, Long.MIN_VALUE + 1, Long.MAX_VALUE, 0}, + {1, 0, 0, 1, 0}, + {1, 0, 1, 0, 0}, + {1, 0, -1, 2, 0}, + {1, 0, Long.MAX_VALUE - 1, -Long.MAX_VALUE + 2, 0}, + {1, 0, Long.MIN_VALUE + 2, Long.MAX_VALUE, 0}, + {1, 1, 0, 1, 1}, + {1, 1, 1, 0, 1}, + {1, 1, -1, 2, 1}, + {1, 1, Long.MAX_VALUE, -Long.MAX_VALUE + 1, 1}, + {1, 1, Long.MIN_VALUE + 2, Long.MAX_VALUE, 1}, + {-1, 1, 0, -1, 1}, + {-1, 1, 1, -2, 1}, + {-1, 1, -1, 0, 1}, + {-1, 1, Long.MAX_VALUE, Long.MIN_VALUE, 1}, + {-1, 1, Long.MIN_VALUE + 1, Long.MAX_VALUE - 1, 1}, + }; + } + + @Test(dataProvider="MinusSeconds", groups={"tck"}) + public void minusSeconds_long(long seconds, int nanos, long amount, long expectedSeconds, int expectedNanoOfSecond) { + Duration t = Duration.ofSeconds(seconds, nanos); + t = t.minusSeconds(amount); + assertEquals(t.getSeconds(), expectedSeconds); + assertEquals(t.getNano(), expectedNanoOfSecond); + } + + @Test(expectedExceptions = {ArithmeticException.class}, groups={"tck"}) + public void minusSeconds_long_overflowTooBig() { + Duration t = Duration.ofSeconds(1, 0); + t.minusSeconds(Long.MIN_VALUE + 1); + } + + @Test(expectedExceptions = {ArithmeticException.class}, groups={"tck"}) + public void minusSeconds_long_overflowTooSmall() { + Duration t = Duration.ofSeconds(-2, 0); + t.minusSeconds(Long.MAX_VALUE); + } + + //----------------------------------------------------------------------- + @DataProvider(name="MinusMillis") + Object[][] provider_minusMillis_long() { + return new Object[][] { + {0, 0, 0, 0, 0}, + {0, 0, 1, -1, 999000000}, + {0, 0, 999, -1, 1000000}, + {0, 0, 1000, -1, 0}, + {0, 0, 1001, -2, 999000000}, + {0, 0, 1999, -2, 1000000}, + {0, 0, 2000, -2, 0}, + {0, 0, -1, 0, 1000000}, + {0, 0, -999, 0, 999000000}, + {0, 0, -1000, 1, 0}, + {0, 0, -1001, 1, 1000000}, + {0, 0, -1999, 1, 999000000}, + + {0, 1, 0, 0, 1}, + {0, 1, 1, -1, 999000001}, + {0, 1, 998, -1, 2000001}, + {0, 1, 999, -1, 1000001}, + {0, 1, 1000, -1, 1}, + {0, 1, 1998, -2, 2000001}, + {0, 1, 1999, -2, 1000001}, + {0, 1, 2000, -2, 1}, + {0, 1, -1, 0, 1000001}, + {0, 1, -2, 0, 2000001}, + {0, 1, -1000, 1, 1}, + {0, 1, -1001, 1, 1000001}, + + {0, 1000000, 0, 0, 1000000}, + {0, 1000000, 1, 0, 0}, + {0, 1000000, 998, -1, 3000000}, + {0, 1000000, 999, -1, 2000000}, + {0, 1000000, 1000, -1, 1000000}, + {0, 1000000, 1998, -2, 3000000}, + {0, 1000000, 1999, -2, 2000000}, + {0, 1000000, 2000, -2, 1000000}, + {0, 1000000, -1, 0, 2000000}, + {0, 1000000, -2, 0, 3000000}, + {0, 1000000, -999, 1, 0}, + {0, 1000000, -1000, 1, 1000000}, + {0, 1000000, -1001, 1, 2000000}, + {0, 1000000, -1002, 1, 3000000}, + + {0, 999999999, 0, 0, 999999999}, + {0, 999999999, 1, 0, 998999999}, + {0, 999999999, 999, 0, 999999}, + {0, 999999999, 1000, -1, 999999999}, + {0, 999999999, 1001, -1, 998999999}, + {0, 999999999, -1, 1, 999999}, + {0, 999999999, -1000, 1, 999999999}, + {0, 999999999, -1001, 2, 999999}, + }; + } + + @Test(dataProvider="MinusMillis", groups={"tck"}) + public void minusMillis_long(long seconds, int nanos, long amount, long expectedSeconds, int expectedNanoOfSecond) { + Duration t = Duration.ofSeconds(seconds, nanos); + t = t.minusMillis(amount); + assertEquals(t.getSeconds(), expectedSeconds); + assertEquals(t.getNano(), expectedNanoOfSecond); + } + @Test(dataProvider="MinusMillis", groups={"tck"}) + public void minusMillis_long_oneMore(long seconds, int nanos, long amount, long expectedSeconds, int expectedNanoOfSecond) { + Duration t = Duration.ofSeconds(seconds + 1, nanos); + t = t.minusMillis(amount); + assertEquals(t.getSeconds(), expectedSeconds + 1); + assertEquals(t.getNano(), expectedNanoOfSecond); + } + @Test(dataProvider="MinusMillis", groups={"tck"}) + public void minusMillis_long_minusOneLess(long seconds, int nanos, long amount, long expectedSeconds, int expectedNanoOfSecond) { + Duration t = Duration.ofSeconds(seconds - 1, nanos); + t = t.minusMillis(amount); + assertEquals(t.getSeconds(), expectedSeconds - 1); + assertEquals(t.getNano(), expectedNanoOfSecond); + } + + @Test(groups={"tck"}) + public void minusMillis_long_max() { + Duration t = Duration.ofSeconds(Long.MAX_VALUE, 998999999); + t = t.minusMillis(-1); + assertEquals(t.getSeconds(), Long.MAX_VALUE); + assertEquals(t.getNano(), 999999999); + } + + @Test(expectedExceptions = {ArithmeticException.class}, groups={"tck"}) + public void minusMillis_long_overflowTooBig() { + Duration t = Duration.ofSeconds(Long.MAX_VALUE, 999000000); + t.minusMillis(-1); + } + + @Test(groups={"tck"}) + public void minusMillis_long_min() { + Duration t = Duration.ofSeconds(Long.MIN_VALUE, 1000000); + t = t.minusMillis(1); + assertEquals(t.getSeconds(), Long.MIN_VALUE); + assertEquals(t.getNano(), 0); + } + + @Test(expectedExceptions = {ArithmeticException.class}, groups={"tck"}) + public void minusMillis_long_overflowTooSmall() { + Duration t = Duration.ofSeconds(Long.MIN_VALUE, 0); + t.minusMillis(1); + } + + //----------------------------------------------------------------------- + @DataProvider(name="MinusNanos") + Object[][] provider_minusNanos_long() { + return new Object[][] { + {0, 0, 0, 0, 0}, + {0, 0, 1, -1, 999999999}, + {0, 0, 999999999, -1, 1}, + {0, 0, 1000000000, -1, 0}, + {0, 0, 1000000001, -2, 999999999}, + {0, 0, 1999999999, -2, 1}, + {0, 0, 2000000000, -2, 0}, + {0, 0, -1, 0, 1}, + {0, 0, -999999999, 0, 999999999}, + {0, 0, -1000000000, 1, 0}, + {0, 0, -1000000001, 1, 1}, + {0, 0, -1999999999, 1, 999999999}, + + {1, 0, 0, 1, 0}, + {1, 0, 1, 0, 999999999}, + {1, 0, 999999999, 0, 1}, + {1, 0, 1000000000, 0, 0}, + {1, 0, 1000000001, -1, 999999999}, + {1, 0, 1999999999, -1, 1}, + {1, 0, 2000000000, -1, 0}, + {1, 0, -1, 1, 1}, + {1, 0, -999999999, 1, 999999999}, + {1, 0, -1000000000, 2, 0}, + {1, 0, -1000000001, 2, 1}, + {1, 0, -1999999999, 2, 999999999}, + + {-1, 0, 0, -1, 0}, + {-1, 0, 1, -2, 999999999}, + {-1, 0, 999999999, -2, 1}, + {-1, 0, 1000000000, -2, 0}, + {-1, 0, 1000000001, -3, 999999999}, + {-1, 0, 1999999999, -3, 1}, + {-1, 0, 2000000000, -3, 0}, + {-1, 0, -1, -1, 1}, + {-1, 0, -999999999, -1, 999999999}, + {-1, 0, -1000000000, 0, 0}, + {-1, 0, -1000000001, 0, 1}, + {-1, 0, -1999999999, 0, 999999999}, + + {1, 1, 0, 1, 1}, + {1, 1, 1, 1, 0}, + {1, 1, 999999998, 0, 3}, + {1, 1, 999999999, 0, 2}, + {1, 1, 1000000000, 0, 1}, + {1, 1, 1999999998, -1, 3}, + {1, 1, 1999999999, -1, 2}, + {1, 1, 2000000000, -1, 1}, + {1, 1, -1, 1, 2}, + {1, 1, -2, 1, 3}, + {1, 1, -1000000000, 2, 1}, + {1, 1, -1000000001, 2, 2}, + {1, 1, -1000000002, 2, 3}, + {1, 1, -2000000000, 3, 1}, + + {1, 999999999, 0, 1, 999999999}, + {1, 999999999, 1, 1, 999999998}, + {1, 999999999, 999999999, 1, 0}, + {1, 999999999, 1000000000, 0, 999999999}, + {1, 999999999, 1000000001, 0, 999999998}, + {1, 999999999, -1, 2, 0}, + {1, 999999999, -1000000000, 2, 999999999}, + {1, 999999999, -1000000001, 3, 0}, + {1, 999999999, -1999999999, 3, 999999998}, + {1, 999999999, -2000000000, 3, 999999999}, + + {Long.MAX_VALUE, 0, -999999999, Long.MAX_VALUE, 999999999}, + {Long.MAX_VALUE - 1, 0, -1999999999, Long.MAX_VALUE, 999999999}, + {Long.MIN_VALUE, 1, 1, Long.MIN_VALUE, 0}, + {Long.MIN_VALUE + 1, 1, 1000000001, Long.MIN_VALUE, 0}, + }; + } + + @Test(dataProvider="MinusNanos", groups={"tck"}) + public void minusNanos_long(long seconds, int nanos, long amount, long expectedSeconds, int expectedNanoOfSecond) { + Duration t = Duration.ofSeconds(seconds, nanos); + t = t.minusNanos(amount); + assertEquals(t.getSeconds(), expectedSeconds); + assertEquals(t.getNano(), expectedNanoOfSecond); + } + + @Test(expectedExceptions = {ArithmeticException.class}, groups={"tck"}) + public void minusNanos_long_overflowTooBig() { + Duration t = Duration.ofSeconds(Long.MAX_VALUE, 999999999); + t.minusNanos(-1); + } + + @Test(expectedExceptions = {ArithmeticException.class}, groups={"tck"}) + public void minusNanos_long_overflowTooSmall() { + Duration t = Duration.ofSeconds(Long.MIN_VALUE, 0); + t.minusNanos(1); + } + + //----------------------------------------------------------------------- + // multipliedBy() + //----------------------------------------------------------------------- + @DataProvider(name="MultipliedBy") + Object[][] provider_multipliedBy() { + return new Object[][] { + {-4, 666666667, -3, 9, 999999999}, + {-4, 666666667, -2, 6, 666666666}, + {-4, 666666667, -1, 3, 333333333}, + {-4, 666666667, 0, 0, 0}, + {-4, 666666667, 1, -4, 666666667}, + {-4, 666666667, 2, -7, 333333334}, + {-4, 666666667, 3, -10, 000000001}, + + {-3, 0, -3, 9, 0}, + {-3, 0, -2, 6, 0}, + {-3, 0, -1, 3, 0}, + {-3, 0, 0, 0, 0}, + {-3, 0, 1, -3, 0}, + {-3, 0, 2, -6, 0}, + {-3, 0, 3, -9, 0}, + + {-2, 0, -3, 6, 0}, + {-2, 0, -2, 4, 0}, + {-2, 0, -1, 2, 0}, + {-2, 0, 0, 0, 0}, + {-2, 0, 1, -2, 0}, + {-2, 0, 2, -4, 0}, + {-2, 0, 3, -6, 0}, + + {-1, 0, -3, 3, 0}, + {-1, 0, -2, 2, 0}, + {-1, 0, -1, 1, 0}, + {-1, 0, 0, 0, 0}, + {-1, 0, 1, -1, 0}, + {-1, 0, 2, -2, 0}, + {-1, 0, 3, -3, 0}, + + {-1, 500000000, -3, 1, 500000000}, + {-1, 500000000, -2, 1, 0}, + {-1, 500000000, -1, 0, 500000000}, + {-1, 500000000, 0, 0, 0}, + {-1, 500000000, 1, -1, 500000000}, + {-1, 500000000, 2, -1, 0}, + {-1, 500000000, 3, -2, 500000000}, + + {0, 0, -3, 0, 0}, + {0, 0, -2, 0, 0}, + {0, 0, -1, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 1, 0, 0}, + {0, 0, 2, 0, 0}, + {0, 0, 3, 0, 0}, + + {0, 500000000, -3, -2, 500000000}, + {0, 500000000, -2, -1, 0}, + {0, 500000000, -1, -1, 500000000}, + {0, 500000000, 0, 0, 0}, + {0, 500000000, 1, 0, 500000000}, + {0, 500000000, 2, 1, 0}, + {0, 500000000, 3, 1, 500000000}, + + {1, 0, -3, -3, 0}, + {1, 0, -2, -2, 0}, + {1, 0, -1, -1, 0}, + {1, 0, 0, 0, 0}, + {1, 0, 1, 1, 0}, + {1, 0, 2, 2, 0}, + {1, 0, 3, 3, 0}, + + {2, 0, -3, -6, 0}, + {2, 0, -2, -4, 0}, + {2, 0, -1, -2, 0}, + {2, 0, 0, 0, 0}, + {2, 0, 1, 2, 0}, + {2, 0, 2, 4, 0}, + {2, 0, 3, 6, 0}, + + {3, 0, -3, -9, 0}, + {3, 0, -2, -6, 0}, + {3, 0, -1, -3, 0}, + {3, 0, 0, 0, 0}, + {3, 0, 1, 3, 0}, + {3, 0, 2, 6, 0}, + {3, 0, 3, 9, 0}, + + {3, 333333333, -3, -10, 000000001}, + {3, 333333333, -2, -7, 333333334}, + {3, 333333333, -1, -4, 666666667}, + {3, 333333333, 0, 0, 0}, + {3, 333333333, 1, 3, 333333333}, + {3, 333333333, 2, 6, 666666666}, + {3, 333333333, 3, 9, 999999999}, + }; + } + + @Test(dataProvider="MultipliedBy", groups={"tck"}) + public void multipliedBy(long seconds, int nanos, int multiplicand, long expectedSeconds, int expectedNanos) { + Duration t = Duration.ofSeconds(seconds, nanos); + t = t.multipliedBy(multiplicand); + assertEquals(t.getSeconds(), expectedSeconds); + assertEquals(t.getNano(), expectedNanos); + } + + @Test(groups={"tck"}) + public void multipliedBy_max() { + Duration test = Duration.ofSeconds(1); + assertEquals(test.multipliedBy(Long.MAX_VALUE), Duration.ofSeconds(Long.MAX_VALUE)); + } + + @Test(groups={"tck"}) + public void multipliedBy_min() { + Duration test = Duration.ofSeconds(1); + assertEquals(test.multipliedBy(Long.MIN_VALUE), Duration.ofSeconds(Long.MIN_VALUE)); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void multipliedBy_tooBig() { + Duration test = Duration.ofSeconds(1, 1); + test.multipliedBy(Long.MAX_VALUE); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void multipliedBy_tooBig_negative() { + Duration test = Duration.ofSeconds(1, 1); + test.multipliedBy(Long.MIN_VALUE); + } + + //----------------------------------------------------------------------- + // dividedBy() + //----------------------------------------------------------------------- + @DataProvider(name="DividedBy") + Object[][] provider_dividedBy() { + return new Object[][] { + {-4, 666666667, -3, 1, 111111111}, + {-4, 666666667, -2, 1, 666666666}, + {-4, 666666667, -1, 3, 333333333}, + {-4, 666666667, 1, -4, 666666667}, + {-4, 666666667, 2, -2, 333333334}, + {-4, 666666667, 3, -2, 888888889}, + + {-3, 0, -3, 1, 0}, + {-3, 0, -2, 1, 500000000}, + {-3, 0, -1, 3, 0}, + {-3, 0, 1, -3, 0}, + {-3, 0, 2, -2, 500000000}, + {-3, 0, 3, -1, 0}, + + {-2, 0, -3, 0, 666666666}, + {-2, 0, -2, 1, 0}, + {-2, 0, -1, 2, 0}, + {-2, 0, 1, -2, 0}, + {-2, 0, 2, -1, 0}, + {-2, 0, 3, -1, 333333334}, + + {-1, 0, -3, 0, 333333333}, + {-1, 0, -2, 0, 500000000}, + {-1, 0, -1, 1, 0}, + {-1, 0, 1, -1, 0}, + {-1, 0, 2, -1, 500000000}, + {-1, 0, 3, -1, 666666667}, + + {-1, 500000000, -3, 0, 166666666}, + {-1, 500000000, -2, 0, 250000000}, + {-1, 500000000, -1, 0, 500000000}, + {-1, 500000000, 1, -1, 500000000}, + {-1, 500000000, 2, -1, 750000000}, + {-1, 500000000, 3, -1, 833333334}, + + {0, 0, -3, 0, 0}, + {0, 0, -2, 0, 0}, + {0, 0, -1, 0, 0}, + {0, 0, 1, 0, 0}, + {0, 0, 2, 0, 0}, + {0, 0, 3, 0, 0}, + + {0, 500000000, -3, -1, 833333334}, + {0, 500000000, -2, -1, 750000000}, + {0, 500000000, -1, -1, 500000000}, + {0, 500000000, 1, 0, 500000000}, + {0, 500000000, 2, 0, 250000000}, + {0, 500000000, 3, 0, 166666666}, + + {1, 0, -3, -1, 666666667}, + {1, 0, -2, -1, 500000000}, + {1, 0, -1, -1, 0}, + {1, 0, 1, 1, 0}, + {1, 0, 2, 0, 500000000}, + {1, 0, 3, 0, 333333333}, + + {2, 0, -3, -1, 333333334}, + {2, 0, -2, -1, 0}, + {2, 0, -1, -2, 0}, + {2, 0, 1, 2, 0}, + {2, 0, 2, 1, 0}, + {2, 0, 3, 0, 666666666}, + + {3, 0, -3, -1, 0}, + {3, 0, -2, -2, 500000000}, + {3, 0, -1, -3, 0}, + {3, 0, 1, 3, 0}, + {3, 0, 2, 1, 500000000}, + {3, 0, 3, 1, 0}, + + {3, 333333333, -3, -2, 888888889}, + {3, 333333333, -2, -2, 333333334}, + {3, 333333333, -1, -4, 666666667}, + {3, 333333333, 1, 3, 333333333}, + {3, 333333333, 2, 1, 666666666}, + {3, 333333333, 3, 1, 111111111}, + }; + } + + @Test(dataProvider="DividedBy", groups={"tck"}) + public void dividedBy(long seconds, int nanos, int divisor, long expectedSeconds, int expectedNanos) { + Duration t = Duration.ofSeconds(seconds, nanos); + t = t.dividedBy(divisor); + assertEquals(t.getSeconds(), expectedSeconds); + assertEquals(t.getNano(), expectedNanos); + } + + @Test(dataProvider="DividedBy", expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void dividedByZero(long seconds, int nanos, int divisor, long expectedSeconds, int expectedNanos) { + Duration t = Duration.ofSeconds(seconds, nanos); + t.dividedBy(0); + fail(t + " divided by zero did not throw ArithmeticException"); + } + + @Test(groups={"tck"}) + public void dividedBy_max() { + Duration test = Duration.ofSeconds(Long.MAX_VALUE); + assertEquals(test.dividedBy(Long.MAX_VALUE), Duration.ofSeconds(1)); + } + + //----------------------------------------------------------------------- + // negated() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_negated() { + assertEquals(Duration.ofSeconds(0).negated(), Duration.ofSeconds(0)); + assertEquals(Duration.ofSeconds(12).negated(), Duration.ofSeconds(-12)); + assertEquals(Duration.ofSeconds(-12).negated(), Duration.ofSeconds(12)); + assertEquals(Duration.ofSeconds(12, 20).negated(), Duration.ofSeconds(-12, -20)); + assertEquals(Duration.ofSeconds(12, -20).negated(), Duration.ofSeconds(-12, 20)); + assertEquals(Duration.ofSeconds(-12, -20).negated(), Duration.ofSeconds(12, 20)); + assertEquals(Duration.ofSeconds(-12, 20).negated(), Duration.ofSeconds(12, -20)); + assertEquals(Duration.ofSeconds(Long.MAX_VALUE).negated(), Duration.ofSeconds(-Long.MAX_VALUE)); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void test_negated_overflow() { + Duration.ofSeconds(Long.MIN_VALUE).negated(); + } + + //----------------------------------------------------------------------- + // abs() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_abs() { + assertEquals(Duration.ofSeconds(0).abs(), Duration.ofSeconds(0)); + assertEquals(Duration.ofSeconds(12).abs(), Duration.ofSeconds(12)); + assertEquals(Duration.ofSeconds(-12).abs(), Duration.ofSeconds(12)); + assertEquals(Duration.ofSeconds(12, 20).abs(), Duration.ofSeconds(12, 20)); + assertEquals(Duration.ofSeconds(12, -20).abs(), Duration.ofSeconds(12, -20)); + assertEquals(Duration.ofSeconds(-12, -20).abs(), Duration.ofSeconds(12, 20)); + assertEquals(Duration.ofSeconds(-12, 20).abs(), Duration.ofSeconds(12, -20)); + assertEquals(Duration.ofSeconds(Long.MAX_VALUE).abs(), Duration.ofSeconds(Long.MAX_VALUE)); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void test_abs_overflow() { + Duration.ofSeconds(Long.MIN_VALUE).abs(); + } + + //----------------------------------------------------------------------- + // toNanos() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toNanos() { + Duration test = Duration.ofSeconds(321, 123456789); + assertEquals(test.toNanos(), 321123456789L); + } + + @Test(groups={"tck"}) + public void test_toNanos_max() { + Duration test = Duration.ofSeconds(0, Long.MAX_VALUE); + assertEquals(test.toNanos(), Long.MAX_VALUE); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void test_toNanos_tooBig() { + Duration test = Duration.ofSeconds(0, Long.MAX_VALUE).plusNanos(1); + test.toNanos(); + } + + //----------------------------------------------------------------------- + // toMillis() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toMillis() { + Duration test = Duration.ofSeconds(321, 123456789); + assertEquals(test.toMillis(), 321000 + 123); + } + + @Test(groups={"tck"}) + public void test_toMillis_max() { + Duration test = Duration.ofSeconds(Long.MAX_VALUE / 1000, (Long.MAX_VALUE % 1000) * 1000000); + assertEquals(test.toMillis(), Long.MAX_VALUE); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void test_toMillis_tooBig() { + Duration test = Duration.ofSeconds(Long.MAX_VALUE / 1000, ((Long.MAX_VALUE % 1000) + 1) * 1000000); + test.toMillis(); + } + + //----------------------------------------------------------------------- + // compareTo() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_comparisons() { + doTest_comparisons_Duration( + Duration.ofSeconds(-2L, 0), + Duration.ofSeconds(-2L, 999999998), + Duration.ofSeconds(-2L, 999999999), + Duration.ofSeconds(-1L, 0), + Duration.ofSeconds(-1L, 1), + Duration.ofSeconds(-1L, 999999998), + Duration.ofSeconds(-1L, 999999999), + Duration.ofSeconds(0L, 0), + Duration.ofSeconds(0L, 1), + Duration.ofSeconds(0L, 2), + Duration.ofSeconds(0L, 999999999), + Duration.ofSeconds(1L, 0), + Duration.ofSeconds(2L, 0) + ); + } + + void doTest_comparisons_Duration(Duration... durations) { + for (int i = 0; i < durations.length; i++) { + Duration a = durations[i]; + for (int j = 0; j < durations.length; j++) { + Duration b = durations[j]; + if (i < j) { + assertEquals(a.compareTo(b)< 0, true, a + " <=> " + b); + assertEquals(a.isLessThan(b), true, a + " <=> " + b); + assertEquals(a.isGreaterThan(b), false, a + " <=> " + b); + assertEquals(a.equals(b), false, a + " <=> " + b); + } else if (i > j) { + assertEquals(a.compareTo(b) > 0, true, a + " <=> " + b); + assertEquals(a.isLessThan(b), false, a + " <=> " + b); + assertEquals(a.isGreaterThan(b), true, a + " <=> " + b); + assertEquals(a.equals(b), false, a + " <=> " + b); + } else { + assertEquals(a.compareTo(b), 0, a + " <=> " + b); + assertEquals(a.isLessThan(b), false, a + " <=> " + b); + assertEquals(a.isGreaterThan(b), false, a + " <=> " + b); + assertEquals(a.equals(b), true, a + " <=> " + b); + } + } + } + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_compareTo_ObjectNull() { + Duration a = Duration.ofSeconds(0L, 0); + a.compareTo(null); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_isLessThan_ObjectNull() { + Duration a = Duration.ofSeconds(0L, 0); + a.isLessThan(null); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_isGreaterThan_ObjectNull() { + Duration a = Duration.ofSeconds(0L, 0); + a.isGreaterThan(null); + } + + @Test(expectedExceptions=ClassCastException.class, groups={"tck"}) + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void compareToNonDuration() { + Comparable c = Duration.ofSeconds(0L); + c.compareTo(new Object()); + } + + //----------------------------------------------------------------------- + // equals() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_equals() { + Duration test5a = Duration.ofSeconds(5L, 20); + Duration test5b = Duration.ofSeconds(5L, 20); + Duration test5n = Duration.ofSeconds(5L, 30); + Duration test6 = Duration.ofSeconds(6L, 20); + + assertEquals(test5a.equals(test5a), true); + assertEquals(test5a.equals(test5b), true); + assertEquals(test5a.equals(test5n), false); + assertEquals(test5a.equals(test6), false); + + assertEquals(test5b.equals(test5a), true); + assertEquals(test5b.equals(test5b), true); + assertEquals(test5b.equals(test5n), false); + assertEquals(test5b.equals(test6), false); + + assertEquals(test5n.equals(test5a), false); + assertEquals(test5n.equals(test5b), false); + assertEquals(test5n.equals(test5n), true); + assertEquals(test5n.equals(test6), false); + + assertEquals(test6.equals(test5a), false); + assertEquals(test6.equals(test5b), false); + assertEquals(test6.equals(test5n), false); + assertEquals(test6.equals(test6), true); + } + + @Test(groups={"tck"}) + public void test_equals_null() { + Duration test5 = Duration.ofSeconds(5L, 20); + assertEquals(test5.equals(null), false); + } + + @Test(groups={"tck"}) + public void test_equals_otherClass() { + Duration test5 = Duration.ofSeconds(5L, 20); + assertEquals(test5.equals(""), false); + } + + //----------------------------------------------------------------------- + // hashCode() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_hashCode() { + Duration test5a = Duration.ofSeconds(5L, 20); + Duration test5b = Duration.ofSeconds(5L, 20); + Duration test5n = Duration.ofSeconds(5L, 30); + Duration test6 = Duration.ofSeconds(6L, 20); + + assertEquals(test5a.hashCode() == test5a.hashCode(), true); + assertEquals(test5a.hashCode() == test5b.hashCode(), true); + assertEquals(test5b.hashCode() == test5b.hashCode(), true); + + assertEquals(test5a.hashCode() == test5n.hashCode(), false); + assertEquals(test5a.hashCode() == test6.hashCode(), false); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @DataProvider(name="ToString") + Object[][] provider_toString() { + return new Object[][] { + {0, 0, "PT0S"}, + {0, 1, "PT0.000000001S"}, + {0, 10, "PT0.00000001S"}, + {0, 100, "PT0.0000001S"}, + {0, 1000, "PT0.000001S"}, + {0, 10000, "PT0.00001S"}, + {0, 100000, "PT0.0001S"}, + {0, 1000000, "PT0.001S"}, + {0, 10000000, "PT0.01S"}, + {0, 100000000, "PT0.1S"}, + {0, 120000000, "PT0.12S"}, + {0, 123000000, "PT0.123S"}, + {0, 123400000, "PT0.1234S"}, + {0, 123450000, "PT0.12345S"}, + {0, 123456000, "PT0.123456S"}, + {0, 123456700, "PT0.1234567S"}, + {0, 123456780, "PT0.12345678S"}, + {0, 123456789, "PT0.123456789S"}, + {1, 0, "PT1S"}, + {-1, 0, "PT-1S"}, + {-1, 1000, "PT-0.999999S"}, + {-1, 900000000, "PT-0.1S"}, + {Long.MAX_VALUE, 0, "PT9223372036854775807S"}, + {Long.MIN_VALUE, 0, "PT-9223372036854775808S"}, + }; + } + + @Test(dataProvider="ToString", groups={"tck"}) + public void test_toString(long seconds, int nanos, String expected) { + Duration t = Duration.ofSeconds(seconds, nanos); + assertEquals(t.toString(), expected); + } + +} diff --git a/jdk/test/java/time/tck/java/time/TCKInstant.java b/jdk/test/java/time/tck/java/time/TCKInstant.java new file mode 100644 index 00000000000..e1f99d925ed --- /dev/null +++ b/jdk/test/java/time/tck/java/time/TCKInstant.java @@ -0,0 +1,1680 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time; + +import static java.time.temporal.ChronoField.INSTANT_SECONDS; +import static java.time.temporal.ChronoField.MICRO_OF_SECOND; +import static java.time.temporal.ChronoField.MILLI_OF_SECOND; +import static java.time.temporal.ChronoField.NANO_OF_SECOND; +import static java.time.temporal.ChronoUnit.DAYS; +import static java.time.temporal.ChronoUnit.NANOS; +import static java.time.temporal.ChronoUnit.SECONDS; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +import java.time.Clock; +import java.time.DateTimeException; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoField; +import java.time.temporal.JulianFields; +import java.time.temporal.Queries; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalField; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test Instant. + */ +@Test +public class TCKInstant extends AbstractDateTimeTest { + + private static final long MIN_SECOND = Instant.MIN.getEpochSecond(); + private static final long MAX_SECOND = Instant.MAX.getEpochSecond(); + + private Instant TEST_12345_123456789; + + @BeforeMethod + public void setUp() { + TEST_12345_123456789 = Instant.ofEpochSecond(12345, 123456789); + } + + //----------------------------------------------------------------------- + @Override + protected List samples() { + TemporalAccessor[] array = {TEST_12345_123456789, Instant.MIN, Instant.MAX, Instant.EPOCH}; + return Arrays.asList(array); + } + + @Override + protected List validFields() { + TemporalField[] array = { + NANO_OF_SECOND, + MICRO_OF_SECOND, + MILLI_OF_SECOND, + INSTANT_SECONDS, + }; + return Arrays.asList(array); + } + + @Override + protected List invalidFields() { + List list = new ArrayList<>(Arrays.asList(ChronoField.values())); + list.removeAll(validFields()); + list.add(JulianFields.JULIAN_DAY); + list.add(JulianFields.MODIFIED_JULIAN_DAY); + list.add(JulianFields.RATA_DIE); + return list; + } + + //----------------------------------------------------------------------- + @Test + public void test_serialization() throws Exception { + assertSerializable(Instant.ofEpochMilli(134l)); + } + + @Test + public void test_serialization_format() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baos) ) { + dos.writeByte(2); + dos.writeLong(654321); + dos.writeInt(123456789); + } + byte[] bytes = baos.toByteArray(); + assertSerializedBySer(Instant.ofEpochSecond(654321, 123456789), bytes); + } + + //----------------------------------------------------------------------- + private void check(Instant instant, long epochSecs, int nos) { + assertEquals(instant.getEpochSecond(), epochSecs); + assertEquals(instant.getNano(), nos); + assertEquals(instant, instant); + assertEquals(instant.hashCode(), instant.hashCode()); + } + + //----------------------------------------------------------------------- + @Test + public void constant_EPOCH() { + check(Instant.EPOCH, 0, 0); + } + + @Test + public void constant_MIN() { + check(Instant.MIN, -31557014167219200L, 0); + } + + @Test + public void constant_MAX() { + check(Instant.MAX, 31556889864403199L, 999_999_999); + } + + //----------------------------------------------------------------------- + // now() + //----------------------------------------------------------------------- + @Test + public void now() { + Instant expected = Instant.now(Clock.systemUTC()); + Instant test = Instant.now(); + long diff = Math.abs(test.toEpochMilli() - expected.toEpochMilli()); + assertTrue(diff < 100); // less than 0.1 secs + } + + //----------------------------------------------------------------------- + // now(Clock) + //----------------------------------------------------------------------- + @Test(expectedExceptions=NullPointerException.class) + public void now_Clock_nullClock() { + Instant.now(null); + } + + @Test + public void now_Clock_allSecsInDay_utc() { + for (int i = 0; i < (2 * 24 * 60 * 60); i++) { + Instant expected = Instant.ofEpochSecond(i).plusNanos(123456789L); + Clock clock = Clock.fixed(expected, ZoneOffset.UTC); + Instant test = Instant.now(clock); + assertEquals(test, expected); + } + } + + @Test + public void now_Clock_allSecsInDay_beforeEpoch() { + for (int i =-1; i >= -(24 * 60 * 60); i--) { + Instant expected = Instant.ofEpochSecond(i).plusNanos(123456789L); + Clock clock = Clock.fixed(expected, ZoneOffset.UTC); + Instant test = Instant.now(clock); + assertEquals(test, expected); + } + } + + //----------------------------------------------------------------------- + // ofEpochSecond(long) + //----------------------------------------------------------------------- + @Test + public void factory_seconds_long() { + for (long i = -2; i <= 2; i++) { + Instant t = Instant.ofEpochSecond(i); + assertEquals(t.getEpochSecond(), i); + assertEquals(t.getNano(), 0); + } + } + + //----------------------------------------------------------------------- + // ofEpochSecond(long,long) + //----------------------------------------------------------------------- + @Test + public void factory_seconds_long_long() { + for (long i = -2; i <= 2; i++) { + for (int j = 0; j < 10; j++) { + Instant t = Instant.ofEpochSecond(i, j); + assertEquals(t.getEpochSecond(), i); + assertEquals(t.getNano(), j); + } + for (int j = -10; j < 0; j++) { + Instant t = Instant.ofEpochSecond(i, j); + assertEquals(t.getEpochSecond(), i - 1); + assertEquals(t.getNano(), j + 1000000000); + } + for (int j = 999999990; j < 1000000000; j++) { + Instant t = Instant.ofEpochSecond(i, j); + assertEquals(t.getEpochSecond(), i); + assertEquals(t.getNano(), j); + } + } + } + + @Test + public void factory_seconds_long_long_nanosNegativeAdjusted() { + Instant test = Instant.ofEpochSecond(2L, -1); + assertEquals(test.getEpochSecond(), 1); + assertEquals(test.getNano(), 999999999); + } + + @Test(expectedExceptions=DateTimeException.class) + public void factory_seconds_long_long_tooBig() { + Instant.ofEpochSecond(MAX_SECOND, 1000000000); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void factory_seconds_long_long_tooBigBig() { + Instant.ofEpochSecond(Long.MAX_VALUE, Long.MAX_VALUE); + } + + //----------------------------------------------------------------------- + // ofEpochMilli(long) + //----------------------------------------------------------------------- + @DataProvider(name="MillisInstantNoNanos") + Object[][] provider_factory_millis_long() { + return new Object[][] { + {0, 0, 0}, + {1, 0, 1000000}, + {2, 0, 2000000}, + {999, 0, 999000000}, + {1000, 1, 0}, + {1001, 1, 1000000}, + {-1, -1, 999000000}, + {-2, -1, 998000000}, + {-999, -1, 1000000}, + {-1000, -1, 0}, + {-1001, -2, 999000000}, + }; + } + + @Test(dataProvider="MillisInstantNoNanos") + public void factory_millis_long(long millis, long expectedSeconds, int expectedNanoOfSecond) { + Instant t = Instant.ofEpochMilli(millis); + assertEquals(t.getEpochSecond(), expectedSeconds); + assertEquals(t.getNano(), expectedNanoOfSecond); + } + + //----------------------------------------------------------------------- + // parse(String) + //----------------------------------------------------------------------- + // see also parse tests under toString() + @DataProvider(name="Parse") + Object[][] provider_factory_parse() { + return new Object[][] { + {"1970-01-01T00:00:00Z", 0, 0}, + {"1970-01-01t00:00:00Z", 0, 0}, + {"1970-01-01T00:00:00z", 0, 0}, + {"1970-01-01T00:00:00.0Z", 0, 0}, + {"1970-01-01T00:00:00.000000000Z", 0, 0}, + + {"1970-01-01T00:00:00.000000001Z", 0, 1}, + {"1970-01-01T00:00:00.100000000Z", 0, 100000000}, + {"1970-01-01T00:00:01Z", 1, 0}, + {"1970-01-01T00:01:00Z", 60, 0}, + {"1970-01-01T00:01:01Z", 61, 0}, + {"1970-01-01T00:01:01.000000001Z", 61, 1}, + {"1970-01-01T01:00:00.000000000Z", 3600, 0}, + {"1970-01-01T01:01:01.000000001Z", 3661, 1}, + {"1970-01-02T01:01:01.100000000Z", 90061, 100000000}, + }; + } + + @Test(dataProvider="Parse") + public void factory_parse(String text, long expectedEpochSeconds, int expectedNanoOfSecond) { + Instant t = Instant.parse(text); + assertEquals(t.getEpochSecond(), expectedEpochSeconds); + assertEquals(t.getNano(), expectedNanoOfSecond); + } + + @Test(dataProvider="Parse") + public void factory_parseLowercase(String text, long expectedEpochSeconds, int expectedNanoOfSecond) { + Instant t = Instant.parse(text.toLowerCase(Locale.ENGLISH)); + assertEquals(t.getEpochSecond(), expectedEpochSeconds); + assertEquals(t.getNano(), expectedNanoOfSecond); + } + +// TODO: should comma be accepted? +// @Test(dataProvider="Parse") +// public void factory_parse_comma(String text, long expectedEpochSeconds, int expectedNanoOfSecond) { +// text = text.replace('.', ','); +// Instant t = Instant.parse(text); +// assertEquals(t.getEpochSecond(), expectedEpochSeconds); +// assertEquals(t.getNano(), expectedNanoOfSecond); +// } + + @DataProvider(name="ParseFailures") + Object[][] provider_factory_parseFailures() { + return new Object[][] { + {""}, + {"Z"}, + {"1970-01-01T00:00:00"}, + {"1970-01-01T00:00:0Z"}, + {"1970-01-01T00:00:00.0000000000Z"}, + }; + } + + @Test(dataProvider="ParseFailures", expectedExceptions=DateTimeParseException.class) + public void factory_parseFailures(String text) { + Instant.parse(text); + } + + @Test(dataProvider="ParseFailures", expectedExceptions=DateTimeParseException.class) + public void factory_parseFailures_comma(String text) { + text = text.replace('.', ','); + Instant.parse(text); + } + + @Test(expectedExceptions=NullPointerException.class) + public void factory_parse_nullText() { + Instant.parse(null); + } + + //----------------------------------------------------------------------- + // get(TemporalField) + //----------------------------------------------------------------------- + @Test + public void test_get_TemporalField() { + Instant test = TEST_12345_123456789; + assertEquals(test.get(ChronoField.NANO_OF_SECOND), 123456789); + assertEquals(test.get(ChronoField.MICRO_OF_SECOND), 123456); + assertEquals(test.get(ChronoField.MILLI_OF_SECOND), 123); + } + + @Test + public void test_getLong_TemporalField() { + Instant test = TEST_12345_123456789; + assertEquals(test.getLong(ChronoField.NANO_OF_SECOND), 123456789); + assertEquals(test.getLong(ChronoField.MICRO_OF_SECOND), 123456); + assertEquals(test.getLong(ChronoField.MILLI_OF_SECOND), 123); + assertEquals(test.getLong(ChronoField.INSTANT_SECONDS), 12345); + } + + //----------------------------------------------------------------------- + // query(TemporalQuery) + //----------------------------------------------------------------------- + @Test + public void test_query_chrono() { + assertEquals(TEST_12345_123456789.query(Queries.chrono()), null); + assertEquals(Queries.chrono().queryFrom(TEST_12345_123456789), null); + } + + @Test + public void test_query_zoneId() { + assertEquals(TEST_12345_123456789.query(Queries.zoneId()), null); + assertEquals(Queries.zoneId().queryFrom(TEST_12345_123456789), null); + } + + @Test + public void test_query_precision() { + assertEquals(TEST_12345_123456789.query(Queries.precision()), NANOS); + assertEquals(Queries.precision().queryFrom(TEST_12345_123456789), NANOS); + } + + @Test + public void test_query_offset() { + assertEquals(TEST_12345_123456789.query(Queries.offset()), null); + assertEquals(Queries.offset().queryFrom(TEST_12345_123456789), null); + } + + @Test + public void test_query_zone() { + assertEquals(TEST_12345_123456789.query(Queries.zone()), null); + assertEquals(Queries.zone().queryFrom(TEST_12345_123456789), null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_query_null() { + TEST_12345_123456789.query(null); + } + + //----------------------------------------------------------------------- + @DataProvider(name="Plus") + Object[][] provider_plus() { + return new Object[][] { + {MIN_SECOND, 0, -MIN_SECOND, 0, 0, 0}, + + {MIN_SECOND, 0, 1, 0, MIN_SECOND + 1, 0}, + {MIN_SECOND, 0, 0, 500, MIN_SECOND, 500}, + {MIN_SECOND, 0, 0, 1000000000, MIN_SECOND + 1, 0}, + + {MIN_SECOND + 1, 0, -1, 0, MIN_SECOND, 0}, + {MIN_SECOND + 1, 0, 0, -500, MIN_SECOND, 999999500}, + {MIN_SECOND + 1, 0, 0, -1000000000, MIN_SECOND, 0}, + + {-4, 666666667, -4, 666666667, -7, 333333334}, + {-4, 666666667, -3, 0, -7, 666666667}, + {-4, 666666667, -2, 0, -6, 666666667}, + {-4, 666666667, -1, 0, -5, 666666667}, + {-4, 666666667, -1, 333333334, -4, 1}, + {-4, 666666667, -1, 666666667, -4, 333333334}, + {-4, 666666667, -1, 999999999, -4, 666666666}, + {-4, 666666667, 0, 0, -4, 666666667}, + {-4, 666666667, 0, 1, -4, 666666668}, + {-4, 666666667, 0, 333333333, -3, 0}, + {-4, 666666667, 0, 666666666, -3, 333333333}, + {-4, 666666667, 1, 0, -3, 666666667}, + {-4, 666666667, 2, 0, -2, 666666667}, + {-4, 666666667, 3, 0, -1, 666666667}, + {-4, 666666667, 3, 333333333, 0, 0}, + + {-3, 0, -4, 666666667, -7, 666666667}, + {-3, 0, -3, 0, -6, 0}, + {-3, 0, -2, 0, -5, 0}, + {-3, 0, -1, 0, -4, 0}, + {-3, 0, -1, 333333334, -4, 333333334}, + {-3, 0, -1, 666666667, -4, 666666667}, + {-3, 0, -1, 999999999, -4, 999999999}, + {-3, 0, 0, 0, -3, 0}, + {-3, 0, 0, 1, -3, 1}, + {-3, 0, 0, 333333333, -3, 333333333}, + {-3, 0, 0, 666666666, -3, 666666666}, + {-3, 0, 1, 0, -2, 0}, + {-3, 0, 2, 0, -1, 0}, + {-3, 0, 3, 0, 0, 0}, + {-3, 0, 3, 333333333, 0, 333333333}, + + {-2, 0, -4, 666666667, -6, 666666667}, + {-2, 0, -3, 0, -5, 0}, + {-2, 0, -2, 0, -4, 0}, + {-2, 0, -1, 0, -3, 0}, + {-2, 0, -1, 333333334, -3, 333333334}, + {-2, 0, -1, 666666667, -3, 666666667}, + {-2, 0, -1, 999999999, -3, 999999999}, + {-2, 0, 0, 0, -2, 0}, + {-2, 0, 0, 1, -2, 1}, + {-2, 0, 0, 333333333, -2, 333333333}, + {-2, 0, 0, 666666666, -2, 666666666}, + {-2, 0, 1, 0, -1, 0}, + {-2, 0, 2, 0, 0, 0}, + {-2, 0, 3, 0, 1, 0}, + {-2, 0, 3, 333333333, 1, 333333333}, + + {-1, 0, -4, 666666667, -5, 666666667}, + {-1, 0, -3, 0, -4, 0}, + {-1, 0, -2, 0, -3, 0}, + {-1, 0, -1, 0, -2, 0}, + {-1, 0, -1, 333333334, -2, 333333334}, + {-1, 0, -1, 666666667, -2, 666666667}, + {-1, 0, -1, 999999999, -2, 999999999}, + {-1, 0, 0, 0, -1, 0}, + {-1, 0, 0, 1, -1, 1}, + {-1, 0, 0, 333333333, -1, 333333333}, + {-1, 0, 0, 666666666, -1, 666666666}, + {-1, 0, 1, 0, 0, 0}, + {-1, 0, 2, 0, 1, 0}, + {-1, 0, 3, 0, 2, 0}, + {-1, 0, 3, 333333333, 2, 333333333}, + + {-1, 666666667, -4, 666666667, -4, 333333334}, + {-1, 666666667, -3, 0, -4, 666666667}, + {-1, 666666667, -2, 0, -3, 666666667}, + {-1, 666666667, -1, 0, -2, 666666667}, + {-1, 666666667, -1, 333333334, -1, 1}, + {-1, 666666667, -1, 666666667, -1, 333333334}, + {-1, 666666667, -1, 999999999, -1, 666666666}, + {-1, 666666667, 0, 0, -1, 666666667}, + {-1, 666666667, 0, 1, -1, 666666668}, + {-1, 666666667, 0, 333333333, 0, 0}, + {-1, 666666667, 0, 666666666, 0, 333333333}, + {-1, 666666667, 1, 0, 0, 666666667}, + {-1, 666666667, 2, 0, 1, 666666667}, + {-1, 666666667, 3, 0, 2, 666666667}, + {-1, 666666667, 3, 333333333, 3, 0}, + + {0, 0, -4, 666666667, -4, 666666667}, + {0, 0, -3, 0, -3, 0}, + {0, 0, -2, 0, -2, 0}, + {0, 0, -1, 0, -1, 0}, + {0, 0, -1, 333333334, -1, 333333334}, + {0, 0, -1, 666666667, -1, 666666667}, + {0, 0, -1, 999999999, -1, 999999999}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 1, 0, 1}, + {0, 0, 0, 333333333, 0, 333333333}, + {0, 0, 0, 666666666, 0, 666666666}, + {0, 0, 1, 0, 1, 0}, + {0, 0, 2, 0, 2, 0}, + {0, 0, 3, 0, 3, 0}, + {0, 0, 3, 333333333, 3, 333333333}, + + {0, 333333333, -4, 666666667, -3, 0}, + {0, 333333333, -3, 0, -3, 333333333}, + {0, 333333333, -2, 0, -2, 333333333}, + {0, 333333333, -1, 0, -1, 333333333}, + {0, 333333333, -1, 333333334, -1, 666666667}, + {0, 333333333, -1, 666666667, 0, 0}, + {0, 333333333, -1, 999999999, 0, 333333332}, + {0, 333333333, 0, 0, 0, 333333333}, + {0, 333333333, 0, 1, 0, 333333334}, + {0, 333333333, 0, 333333333, 0, 666666666}, + {0, 333333333, 0, 666666666, 0, 999999999}, + {0, 333333333, 1, 0, 1, 333333333}, + {0, 333333333, 2, 0, 2, 333333333}, + {0, 333333333, 3, 0, 3, 333333333}, + {0, 333333333, 3, 333333333, 3, 666666666}, + + {1, 0, -4, 666666667, -3, 666666667}, + {1, 0, -3, 0, -2, 0}, + {1, 0, -2, 0, -1, 0}, + {1, 0, -1, 0, 0, 0}, + {1, 0, -1, 333333334, 0, 333333334}, + {1, 0, -1, 666666667, 0, 666666667}, + {1, 0, -1, 999999999, 0, 999999999}, + {1, 0, 0, 0, 1, 0}, + {1, 0, 0, 1, 1, 1}, + {1, 0, 0, 333333333, 1, 333333333}, + {1, 0, 0, 666666666, 1, 666666666}, + {1, 0, 1, 0, 2, 0}, + {1, 0, 2, 0, 3, 0}, + {1, 0, 3, 0, 4, 0}, + {1, 0, 3, 333333333, 4, 333333333}, + + {2, 0, -4, 666666667, -2, 666666667}, + {2, 0, -3, 0, -1, 0}, + {2, 0, -2, 0, 0, 0}, + {2, 0, -1, 0, 1, 0}, + {2, 0, -1, 333333334, 1, 333333334}, + {2, 0, -1, 666666667, 1, 666666667}, + {2, 0, -1, 999999999, 1, 999999999}, + {2, 0, 0, 0, 2, 0}, + {2, 0, 0, 1, 2, 1}, + {2, 0, 0, 333333333, 2, 333333333}, + {2, 0, 0, 666666666, 2, 666666666}, + {2, 0, 1, 0, 3, 0}, + {2, 0, 2, 0, 4, 0}, + {2, 0, 3, 0, 5, 0}, + {2, 0, 3, 333333333, 5, 333333333}, + + {3, 0, -4, 666666667, -1, 666666667}, + {3, 0, -3, 0, 0, 0}, + {3, 0, -2, 0, 1, 0}, + {3, 0, -1, 0, 2, 0}, + {3, 0, -1, 333333334, 2, 333333334}, + {3, 0, -1, 666666667, 2, 666666667}, + {3, 0, -1, 999999999, 2, 999999999}, + {3, 0, 0, 0, 3, 0}, + {3, 0, 0, 1, 3, 1}, + {3, 0, 0, 333333333, 3, 333333333}, + {3, 0, 0, 666666666, 3, 666666666}, + {3, 0, 1, 0, 4, 0}, + {3, 0, 2, 0, 5, 0}, + {3, 0, 3, 0, 6, 0}, + {3, 0, 3, 333333333, 6, 333333333}, + + {3, 333333333, -4, 666666667, 0, 0}, + {3, 333333333, -3, 0, 0, 333333333}, + {3, 333333333, -2, 0, 1, 333333333}, + {3, 333333333, -1, 0, 2, 333333333}, + {3, 333333333, -1, 333333334, 2, 666666667}, + {3, 333333333, -1, 666666667, 3, 0}, + {3, 333333333, -1, 999999999, 3, 333333332}, + {3, 333333333, 0, 0, 3, 333333333}, + {3, 333333333, 0, 1, 3, 333333334}, + {3, 333333333, 0, 333333333, 3, 666666666}, + {3, 333333333, 0, 666666666, 3, 999999999}, + {3, 333333333, 1, 0, 4, 333333333}, + {3, 333333333, 2, 0, 5, 333333333}, + {3, 333333333, 3, 0, 6, 333333333}, + {3, 333333333, 3, 333333333, 6, 666666666}, + + {MAX_SECOND - 1, 0, 1, 0, MAX_SECOND, 0}, + {MAX_SECOND - 1, 0, 0, 500, MAX_SECOND - 1, 500}, + {MAX_SECOND - 1, 0, 0, 1000000000, MAX_SECOND, 0}, + + {MAX_SECOND, 0, -1, 0, MAX_SECOND - 1, 0}, + {MAX_SECOND, 0, 0, -500, MAX_SECOND - 1, 999999500}, + {MAX_SECOND, 0, 0, -1000000000, MAX_SECOND - 1, 0}, + + {MAX_SECOND, 0, -MAX_SECOND, 0, 0, 0}, + }; + } + + @Test(dataProvider="Plus") + public void plus_Duration(long seconds, int nanos, long otherSeconds, int otherNanos, long expectedSeconds, int expectedNanoOfSecond) { + Instant i = Instant.ofEpochSecond(seconds, nanos).plus(Duration.ofSeconds(otherSeconds, otherNanos)); + assertEquals(i.getEpochSecond(), expectedSeconds); + assertEquals(i.getNano(), expectedNanoOfSecond); + } + + @Test(expectedExceptions=DateTimeException.class) + public void plus_Duration_overflowTooBig() { + Instant i = Instant.ofEpochSecond(MAX_SECOND, 999999999); + i.plus(Duration.ofSeconds(0, 1)); + } + + @Test(expectedExceptions=DateTimeException.class) + public void plus_Duration_overflowTooSmall() { + Instant i = Instant.ofEpochSecond(MIN_SECOND); + i.plus(Duration.ofSeconds(-1, 999999999)); + } + + //-----------------------------------------------------------------------a + @Test(dataProvider="Plus") + public void plus_longTemporalUnit(long seconds, int nanos, long otherSeconds, int otherNanos, long expectedSeconds, int expectedNanoOfSecond) { + Instant i = Instant.ofEpochSecond(seconds, nanos).plus(otherSeconds, SECONDS).plus(otherNanos, NANOS); + assertEquals(i.getEpochSecond(), expectedSeconds); + assertEquals(i.getNano(), expectedNanoOfSecond); + } + + @Test(expectedExceptions=DateTimeException.class) + public void plus_longTemporalUnit_overflowTooBig() { + Instant i = Instant.ofEpochSecond(MAX_SECOND, 999999999); + i.plus(1, NANOS); + } + + @Test(expectedExceptions=DateTimeException.class) + public void plus_longTemporalUnit_overflowTooSmall() { + Instant i = Instant.ofEpochSecond(MIN_SECOND); + i.plus(999999999, NANOS); + i.plus(-1, SECONDS); + } + + //----------------------------------------------------------------------- + @DataProvider(name="PlusSeconds") + Object[][] provider_plusSeconds_long() { + return new Object[][] { + {0, 0, 0, 0, 0}, + {0, 0, 1, 1, 0}, + {0, 0, -1, -1, 0}, + {0, 0, MAX_SECOND, MAX_SECOND, 0}, + {0, 0, MIN_SECOND, MIN_SECOND, 0}, + {1, 0, 0, 1, 0}, + {1, 0, 1, 2, 0}, + {1, 0, -1, 0, 0}, + {1, 0, MAX_SECOND - 1, MAX_SECOND, 0}, + {1, 0, MIN_SECOND, MIN_SECOND + 1, 0}, + {1, 1, 0, 1, 1}, + {1, 1, 1, 2, 1}, + {1, 1, -1, 0, 1}, + {1, 1, MAX_SECOND - 1, MAX_SECOND, 1}, + {1, 1, MIN_SECOND, MIN_SECOND + 1, 1}, + {-1, 1, 0, -1, 1}, + {-1, 1, 1, 0, 1}, + {-1, 1, -1, -2, 1}, + {-1, 1, MAX_SECOND, MAX_SECOND - 1, 1}, + {-1, 1, MIN_SECOND + 1, MIN_SECOND, 1}, + + {MAX_SECOND, 2, -MAX_SECOND, 0, 2}, + {MIN_SECOND, 2, -MIN_SECOND, 0, 2}, + }; + } + + @Test(dataProvider="PlusSeconds") + public void plusSeconds_long(long seconds, int nanos, long amount, long expectedSeconds, int expectedNanoOfSecond) { + Instant t = Instant.ofEpochSecond(seconds, nanos); + t = t.plusSeconds(amount); + assertEquals(t.getEpochSecond(), expectedSeconds); + assertEquals(t.getNano(), expectedNanoOfSecond); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void plusSeconds_long_overflowTooBig() { + Instant t = Instant.ofEpochSecond(1, 0); + t.plusSeconds(Long.MAX_VALUE); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void plusSeconds_long_overflowTooSmall() { + Instant t = Instant.ofEpochSecond(-1, 0); + t.plusSeconds(Long.MIN_VALUE); + } + + //----------------------------------------------------------------------- + @DataProvider(name="PlusMillis") + Object[][] provider_plusMillis_long() { + return new Object[][] { + {0, 0, 0, 0, 0}, + {0, 0, 1, 0, 1000000}, + {0, 0, 999, 0, 999000000}, + {0, 0, 1000, 1, 0}, + {0, 0, 1001, 1, 1000000}, + {0, 0, 1999, 1, 999000000}, + {0, 0, 2000, 2, 0}, + {0, 0, -1, -1, 999000000}, + {0, 0, -999, -1, 1000000}, + {0, 0, -1000, -1, 0}, + {0, 0, -1001, -2, 999000000}, + {0, 0, -1999, -2, 1000000}, + + {0, 1, 0, 0, 1}, + {0, 1, 1, 0, 1000001}, + {0, 1, 998, 0, 998000001}, + {0, 1, 999, 0, 999000001}, + {0, 1, 1000, 1, 1}, + {0, 1, 1998, 1, 998000001}, + {0, 1, 1999, 1, 999000001}, + {0, 1, 2000, 2, 1}, + {0, 1, -1, -1, 999000001}, + {0, 1, -2, -1, 998000001}, + {0, 1, -1000, -1, 1}, + {0, 1, -1001, -2, 999000001}, + + {0, 1000000, 0, 0, 1000000}, + {0, 1000000, 1, 0, 2000000}, + {0, 1000000, 998, 0, 999000000}, + {0, 1000000, 999, 1, 0}, + {0, 1000000, 1000, 1, 1000000}, + {0, 1000000, 1998, 1, 999000000}, + {0, 1000000, 1999, 2, 0}, + {0, 1000000, 2000, 2, 1000000}, + {0, 1000000, -1, 0, 0}, + {0, 1000000, -2, -1, 999000000}, + {0, 1000000, -999, -1, 2000000}, + {0, 1000000, -1000, -1, 1000000}, + {0, 1000000, -1001, -1, 0}, + {0, 1000000, -1002, -2, 999000000}, + + {0, 999999999, 0, 0, 999999999}, + {0, 999999999, 1, 1, 999999}, + {0, 999999999, 999, 1, 998999999}, + {0, 999999999, 1000, 1, 999999999}, + {0, 999999999, 1001, 2, 999999}, + {0, 999999999, -1, 0, 998999999}, + {0, 999999999, -1000, -1, 999999999}, + {0, 999999999, -1001, -1, 998999999}, + + {0, 0, Long.MAX_VALUE, Long.MAX_VALUE / 1000, (int) (Long.MAX_VALUE % 1000) * 1000000}, + {0, 0, Long.MIN_VALUE, Long.MIN_VALUE / 1000 - 1, (int) (Long.MIN_VALUE % 1000) * 1000000 + 1000000000}, + }; + } + + @Test(dataProvider="PlusMillis") + public void plusMillis_long(long seconds, int nanos, long amount, long expectedSeconds, int expectedNanoOfSecond) { + Instant t = Instant.ofEpochSecond(seconds, nanos); + t = t.plusMillis(amount); + assertEquals(t.getEpochSecond(), expectedSeconds); + assertEquals(t.getNano(), expectedNanoOfSecond); + } + @Test(dataProvider="PlusMillis") + public void plusMillis_long_oneMore(long seconds, int nanos, long amount, long expectedSeconds, int expectedNanoOfSecond) { + Instant t = Instant.ofEpochSecond(seconds + 1, nanos); + t = t.plusMillis(amount); + assertEquals(t.getEpochSecond(), expectedSeconds + 1); + assertEquals(t.getNano(), expectedNanoOfSecond); + } + @Test(dataProvider="PlusMillis") + public void plusMillis_long_minusOneLess(long seconds, int nanos, long amount, long expectedSeconds, int expectedNanoOfSecond) { + Instant t = Instant.ofEpochSecond(seconds - 1, nanos); + t = t.plusMillis(amount); + assertEquals(t.getEpochSecond(), expectedSeconds - 1); + assertEquals(t.getNano(), expectedNanoOfSecond); + } + + @Test + public void plusMillis_long_max() { + Instant t = Instant.ofEpochSecond(MAX_SECOND, 998999999); + t = t.plusMillis(1); + assertEquals(t.getEpochSecond(), MAX_SECOND); + assertEquals(t.getNano(), 999999999); + } + + @Test(expectedExceptions=DateTimeException.class) + public void plusMillis_long_overflowTooBig() { + Instant t = Instant.ofEpochSecond(MAX_SECOND, 999000000); + t.plusMillis(1); + } + + @Test + public void plusMillis_long_min() { + Instant t = Instant.ofEpochSecond(MIN_SECOND, 1000000); + t = t.plusMillis(-1); + assertEquals(t.getEpochSecond(), MIN_SECOND); + assertEquals(t.getNano(), 0); + } + + @Test(expectedExceptions=DateTimeException.class) + public void plusMillis_long_overflowTooSmall() { + Instant t = Instant.ofEpochSecond(MIN_SECOND, 0); + t.plusMillis(-1); + } + + //----------------------------------------------------------------------- + @DataProvider(name="PlusNanos") + Object[][] provider_plusNanos_long() { + return new Object[][] { + {0, 0, 0, 0, 0}, + {0, 0, 1, 0, 1}, + {0, 0, 999999999, 0, 999999999}, + {0, 0, 1000000000, 1, 0}, + {0, 0, 1000000001, 1, 1}, + {0, 0, 1999999999, 1, 999999999}, + {0, 0, 2000000000, 2, 0}, + {0, 0, -1, -1, 999999999}, + {0, 0, -999999999, -1, 1}, + {0, 0, -1000000000, -1, 0}, + {0, 0, -1000000001, -2, 999999999}, + {0, 0, -1999999999, -2, 1}, + + {1, 0, 0, 1, 0}, + {1, 0, 1, 1, 1}, + {1, 0, 999999999, 1, 999999999}, + {1, 0, 1000000000, 2, 0}, + {1, 0, 1000000001, 2, 1}, + {1, 0, 1999999999, 2, 999999999}, + {1, 0, 2000000000, 3, 0}, + {1, 0, -1, 0, 999999999}, + {1, 0, -999999999, 0, 1}, + {1, 0, -1000000000, 0, 0}, + {1, 0, -1000000001, -1, 999999999}, + {1, 0, -1999999999, -1, 1}, + + {-1, 0, 0, -1, 0}, + {-1, 0, 1, -1, 1}, + {-1, 0, 999999999, -1, 999999999}, + {-1, 0, 1000000000, 0, 0}, + {-1, 0, 1000000001, 0, 1}, + {-1, 0, 1999999999, 0, 999999999}, + {-1, 0, 2000000000, 1, 0}, + {-1, 0, -1, -2, 999999999}, + {-1, 0, -999999999, -2, 1}, + {-1, 0, -1000000000, -2, 0}, + {-1, 0, -1000000001, -3, 999999999}, + {-1, 0, -1999999999, -3, 1}, + + {1, 1, 0, 1, 1}, + {1, 1, 1, 1, 2}, + {1, 1, 999999998, 1, 999999999}, + {1, 1, 999999999, 2, 0}, + {1, 1, 1000000000, 2, 1}, + {1, 1, 1999999998, 2, 999999999}, + {1, 1, 1999999999, 3, 0}, + {1, 1, 2000000000, 3, 1}, + {1, 1, -1, 1, 0}, + {1, 1, -2, 0, 999999999}, + {1, 1, -1000000000, 0, 1}, + {1, 1, -1000000001, 0, 0}, + {1, 1, -1000000002, -1, 999999999}, + {1, 1, -2000000000, -1, 1}, + + {1, 999999999, 0, 1, 999999999}, + {1, 999999999, 1, 2, 0}, + {1, 999999999, 999999999, 2, 999999998}, + {1, 999999999, 1000000000, 2, 999999999}, + {1, 999999999, 1000000001, 3, 0}, + {1, 999999999, -1, 1, 999999998}, + {1, 999999999, -1000000000, 0, 999999999}, + {1, 999999999, -1000000001, 0, 999999998}, + {1, 999999999, -1999999999, 0, 0}, + {1, 999999999, -2000000000, -1, 999999999}, + + {MAX_SECOND, 0, 999999999, MAX_SECOND, 999999999}, + {MAX_SECOND - 1, 0, 1999999999, MAX_SECOND, 999999999}, + {MIN_SECOND, 1, -1, MIN_SECOND, 0}, + {MIN_SECOND + 1, 1, -1000000001, MIN_SECOND, 0}, + + {0, 0, MAX_SECOND, MAX_SECOND / 1000000000, (int) (MAX_SECOND % 1000000000)}, + {0, 0, MIN_SECOND, MIN_SECOND / 1000000000 - 1, (int) (MIN_SECOND % 1000000000) + 1000000000}, + }; + } + + @Test(dataProvider="PlusNanos") + public void plusNanos_long(long seconds, int nanos, long amount, long expectedSeconds, int expectedNanoOfSecond) { + Instant t = Instant.ofEpochSecond(seconds, nanos); + t = t.plusNanos(amount); + assertEquals(t.getEpochSecond(), expectedSeconds); + assertEquals(t.getNano(), expectedNanoOfSecond); + } + + @Test(expectedExceptions=DateTimeException.class) + public void plusNanos_long_overflowTooBig() { + Instant t = Instant.ofEpochSecond(MAX_SECOND, 999999999); + t.plusNanos(1); + } + + @Test(expectedExceptions=DateTimeException.class) + public void plusNanos_long_overflowTooSmall() { + Instant t = Instant.ofEpochSecond(MIN_SECOND, 0); + t.plusNanos(-1); + } + + //----------------------------------------------------------------------- + @DataProvider(name="Minus") + Object[][] provider_minus() { + return new Object[][] { + {MIN_SECOND, 0, MIN_SECOND, 0, 0, 0}, + + {MIN_SECOND, 0, -1, 0, MIN_SECOND + 1, 0}, + {MIN_SECOND, 0, 0, -500, MIN_SECOND, 500}, + {MIN_SECOND, 0, 0, -1000000000, MIN_SECOND + 1, 0}, + + {MIN_SECOND + 1, 0, 1, 0, MIN_SECOND, 0}, + {MIN_SECOND + 1, 0, 0, 500, MIN_SECOND, 999999500}, + {MIN_SECOND + 1, 0, 0, 1000000000, MIN_SECOND, 0}, + + {-4, 666666667, -4, 666666667, 0, 0}, + {-4, 666666667, -3, 0, -1, 666666667}, + {-4, 666666667, -2, 0, -2, 666666667}, + {-4, 666666667, -1, 0, -3, 666666667}, + {-4, 666666667, -1, 333333334, -3, 333333333}, + {-4, 666666667, -1, 666666667, -3, 0}, + {-4, 666666667, -1, 999999999, -4, 666666668}, + {-4, 666666667, 0, 0, -4, 666666667}, + {-4, 666666667, 0, 1, -4, 666666666}, + {-4, 666666667, 0, 333333333, -4, 333333334}, + {-4, 666666667, 0, 666666666, -4, 1}, + {-4, 666666667, 1, 0, -5, 666666667}, + {-4, 666666667, 2, 0, -6, 666666667}, + {-4, 666666667, 3, 0, -7, 666666667}, + {-4, 666666667, 3, 333333333, -7, 333333334}, + + {-3, 0, -4, 666666667, 0, 333333333}, + {-3, 0, -3, 0, 0, 0}, + {-3, 0, -2, 0, -1, 0}, + {-3, 0, -1, 0, -2, 0}, + {-3, 0, -1, 333333334, -3, 666666666}, + {-3, 0, -1, 666666667, -3, 333333333}, + {-3, 0, -1, 999999999, -3, 1}, + {-3, 0, 0, 0, -3, 0}, + {-3, 0, 0, 1, -4, 999999999}, + {-3, 0, 0, 333333333, -4, 666666667}, + {-3, 0, 0, 666666666, -4, 333333334}, + {-3, 0, 1, 0, -4, 0}, + {-3, 0, 2, 0, -5, 0}, + {-3, 0, 3, 0, -6, 0}, + {-3, 0, 3, 333333333, -7, 666666667}, + + {-2, 0, -4, 666666667, 1, 333333333}, + {-2, 0, -3, 0, 1, 0}, + {-2, 0, -2, 0, 0, 0}, + {-2, 0, -1, 0, -1, 0}, + {-2, 0, -1, 333333334, -2, 666666666}, + {-2, 0, -1, 666666667, -2, 333333333}, + {-2, 0, -1, 999999999, -2, 1}, + {-2, 0, 0, 0, -2, 0}, + {-2, 0, 0, 1, -3, 999999999}, + {-2, 0, 0, 333333333, -3, 666666667}, + {-2, 0, 0, 666666666, -3, 333333334}, + {-2, 0, 1, 0, -3, 0}, + {-2, 0, 2, 0, -4, 0}, + {-2, 0, 3, 0, -5, 0}, + {-2, 0, 3, 333333333, -6, 666666667}, + + {-1, 0, -4, 666666667, 2, 333333333}, + {-1, 0, -3, 0, 2, 0}, + {-1, 0, -2, 0, 1, 0}, + {-1, 0, -1, 0, 0, 0}, + {-1, 0, -1, 333333334, -1, 666666666}, + {-1, 0, -1, 666666667, -1, 333333333}, + {-1, 0, -1, 999999999, -1, 1}, + {-1, 0, 0, 0, -1, 0}, + {-1, 0, 0, 1, -2, 999999999}, + {-1, 0, 0, 333333333, -2, 666666667}, + {-1, 0, 0, 666666666, -2, 333333334}, + {-1, 0, 1, 0, -2, 0}, + {-1, 0, 2, 0, -3, 0}, + {-1, 0, 3, 0, -4, 0}, + {-1, 0, 3, 333333333, -5, 666666667}, + + {-1, 666666667, -4, 666666667, 3, 0}, + {-1, 666666667, -3, 0, 2, 666666667}, + {-1, 666666667, -2, 0, 1, 666666667}, + {-1, 666666667, -1, 0, 0, 666666667}, + {-1, 666666667, -1, 333333334, 0, 333333333}, + {-1, 666666667, -1, 666666667, 0, 0}, + {-1, 666666667, -1, 999999999, -1, 666666668}, + {-1, 666666667, 0, 0, -1, 666666667}, + {-1, 666666667, 0, 1, -1, 666666666}, + {-1, 666666667, 0, 333333333, -1, 333333334}, + {-1, 666666667, 0, 666666666, -1, 1}, + {-1, 666666667, 1, 0, -2, 666666667}, + {-1, 666666667, 2, 0, -3, 666666667}, + {-1, 666666667, 3, 0, -4, 666666667}, + {-1, 666666667, 3, 333333333, -4, 333333334}, + + {0, 0, -4, 666666667, 3, 333333333}, + {0, 0, -3, 0, 3, 0}, + {0, 0, -2, 0, 2, 0}, + {0, 0, -1, 0, 1, 0}, + {0, 0, -1, 333333334, 0, 666666666}, + {0, 0, -1, 666666667, 0, 333333333}, + {0, 0, -1, 999999999, 0, 1}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 1, -1, 999999999}, + {0, 0, 0, 333333333, -1, 666666667}, + {0, 0, 0, 666666666, -1, 333333334}, + {0, 0, 1, 0, -1, 0}, + {0, 0, 2, 0, -2, 0}, + {0, 0, 3, 0, -3, 0}, + {0, 0, 3, 333333333, -4, 666666667}, + + {0, 333333333, -4, 666666667, 3, 666666666}, + {0, 333333333, -3, 0, 3, 333333333}, + {0, 333333333, -2, 0, 2, 333333333}, + {0, 333333333, -1, 0, 1, 333333333}, + {0, 333333333, -1, 333333334, 0, 999999999}, + {0, 333333333, -1, 666666667, 0, 666666666}, + {0, 333333333, -1, 999999999, 0, 333333334}, + {0, 333333333, 0, 0, 0, 333333333}, + {0, 333333333, 0, 1, 0, 333333332}, + {0, 333333333, 0, 333333333, 0, 0}, + {0, 333333333, 0, 666666666, -1, 666666667}, + {0, 333333333, 1, 0, -1, 333333333}, + {0, 333333333, 2, 0, -2, 333333333}, + {0, 333333333, 3, 0, -3, 333333333}, + {0, 333333333, 3, 333333333, -3, 0}, + + {1, 0, -4, 666666667, 4, 333333333}, + {1, 0, -3, 0, 4, 0}, + {1, 0, -2, 0, 3, 0}, + {1, 0, -1, 0, 2, 0}, + {1, 0, -1, 333333334, 1, 666666666}, + {1, 0, -1, 666666667, 1, 333333333}, + {1, 0, -1, 999999999, 1, 1}, + {1, 0, 0, 0, 1, 0}, + {1, 0, 0, 1, 0, 999999999}, + {1, 0, 0, 333333333, 0, 666666667}, + {1, 0, 0, 666666666, 0, 333333334}, + {1, 0, 1, 0, 0, 0}, + {1, 0, 2, 0, -1, 0}, + {1, 0, 3, 0, -2, 0}, + {1, 0, 3, 333333333, -3, 666666667}, + + {2, 0, -4, 666666667, 5, 333333333}, + {2, 0, -3, 0, 5, 0}, + {2, 0, -2, 0, 4, 0}, + {2, 0, -1, 0, 3, 0}, + {2, 0, -1, 333333334, 2, 666666666}, + {2, 0, -1, 666666667, 2, 333333333}, + {2, 0, -1, 999999999, 2, 1}, + {2, 0, 0, 0, 2, 0}, + {2, 0, 0, 1, 1, 999999999}, + {2, 0, 0, 333333333, 1, 666666667}, + {2, 0, 0, 666666666, 1, 333333334}, + {2, 0, 1, 0, 1, 0}, + {2, 0, 2, 0, 0, 0}, + {2, 0, 3, 0, -1, 0}, + {2, 0, 3, 333333333, -2, 666666667}, + + {3, 0, -4, 666666667, 6, 333333333}, + {3, 0, -3, 0, 6, 0}, + {3, 0, -2, 0, 5, 0}, + {3, 0, -1, 0, 4, 0}, + {3, 0, -1, 333333334, 3, 666666666}, + {3, 0, -1, 666666667, 3, 333333333}, + {3, 0, -1, 999999999, 3, 1}, + {3, 0, 0, 0, 3, 0}, + {3, 0, 0, 1, 2, 999999999}, + {3, 0, 0, 333333333, 2, 666666667}, + {3, 0, 0, 666666666, 2, 333333334}, + {3, 0, 1, 0, 2, 0}, + {3, 0, 2, 0, 1, 0}, + {3, 0, 3, 0, 0, 0}, + {3, 0, 3, 333333333, -1, 666666667}, + + {3, 333333333, -4, 666666667, 6, 666666666}, + {3, 333333333, -3, 0, 6, 333333333}, + {3, 333333333, -2, 0, 5, 333333333}, + {3, 333333333, -1, 0, 4, 333333333}, + {3, 333333333, -1, 333333334, 3, 999999999}, + {3, 333333333, -1, 666666667, 3, 666666666}, + {3, 333333333, -1, 999999999, 3, 333333334}, + {3, 333333333, 0, 0, 3, 333333333}, + {3, 333333333, 0, 1, 3, 333333332}, + {3, 333333333, 0, 333333333, 3, 0}, + {3, 333333333, 0, 666666666, 2, 666666667}, + {3, 333333333, 1, 0, 2, 333333333}, + {3, 333333333, 2, 0, 1, 333333333}, + {3, 333333333, 3, 0, 0, 333333333}, + {3, 333333333, 3, 333333333, 0, 0}, + + {MAX_SECOND - 1, 0, -1, 0, MAX_SECOND, 0}, + {MAX_SECOND - 1, 0, 0, -500, MAX_SECOND - 1, 500}, + {MAX_SECOND - 1, 0, 0, -1000000000, MAX_SECOND, 0}, + + {MAX_SECOND, 0, 1, 0, MAX_SECOND - 1, 0}, + {MAX_SECOND, 0, 0, 500, MAX_SECOND - 1, 999999500}, + {MAX_SECOND, 0, 0, 1000000000, MAX_SECOND - 1, 0}, + + {MAX_SECOND, 0, MAX_SECOND, 0, 0, 0}, + }; + } + + @Test(dataProvider="Minus") + public void minus_Duration(long seconds, int nanos, long otherSeconds, int otherNanos, long expectedSeconds, int expectedNanoOfSecond) { + Instant i = Instant.ofEpochSecond(seconds, nanos).minus(Duration.ofSeconds(otherSeconds, otherNanos)); + assertEquals(i.getEpochSecond(), expectedSeconds); + assertEquals(i.getNano(), expectedNanoOfSecond); + } + + @Test(expectedExceptions=DateTimeException.class) + public void minus_Duration_overflowTooSmall() { + Instant i = Instant.ofEpochSecond(MIN_SECOND); + i.minus(Duration.ofSeconds(0, 1)); + } + + @Test(expectedExceptions=DateTimeException.class) + public void minus_Duration_overflowTooBig() { + Instant i = Instant.ofEpochSecond(MAX_SECOND, 999999999); + i.minus(Duration.ofSeconds(-1, 999999999)); + } + + //----------------------------------------------------------------------- + @Test(dataProvider="Minus") + public void minus_longTemporalUnit(long seconds, int nanos, long otherSeconds, int otherNanos, long expectedSeconds, int expectedNanoOfSecond) { + Instant i = Instant.ofEpochSecond(seconds, nanos).minus(otherSeconds, SECONDS).minus(otherNanos, NANOS); + assertEquals(i.getEpochSecond(), expectedSeconds); + assertEquals(i.getNano(), expectedNanoOfSecond); + } + + @Test(expectedExceptions=DateTimeException.class) + public void minus_longTemporalUnit_overflowTooSmall() { + Instant i = Instant.ofEpochSecond(MIN_SECOND); + i.minus(1, NANOS); + } + + @Test(expectedExceptions=DateTimeException.class) + public void minus_longTemporalUnit_overflowTooBig() { + Instant i = Instant.ofEpochSecond(MAX_SECOND, 999999999); + i.minus(999999999, NANOS); + i.minus(-1, SECONDS); + } + + //----------------------------------------------------------------------- + @DataProvider(name="MinusSeconds") + Object[][] provider_minusSeconds_long() { + return new Object[][] { + {0, 0, 0, 0, 0}, + {0, 0, 1, -1, 0}, + {0, 0, -1, 1, 0}, + {0, 0, -MIN_SECOND, MIN_SECOND, 0}, + {1, 0, 0, 1, 0}, + {1, 0, 1, 0, 0}, + {1, 0, -1, 2, 0}, + {1, 0, -MIN_SECOND + 1, MIN_SECOND, 0}, + {1, 1, 0, 1, 1}, + {1, 1, 1, 0, 1}, + {1, 1, -1, 2, 1}, + {1, 1, -MIN_SECOND, MIN_SECOND + 1, 1}, + {1, 1, -MIN_SECOND + 1, MIN_SECOND, 1}, + {-1, 1, 0, -1, 1}, + {-1, 1, 1, -2, 1}, + {-1, 1, -1, 0, 1}, + {-1, 1, -MAX_SECOND, MAX_SECOND - 1, 1}, + {-1, 1, -(MAX_SECOND + 1), MAX_SECOND, 1}, + + {MIN_SECOND, 2, MIN_SECOND, 0, 2}, + {MIN_SECOND + 1, 2, MIN_SECOND, 1, 2}, + {MAX_SECOND - 1, 2, MAX_SECOND, -1, 2}, + {MAX_SECOND, 2, MAX_SECOND, 0, 2}, + }; + } + + @Test(dataProvider="MinusSeconds") + public void minusSeconds_long(long seconds, int nanos, long amount, long expectedSeconds, int expectedNanoOfSecond) { + Instant i = Instant.ofEpochSecond(seconds, nanos); + i = i.minusSeconds(amount); + assertEquals(i.getEpochSecond(), expectedSeconds); + assertEquals(i.getNano(), expectedNanoOfSecond); + } + + @Test(expectedExceptions = {ArithmeticException.class}) + public void minusSeconds_long_overflowTooBig() { + Instant i = Instant.ofEpochSecond(1, 0); + i.minusSeconds(Long.MIN_VALUE + 1); + } + + @Test(expectedExceptions = {ArithmeticException.class}) + public void minusSeconds_long_overflowTooSmall() { + Instant i = Instant.ofEpochSecond(-2, 0); + i.minusSeconds(Long.MAX_VALUE); + } + + //----------------------------------------------------------------------- + @DataProvider(name="MinusMillis") + Object[][] provider_minusMillis_long() { + return new Object[][] { + {0, 0, 0, 0, 0}, + {0, 0, 1, -1, 999000000}, + {0, 0, 999, -1, 1000000}, + {0, 0, 1000, -1, 0}, + {0, 0, 1001, -2, 999000000}, + {0, 0, 1999, -2, 1000000}, + {0, 0, 2000, -2, 0}, + {0, 0, -1, 0, 1000000}, + {0, 0, -999, 0, 999000000}, + {0, 0, -1000, 1, 0}, + {0, 0, -1001, 1, 1000000}, + {0, 0, -1999, 1, 999000000}, + + {0, 1, 0, 0, 1}, + {0, 1, 1, -1, 999000001}, + {0, 1, 998, -1, 2000001}, + {0, 1, 999, -1, 1000001}, + {0, 1, 1000, -1, 1}, + {0, 1, 1998, -2, 2000001}, + {0, 1, 1999, -2, 1000001}, + {0, 1, 2000, -2, 1}, + {0, 1, -1, 0, 1000001}, + {0, 1, -2, 0, 2000001}, + {0, 1, -1000, 1, 1}, + {0, 1, -1001, 1, 1000001}, + + {0, 1000000, 0, 0, 1000000}, + {0, 1000000, 1, 0, 0}, + {0, 1000000, 998, -1, 3000000}, + {0, 1000000, 999, -1, 2000000}, + {0, 1000000, 1000, -1, 1000000}, + {0, 1000000, 1998, -2, 3000000}, + {0, 1000000, 1999, -2, 2000000}, + {0, 1000000, 2000, -2, 1000000}, + {0, 1000000, -1, 0, 2000000}, + {0, 1000000, -2, 0, 3000000}, + {0, 1000000, -999, 1, 0}, + {0, 1000000, -1000, 1, 1000000}, + {0, 1000000, -1001, 1, 2000000}, + {0, 1000000, -1002, 1, 3000000}, + + {0, 999999999, 0, 0, 999999999}, + {0, 999999999, 1, 0, 998999999}, + {0, 999999999, 999, 0, 999999}, + {0, 999999999, 1000, -1, 999999999}, + {0, 999999999, 1001, -1, 998999999}, + {0, 999999999, -1, 1, 999999}, + {0, 999999999, -1000, 1, 999999999}, + {0, 999999999, -1001, 2, 999999}, + + {0, 0, Long.MAX_VALUE, -(Long.MAX_VALUE / 1000) - 1, (int) -(Long.MAX_VALUE % 1000) * 1000000 + 1000000000}, + {0, 0, Long.MIN_VALUE, -(Long.MIN_VALUE / 1000), (int) -(Long.MIN_VALUE % 1000) * 1000000}, + }; + } + + @Test(dataProvider="MinusMillis") + public void minusMillis_long(long seconds, int nanos, long amount, long expectedSeconds, int expectedNanoOfSecond) { + Instant i = Instant.ofEpochSecond(seconds, nanos); + i = i.minusMillis(amount); + assertEquals(i.getEpochSecond(), expectedSeconds); + assertEquals(i.getNano(), expectedNanoOfSecond); + } + + @Test(dataProvider="MinusMillis") + public void minusMillis_long_oneMore(long seconds, int nanos, long amount, long expectedSeconds, int expectedNanoOfSecond) { + Instant i = Instant.ofEpochSecond(seconds + 1, nanos); + i = i.minusMillis(amount); + assertEquals(i.getEpochSecond(), expectedSeconds + 1); + assertEquals(i.getNano(), expectedNanoOfSecond); + } + + @Test(dataProvider="MinusMillis") + public void minusMillis_long_minusOneLess(long seconds, int nanos, long amount, long expectedSeconds, int expectedNanoOfSecond) { + Instant i = Instant.ofEpochSecond(seconds - 1, nanos); + i = i.minusMillis(amount); + assertEquals(i.getEpochSecond(), expectedSeconds - 1); + assertEquals(i.getNano(), expectedNanoOfSecond); + } + + @Test + public void minusMillis_long_max() { + Instant i = Instant.ofEpochSecond(MAX_SECOND, 998999999); + i = i.minusMillis(-1); + assertEquals(i.getEpochSecond(), MAX_SECOND); + assertEquals(i.getNano(), 999999999); + } + + @Test(expectedExceptions=DateTimeException.class) + public void minusMillis_long_overflowTooBig() { + Instant i = Instant.ofEpochSecond(MAX_SECOND, 999000000); + i.minusMillis(-1); + } + + @Test + public void minusMillis_long_min() { + Instant i = Instant.ofEpochSecond(MIN_SECOND, 1000000); + i = i.minusMillis(1); + assertEquals(i.getEpochSecond(), MIN_SECOND); + assertEquals(i.getNano(), 0); + } + + @Test(expectedExceptions=DateTimeException.class) + public void minusMillis_long_overflowTooSmall() { + Instant i = Instant.ofEpochSecond(MIN_SECOND, 0); + i.minusMillis(1); + } + + //----------------------------------------------------------------------- + @DataProvider(name="MinusNanos") + Object[][] provider_minusNanos_long() { + return new Object[][] { + {0, 0, 0, 0, 0}, + {0, 0, 1, -1, 999999999}, + {0, 0, 999999999, -1, 1}, + {0, 0, 1000000000, -1, 0}, + {0, 0, 1000000001, -2, 999999999}, + {0, 0, 1999999999, -2, 1}, + {0, 0, 2000000000, -2, 0}, + {0, 0, -1, 0, 1}, + {0, 0, -999999999, 0, 999999999}, + {0, 0, -1000000000, 1, 0}, + {0, 0, -1000000001, 1, 1}, + {0, 0, -1999999999, 1, 999999999}, + + {1, 0, 0, 1, 0}, + {1, 0, 1, 0, 999999999}, + {1, 0, 999999999, 0, 1}, + {1, 0, 1000000000, 0, 0}, + {1, 0, 1000000001, -1, 999999999}, + {1, 0, 1999999999, -1, 1}, + {1, 0, 2000000000, -1, 0}, + {1, 0, -1, 1, 1}, + {1, 0, -999999999, 1, 999999999}, + {1, 0, -1000000000, 2, 0}, + {1, 0, -1000000001, 2, 1}, + {1, 0, -1999999999, 2, 999999999}, + + {-1, 0, 0, -1, 0}, + {-1, 0, 1, -2, 999999999}, + {-1, 0, 999999999, -2, 1}, + {-1, 0, 1000000000, -2, 0}, + {-1, 0, 1000000001, -3, 999999999}, + {-1, 0, 1999999999, -3, 1}, + {-1, 0, 2000000000, -3, 0}, + {-1, 0, -1, -1, 1}, + {-1, 0, -999999999, -1, 999999999}, + {-1, 0, -1000000000, 0, 0}, + {-1, 0, -1000000001, 0, 1}, + {-1, 0, -1999999999, 0, 999999999}, + + {1, 1, 0, 1, 1}, + {1, 1, 1, 1, 0}, + {1, 1, 999999998, 0, 3}, + {1, 1, 999999999, 0, 2}, + {1, 1, 1000000000, 0, 1}, + {1, 1, 1999999998, -1, 3}, + {1, 1, 1999999999, -1, 2}, + {1, 1, 2000000000, -1, 1}, + {1, 1, -1, 1, 2}, + {1, 1, -2, 1, 3}, + {1, 1, -1000000000, 2, 1}, + {1, 1, -1000000001, 2, 2}, + {1, 1, -1000000002, 2, 3}, + {1, 1, -2000000000, 3, 1}, + + {1, 999999999, 0, 1, 999999999}, + {1, 999999999, 1, 1, 999999998}, + {1, 999999999, 999999999, 1, 0}, + {1, 999999999, 1000000000, 0, 999999999}, + {1, 999999999, 1000000001, 0, 999999998}, + {1, 999999999, -1, 2, 0}, + {1, 999999999, -1000000000, 2, 999999999}, + {1, 999999999, -1000000001, 3, 0}, + {1, 999999999, -1999999999, 3, 999999998}, + {1, 999999999, -2000000000, 3, 999999999}, + + {MAX_SECOND, 0, -999999999, MAX_SECOND, 999999999}, + {MAX_SECOND - 1, 0, -1999999999, MAX_SECOND, 999999999}, + {MIN_SECOND, 1, 1, MIN_SECOND, 0}, + {MIN_SECOND + 1, 1, 1000000001, MIN_SECOND, 0}, + + {0, 0, Long.MAX_VALUE, -(Long.MAX_VALUE / 1000000000) - 1, (int) -(Long.MAX_VALUE % 1000000000) + 1000000000}, + {0, 0, Long.MIN_VALUE, -(Long.MIN_VALUE / 1000000000), (int) -(Long.MIN_VALUE % 1000000000)}, + }; + } + + @Test(dataProvider="MinusNanos") + public void minusNanos_long(long seconds, int nanos, long amount, long expectedSeconds, int expectedNanoOfSecond) { + Instant i = Instant.ofEpochSecond(seconds, nanos); + i = i.minusNanos(amount); + assertEquals(i.getEpochSecond(), expectedSeconds); + assertEquals(i.getNano(), expectedNanoOfSecond); + } + + @Test(expectedExceptions=DateTimeException.class) + public void minusNanos_long_overflowTooBig() { + Instant i = Instant.ofEpochSecond(MAX_SECOND, 999999999); + i.minusNanos(-1); + } + + @Test(expectedExceptions=DateTimeException.class) + public void minusNanos_long_overflowTooSmall() { + Instant i = Instant.ofEpochSecond(MIN_SECOND, 0); + i.minusNanos(1); + } + + //----------------------------------------------------------------------- + // toEpochMilli() + //----------------------------------------------------------------------- + @Test + public void test_toEpochMilli() { + assertEquals(Instant.ofEpochSecond(1L, 1000000).toEpochMilli(), 1001L); + assertEquals(Instant.ofEpochSecond(1L, 2000000).toEpochMilli(), 1002L); + assertEquals(Instant.ofEpochSecond(1L, 567).toEpochMilli(), 1000L); + assertEquals(Instant.ofEpochSecond(Long.MAX_VALUE / 1000).toEpochMilli(), (Long.MAX_VALUE / 1000) * 1000); + assertEquals(Instant.ofEpochSecond(Long.MIN_VALUE / 1000).toEpochMilli(), (Long.MIN_VALUE / 1000) * 1000); + assertEquals(Instant.ofEpochSecond(0L, -1000000).toEpochMilli(), -1L); + assertEquals(Instant.ofEpochSecond(0L, 1000000).toEpochMilli(), 1); + assertEquals(Instant.ofEpochSecond(0L, 999999).toEpochMilli(), 0); + assertEquals(Instant.ofEpochSecond(0L, 1).toEpochMilli(), 0); + assertEquals(Instant.ofEpochSecond(0L, 0).toEpochMilli(), 0); + assertEquals(Instant.ofEpochSecond(0L, -1).toEpochMilli(), -1L); + assertEquals(Instant.ofEpochSecond(0L, -999999).toEpochMilli(), -1L); + assertEquals(Instant.ofEpochSecond(0L, -1000000).toEpochMilli(), -1L); + assertEquals(Instant.ofEpochSecond(0L, -1000001).toEpochMilli(), -2L); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_toEpochMilli_tooBig() { + Instant.ofEpochSecond(Long.MAX_VALUE / 1000 + 1).toEpochMilli(); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_toEpochMilli_tooSmall() { + Instant.ofEpochSecond(Long.MIN_VALUE / 1000 - 1).toEpochMilli(); + } + + //----------------------------------------------------------------------- + // compareTo() + //----------------------------------------------------------------------- + @Test + public void test_comparisons() { + doTest_comparisons_Instant( + Instant.ofEpochSecond(-2L, 0), + Instant.ofEpochSecond(-2L, 999999998), + Instant.ofEpochSecond(-2L, 999999999), + Instant.ofEpochSecond(-1L, 0), + Instant.ofEpochSecond(-1L, 1), + Instant.ofEpochSecond(-1L, 999999998), + Instant.ofEpochSecond(-1L, 999999999), + Instant.ofEpochSecond(0L, 0), + Instant.ofEpochSecond(0L, 1), + Instant.ofEpochSecond(0L, 2), + Instant.ofEpochSecond(0L, 999999999), + Instant.ofEpochSecond(1L, 0), + Instant.ofEpochSecond(2L, 0) + ); + } + + void doTest_comparisons_Instant(Instant... instants) { + for (int i = 0; i < instants.length; i++) { + Instant a = instants[i]; + for (int j = 0; j < instants.length; j++) { + Instant b = instants[j]; + if (i < j) { + assertEquals(a.compareTo(b) < 0, true, a + " <=> " + b); + assertEquals(a.isBefore(b), true, a + " <=> " + b); + assertEquals(a.isAfter(b), false, a + " <=> " + b); + assertEquals(a.equals(b), false, a + " <=> " + b); + } else if (i > j) { + assertEquals(a.compareTo(b) > 0, true, a + " <=> " + b); + assertEquals(a.isBefore(b), false, a + " <=> " + b); + assertEquals(a.isAfter(b), true, a + " <=> " + b); + assertEquals(a.equals(b), false, a + " <=> " + b); + } else { + assertEquals(a.compareTo(b), 0, a + " <=> " + b); + assertEquals(a.isBefore(b), false, a + " <=> " + b); + assertEquals(a.isAfter(b), false, a + " <=> " + b); + assertEquals(a.equals(b), true, a + " <=> " + b); + } + } + } + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_compareTo_ObjectNull() { + Instant a = Instant.ofEpochSecond(0L, 0); + a.compareTo(null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_isBefore_ObjectNull() { + Instant a = Instant.ofEpochSecond(0L, 0); + a.isBefore(null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_isAfter_ObjectNull() { + Instant a = Instant.ofEpochSecond(0L, 0); + a.isAfter(null); + } + + @Test(expectedExceptions=ClassCastException.class) + @SuppressWarnings({"unchecked", "rawtypes"}) + public void compareToNonInstant() { + Comparable c = Instant.ofEpochSecond(0L); + c.compareTo(new Object()); + } + + //----------------------------------------------------------------------- + // equals() + //----------------------------------------------------------------------- + @Test + public void test_equals() { + Instant test5a = Instant.ofEpochSecond(5L, 20); + Instant test5b = Instant.ofEpochSecond(5L, 20); + Instant test5n = Instant.ofEpochSecond(5L, 30); + Instant test6 = Instant.ofEpochSecond(6L, 20); + + assertEquals(test5a.equals(test5a), true); + assertEquals(test5a.equals(test5b), true); + assertEquals(test5a.equals(test5n), false); + assertEquals(test5a.equals(test6), false); + + assertEquals(test5b.equals(test5a), true); + assertEquals(test5b.equals(test5b), true); + assertEquals(test5b.equals(test5n), false); + assertEquals(test5b.equals(test6), false); + + assertEquals(test5n.equals(test5a), false); + assertEquals(test5n.equals(test5b), false); + assertEquals(test5n.equals(test5n), true); + assertEquals(test5n.equals(test6), false); + + assertEquals(test6.equals(test5a), false); + assertEquals(test6.equals(test5b), false); + assertEquals(test6.equals(test5n), false); + assertEquals(test6.equals(test6), true); + } + + @Test + public void test_equals_null() { + Instant test5 = Instant.ofEpochSecond(5L, 20); + assertEquals(test5.equals(null), false); + } + + @Test + public void test_equals_otherClass() { + Instant test5 = Instant.ofEpochSecond(5L, 20); + assertEquals(test5.equals(""), false); + } + + //----------------------------------------------------------------------- + // hashCode() + //----------------------------------------------------------------------- + @Test + public void test_hashCode() { + Instant test5a = Instant.ofEpochSecond(5L, 20); + Instant test5b = Instant.ofEpochSecond(5L, 20); + Instant test5n = Instant.ofEpochSecond(5L, 30); + Instant test6 = Instant.ofEpochSecond(6L, 20); + + assertEquals(test5a.hashCode() == test5a.hashCode(), true); + assertEquals(test5a.hashCode() == test5b.hashCode(), true); + assertEquals(test5b.hashCode() == test5b.hashCode(), true); + + assertEquals(test5a.hashCode() == test5n.hashCode(), false); + assertEquals(test5a.hashCode() == test6.hashCode(), false); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @DataProvider(name="toStringParse") + Object[][] data_toString() { + return new Object[][] { + {Instant.ofEpochSecond(65L, 567), "1970-01-01T00:01:05.000000567Z"}, + {Instant.ofEpochSecond(1, 0), "1970-01-01T00:00:01Z"}, + {Instant.ofEpochSecond(60, 0), "1970-01-01T00:01Z"}, + {Instant.ofEpochSecond(3600, 0), "1970-01-01T01:00Z"}, + {Instant.ofEpochSecond(-1, 0), "1969-12-31T23:59:59Z"}, + + {LocalDateTime.of(0, 1, 2, 0, 0).toInstant(ZoneOffset.UTC), "0000-01-02T00:00Z"}, + {LocalDateTime.of(0, 1, 1, 12, 30).toInstant(ZoneOffset.UTC), "0000-01-01T12:30Z"}, + {LocalDateTime.of(0, 1, 1, 0, 0, 0, 1).toInstant(ZoneOffset.UTC), "0000-01-01T00:00:00.000000001Z"}, + {LocalDateTime.of(0, 1, 1, 0, 0).toInstant(ZoneOffset.UTC), "0000-01-01T00:00Z"}, + + {LocalDateTime.of(-1, 12, 31, 23, 59, 59, 999_999_999).toInstant(ZoneOffset.UTC), "-0001-12-31T23:59:59.999999999Z"}, + {LocalDateTime.of(-1, 12, 31, 12, 30).toInstant(ZoneOffset.UTC), "-0001-12-31T12:30Z"}, + {LocalDateTime.of(-1, 12, 30, 12, 30).toInstant(ZoneOffset.UTC), "-0001-12-30T12:30Z"}, + + {LocalDateTime.of(-9999, 1, 2, 12, 30).toInstant(ZoneOffset.UTC), "-9999-01-02T12:30Z"}, + {LocalDateTime.of(-9999, 1, 1, 12, 30).toInstant(ZoneOffset.UTC), "-9999-01-01T12:30Z"}, + {LocalDateTime.of(-9999, 1, 1, 0, 0).toInstant(ZoneOffset.UTC), "-9999-01-01T00:00Z"}, + + {LocalDateTime.of(-10000, 12, 31, 23, 59, 59, 999_999_999).toInstant(ZoneOffset.UTC), "-10000-12-31T23:59:59.999999999Z"}, + {LocalDateTime.of(-10000, 12, 31, 12, 30).toInstant(ZoneOffset.UTC), "-10000-12-31T12:30Z"}, + {LocalDateTime.of(-10000, 12, 30, 12, 30).toInstant(ZoneOffset.UTC), "-10000-12-30T12:30Z"}, + {LocalDateTime.of(-15000, 12, 31, 12, 30).toInstant(ZoneOffset.UTC), "-15000-12-31T12:30Z"}, + + {LocalDateTime.of(-19999, 1, 2, 12, 30).toInstant(ZoneOffset.UTC), "-19999-01-02T12:30Z"}, + {LocalDateTime.of(-19999, 1, 1, 12, 30).toInstant(ZoneOffset.UTC), "-19999-01-01T12:30Z"}, + {LocalDateTime.of(-19999, 1, 1, 0, 0).toInstant(ZoneOffset.UTC), "-19999-01-01T00:00Z"}, + + {LocalDateTime.of(-20000, 12, 31, 23, 59, 59, 999_999_999).toInstant(ZoneOffset.UTC), "-20000-12-31T23:59:59.999999999Z"}, + {LocalDateTime.of(-20000, 12, 31, 12, 30).toInstant(ZoneOffset.UTC), "-20000-12-31T12:30Z"}, + {LocalDateTime.of(-20000, 12, 30, 12, 30).toInstant(ZoneOffset.UTC), "-20000-12-30T12:30Z"}, + {LocalDateTime.of(-25000, 12, 31, 12, 30).toInstant(ZoneOffset.UTC), "-25000-12-31T12:30Z"}, + + {LocalDateTime.of(9999, 12, 30, 12, 30).toInstant(ZoneOffset.UTC), "9999-12-30T12:30Z"}, + {LocalDateTime.of(9999, 12, 31, 12, 30).toInstant(ZoneOffset.UTC), "9999-12-31T12:30Z"}, + {LocalDateTime.of(9999, 12, 31, 23, 59, 59, 999_999_999).toInstant(ZoneOffset.UTC), "9999-12-31T23:59:59.999999999Z"}, + + {LocalDateTime.of(10000, 1, 1, 0, 0).toInstant(ZoneOffset.UTC), "+10000-01-01T00:00Z"}, + {LocalDateTime.of(10000, 1, 1, 12, 30).toInstant(ZoneOffset.UTC), "+10000-01-01T12:30Z"}, + {LocalDateTime.of(10000, 1, 2, 12, 30).toInstant(ZoneOffset.UTC), "+10000-01-02T12:30Z"}, + {LocalDateTime.of(15000, 12, 31, 12, 30).toInstant(ZoneOffset.UTC), "+15000-12-31T12:30Z"}, + + {LocalDateTime.of(19999, 12, 30, 12, 30).toInstant(ZoneOffset.UTC), "+19999-12-30T12:30Z"}, + {LocalDateTime.of(19999, 12, 31, 12, 30).toInstant(ZoneOffset.UTC), "+19999-12-31T12:30Z"}, + {LocalDateTime.of(19999, 12, 31, 23, 59, 59, 999_999_999).toInstant(ZoneOffset.UTC), "+19999-12-31T23:59:59.999999999Z"}, + + {LocalDateTime.of(20000, 1, 1, 0, 0).toInstant(ZoneOffset.UTC), "+20000-01-01T00:00Z"}, + {LocalDateTime.of(20000, 1, 1, 12, 30).toInstant(ZoneOffset.UTC), "+20000-01-01T12:30Z"}, + {LocalDateTime.of(20000, 1, 2, 12, 30).toInstant(ZoneOffset.UTC), "+20000-01-02T12:30Z"}, + {LocalDateTime.of(25000, 12, 31, 12, 30).toInstant(ZoneOffset.UTC), "+25000-12-31T12:30Z"}, + + {LocalDateTime.of(-999_999_999, 1, 1, 12, 30).toInstant(ZoneOffset.UTC).minus(1, DAYS), "-1000000000-12-31T12:30Z"}, + {LocalDateTime.of(999_999_999, 12, 31, 12, 30).toInstant(ZoneOffset.UTC).plus(1, DAYS), "+1000000000-01-01T12:30Z"}, + + {Instant.MIN, "-1000000000-01-01T00:00Z"}, + {Instant.MAX, "+1000000000-12-31T23:59:59.999999999Z"}, + }; + } + + @Test(dataProvider="toStringParse") + public void test_toString(Instant instant, String expected) { + assertEquals(instant.toString(), expected); + } + + @Test(dataProvider="toStringParse") + public void test_parse(Instant instant, String text) { + assertEquals(Instant.parse(text), instant); + } + + @Test(dataProvider="toStringParse") + public void test_parseLowercase(Instant instant, String text) { + assertEquals(Instant.parse(text.toLowerCase(Locale.ENGLISH)), instant); + } + +} diff --git a/jdk/test/java/time/tck/java/time/TCKLocalDate.java b/jdk/test/java/time/tck/java/time/TCKLocalDate.java new file mode 100644 index 00000000000..721da56d36b --- /dev/null +++ b/jdk/test/java/time/tck/java/time/TCKLocalDate.java @@ -0,0 +1,2018 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time; + +import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; +import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; +import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; +import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.DAY_OF_WEEK; +import static java.time.temporal.ChronoField.DAY_OF_YEAR; +import static java.time.temporal.ChronoField.EPOCH_DAY; +import static java.time.temporal.ChronoField.EPOCH_MONTH; +import static java.time.temporal.ChronoField.ERA; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.YEAR; +import static java.time.temporal.ChronoField.YEAR_OF_ERA; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import java.time.Clock; +import java.time.DateTimeException; +import java.time.DayOfWeek; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Month; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatters; +import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoUnit; +import java.time.temporal.ISOChrono; +import java.time.temporal.JulianFields; +import java.time.temporal.OffsetDate; +import java.time.temporal.Queries; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalAdjuster; +import java.time.temporal.TemporalField; +import java.time.temporal.TemporalUnit; +import java.time.temporal.Year; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import test.java.time.MockSimplePeriod; +import test.java.time.temporal.MockFieldNoValue; + +/** + * Test LocalDate. + */ +@Test +public class TCKLocalDate extends AbstractDateTimeTest { + + private static final ZoneOffset OFFSET_PONE = ZoneOffset.ofHours(1); + private static final ZoneOffset OFFSET_PTWO = ZoneOffset.ofHours(2); + private static final ZoneId ZONE_PARIS = ZoneId.of("Europe/Paris"); + private static final ZoneId ZONE_GAZA = ZoneId.of("Asia/Gaza"); + + private LocalDate TEST_2007_07_15; + private long MAX_VALID_EPOCHDAYS; + private long MIN_VALID_EPOCHDAYS; + private LocalDate MAX_DATE; + private LocalDate MIN_DATE; + private Instant MAX_INSTANT; + private Instant MIN_INSTANT; + + @BeforeMethod(groups={"tck", "implementation"}) + public void setUp() { + TEST_2007_07_15 = LocalDate.of(2007, 7, 15); + + LocalDate max = LocalDate.MAX; + LocalDate min = LocalDate.MIN; + MAX_VALID_EPOCHDAYS = max.toEpochDay(); + MIN_VALID_EPOCHDAYS = min.toEpochDay(); + MAX_DATE = max; + MIN_DATE = min; + MAX_INSTANT = max.atStartOfDay(ZoneOffset.UTC).toInstant(); + MIN_INSTANT = min.atStartOfDay(ZoneOffset.UTC).toInstant(); + } + + //----------------------------------------------------------------------- + @Override + protected List samples() { + TemporalAccessor[] array = {TEST_2007_07_15, LocalDate.MAX, LocalDate.MIN, }; + return Arrays.asList(array); + } + + @Override + protected List validFields() { + TemporalField[] array = { + DAY_OF_WEEK, + ALIGNED_DAY_OF_WEEK_IN_MONTH, + ALIGNED_DAY_OF_WEEK_IN_YEAR, + DAY_OF_MONTH, + DAY_OF_YEAR, + EPOCH_DAY, + ALIGNED_WEEK_OF_MONTH, + ALIGNED_WEEK_OF_YEAR, + MONTH_OF_YEAR, + EPOCH_MONTH, + YEAR_OF_ERA, + YEAR, + ERA, + JulianFields.JULIAN_DAY, + JulianFields.MODIFIED_JULIAN_DAY, + JulianFields.RATA_DIE, + }; + return Arrays.asList(array); + } + + @Override + protected List invalidFields() { + List list = new ArrayList<>(Arrays.asList(ChronoField.values())); + list.removeAll(validFields()); + return list; + } + + + //----------------------------------------------------------------------- + @Test + public void test_serialization() throws Exception { + assertSerializable(TEST_2007_07_15); + assertSerializable(LocalDate.MIN); + assertSerializable(LocalDate.MAX); + } + + @Test + public void test_serialization_format() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baos) ) { + dos.writeByte(3); + dos.writeInt(2012); + dos.writeByte(9); + dos.writeByte(16); + } + byte[] bytes = baos.toByteArray(); + assertSerializedBySer(LocalDate.of(2012, 9, 16), bytes); + } + + //----------------------------------------------------------------------- + private void check(LocalDate test, int y, int m, int d) { + assertEquals(test.getYear(), y); + assertEquals(test.getMonth().getValue(), m); + assertEquals(test.getDayOfMonth(), d); + assertEquals(test, test); + assertEquals(test.hashCode(), test.hashCode()); + assertEquals(LocalDate.of(y, m, d), test); + } + + //----------------------------------------------------------------------- + // constants + //----------------------------------------------------------------------- + @Test + public void constant_MIN() { + check(LocalDate.MIN, Year.MIN_VALUE, 1, 1); + } + + @Test + public void constant_MAX() { + check(LocalDate.MAX, Year.MAX_VALUE, 12, 31); + } + + //----------------------------------------------------------------------- + // now() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void now() { + LocalDate expected = LocalDate.now(Clock.systemDefaultZone()); + LocalDate test = LocalDate.now(); + for (int i = 0; i < 100; i++) { + if (expected.equals(test)) { + return; + } + expected = LocalDate.now(Clock.systemDefaultZone()); + test = LocalDate.now(); + } + assertEquals(test, expected); + } + + //----------------------------------------------------------------------- + // now(ZoneId) + //----------------------------------------------------------------------- + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void now_ZoneId_nullZoneId() { + LocalDate.now((ZoneId) null); + } + + @Test(groups={"tck"}) + public void now_ZoneId() { + ZoneId zone = ZoneId.of("UTC+01:02:03"); + LocalDate expected = LocalDate.now(Clock.system(zone)); + LocalDate test = LocalDate.now(zone); + for (int i = 0; i < 100; i++) { + if (expected.equals(test)) { + return; + } + expected = LocalDate.now(Clock.system(zone)); + test = LocalDate.now(zone); + } + assertEquals(test, expected); + } + + //----------------------------------------------------------------------- + // now(Clock) + //----------------------------------------------------------------------- + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void now_Clock_nullClock() { + LocalDate.now((Clock) null); + } + + @Test(groups={"tck"}) + public void now_Clock_allSecsInDay_utc() { + for (int i = 0; i < (2 * 24 * 60 * 60); i++) { + Instant instant = Instant.ofEpochSecond(i); + Clock clock = Clock.fixed(instant, ZoneOffset.UTC); + LocalDate test = LocalDate.now(clock); + assertEquals(test.getYear(), 1970); + assertEquals(test.getMonth(), Month.JANUARY); + assertEquals(test.getDayOfMonth(), (i < 24 * 60 * 60 ? 1 : 2)); + } + } + + @Test(groups={"tck"}) + public void now_Clock_allSecsInDay_offset() { + for (int i = 0; i < (2 * 24 * 60 * 60); i++) { + Instant instant = Instant.ofEpochSecond(i); + Clock clock = Clock.fixed(instant.minusSeconds(OFFSET_PONE.getTotalSeconds()), OFFSET_PONE); + LocalDate test = LocalDate.now(clock); + assertEquals(test.getYear(), 1970); + assertEquals(test.getMonth(), Month.JANUARY); + assertEquals(test.getDayOfMonth(), (i < 24 * 60 * 60) ? 1 : 2); + } + } + + @Test(groups={"tck"}) + public void now_Clock_allSecsInDay_beforeEpoch() { + for (int i =-1; i >= -(2 * 24 * 60 * 60); i--) { + Instant instant = Instant.ofEpochSecond(i); + Clock clock = Clock.fixed(instant, ZoneOffset.UTC); + LocalDate test = LocalDate.now(clock); + assertEquals(test.getYear(), 1969); + assertEquals(test.getMonth(), Month.DECEMBER); + assertEquals(test.getDayOfMonth(), (i >= -24 * 60 * 60 ? 31 : 30)); + } + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void now_Clock_maxYear() { + Clock clock = Clock.fixed(MAX_INSTANT, ZoneOffset.UTC); + LocalDate test = LocalDate.now(clock); + assertEquals(test, MAX_DATE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void now_Clock_tooBig() { + Clock clock = Clock.fixed(MAX_INSTANT.plusSeconds(24 * 60 * 60), ZoneOffset.UTC); + LocalDate.now(clock); + } + + @Test(groups={"tck"}) + public void now_Clock_minYear() { + Clock clock = Clock.fixed(MIN_INSTANT, ZoneOffset.UTC); + LocalDate test = LocalDate.now(clock); + assertEquals(test, MIN_DATE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void now_Clock_tooLow() { + Clock clock = Clock.fixed(MIN_INSTANT.minusNanos(1), ZoneOffset.UTC); + LocalDate.now(clock); + } + + //----------------------------------------------------------------------- + // of() factories + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_of_intsMonth() { + assertEquals(TEST_2007_07_15, LocalDate.of(2007, Month.JULY, 15)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_intsMonth_29febNonLeap() { + LocalDate.of(2007, Month.FEBRUARY, 29); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_intsMonth_31apr() { + LocalDate.of(2007, Month.APRIL, 31); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_intsMonth_dayTooLow() { + LocalDate.of(2007, Month.JANUARY, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_intsMonth_dayTooHigh() { + LocalDate.of(2007, Month.JANUARY, 32); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_of_intsMonth_nullMonth() { + LocalDate.of(2007, null, 30); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_intsMonth_yearTooLow() { + LocalDate.of(Integer.MIN_VALUE, Month.JANUARY, 1); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_of_ints() { + check(TEST_2007_07_15, 2007, 7, 15); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_ints_29febNonLeap() { + LocalDate.of(2007, 2, 29); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_ints_31apr() { + LocalDate.of(2007, 4, 31); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_ints_dayTooLow() { + LocalDate.of(2007, 1, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_ints_dayTooHigh() { + LocalDate.of(2007, 1, 32); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_ints_monthTooLow() { + LocalDate.of(2007, 0, 1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_ints_monthTooHigh() { + LocalDate.of(2007, 13, 1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_ints_yearTooLow() { + LocalDate.of(Integer.MIN_VALUE, 1, 1); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_ofYearDay_ints_nonLeap() { + LocalDate date = LocalDate.of(2007, 1, 1); + for (int i = 1; i < 365; i++) { + assertEquals(LocalDate.ofYearDay(2007, i), date); + date = next(date); + } + } + + @Test(groups={"tck"}) + public void factory_ofYearDay_ints_leap() { + LocalDate date = LocalDate.of(2008, 1, 1); + for (int i = 1; i < 366; i++) { + assertEquals(LocalDate.ofYearDay(2008, i), date); + date = next(date); + } + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofYearDay_ints_366nonLeap() { + LocalDate.ofYearDay(2007, 366); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofYearDay_ints_dayTooLow() { + LocalDate.ofYearDay(2007, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofYearDay_ints_dayTooHigh() { + LocalDate.ofYearDay(2007, 367); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofYearDay_ints_yearTooLow() { + LocalDate.ofYearDay(Integer.MIN_VALUE, 1); + } + + //----------------------------------------------------------------------- + // Since plusDays/minusDays actually depends on MJDays, it cannot be used for testing + private LocalDate next(LocalDate date) { + int newDayOfMonth = date.getDayOfMonth() + 1; + if (newDayOfMonth <= date.getMonth().length(isIsoLeap(date.getYear()))) { + return date.withDayOfMonth(newDayOfMonth); + } + date = date.withDayOfMonth(1); + if (date.getMonth() == Month.DECEMBER) { + date = date.withYear(date.getYear() + 1); + } + return date.with(date.getMonth().plus(1)); + } + + private LocalDate previous(LocalDate date) { + int newDayOfMonth = date.getDayOfMonth() - 1; + if (newDayOfMonth > 0) { + return date.withDayOfMonth(newDayOfMonth); + } + date = date.with(date.getMonth().minus(1)); + if (date.getMonth() == Month.DECEMBER) { + date = date.withYear(date.getYear() - 1); + } + return date.withDayOfMonth(date.getMonth().length(isIsoLeap(date.getYear()))); + } + + //----------------------------------------------------------------------- + // ofEpochDay() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_ofEpochDay() { + long date_0000_01_01 = -678941 - 40587; + assertEquals(LocalDate.ofEpochDay(0), LocalDate.of(1970, 1, 1)); + assertEquals(LocalDate.ofEpochDay(date_0000_01_01), LocalDate.of(0, 1, 1)); + assertEquals(LocalDate.ofEpochDay(date_0000_01_01 - 1), LocalDate.of(-1, 12, 31)); + assertEquals(LocalDate.ofEpochDay(MAX_VALID_EPOCHDAYS), LocalDate.of(Year.MAX_VALUE, 12, 31)); + assertEquals(LocalDate.ofEpochDay(MIN_VALID_EPOCHDAYS), LocalDate.of(Year.MIN_VALUE, 1, 1)); + + LocalDate test = LocalDate.of(0, 1, 1); + for (long i = date_0000_01_01; i < 700000; i++) { + assertEquals(LocalDate.ofEpochDay(i), test); + test = next(test); + } + test = LocalDate.of(0, 1, 1); + for (long i = date_0000_01_01; i > -2000000; i--) { + assertEquals(LocalDate.ofEpochDay(i), test); + test = previous(test); + } + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofEpochDay_aboveMax() { + LocalDate.ofEpochDay(MAX_VALID_EPOCHDAYS + 1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofEpochDay_belowMin() { + LocalDate.ofEpochDay(MIN_VALID_EPOCHDAYS - 1); + } + + //----------------------------------------------------------------------- + // from() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_from_TemporalAccessor() { + assertEquals(LocalDate.from(LocalDate.of(2007, 7, 15)), LocalDate.of(2007, 7, 15)); + assertEquals(LocalDate.from(LocalDateTime.of(2007, 7, 15, 12, 30)), LocalDate.of(2007, 7, 15)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_from_TemporalAccessor_invalid_noDerive() { + LocalDate.from(LocalTime.of(12, 30)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_from_TemporalAccessor_null() { + LocalDate.from((TemporalAccessor) null); + } + + //----------------------------------------------------------------------- + // parse() + //----------------------------------------------------------------------- + @Test(dataProvider="sampleToString", groups={"tck"}) + public void factory_parse_validText(int y, int m, int d, String parsable) { + LocalDate t = LocalDate.parse(parsable); + assertNotNull(t, parsable); + assertEquals(t.getYear(), y, parsable); + assertEquals(t.getMonth().getValue(), m, parsable); + assertEquals(t.getDayOfMonth(), d, parsable); + } + + @DataProvider(name="sampleBadParse") + Object[][] provider_sampleBadParse() { + return new Object[][]{ + {"2008/07/05"}, + {"10000-01-01"}, + {"2008-1-1"}, + {"2008--01"}, + {"ABCD-02-01"}, + {"2008-AB-01"}, + {"2008-02-AB"}, + {"-0000-02-01"}, + {"2008-02-01Z"}, + {"2008-02-01+01:00"}, + {"2008-02-01+01:00[Europe/Paris]"}, + }; + } + + @Test(dataProvider="sampleBadParse", expectedExceptions={DateTimeParseException.class}, groups={"tck"}) + public void factory_parse_invalidText(String unparsable) { + LocalDate.parse(unparsable); + } + + @Test(expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_illegalValue() { + LocalDate.parse("2008-06-32"); + } + + @Test(expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_invalidValue() { + LocalDate.parse("2008-06-31"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_nullText() { + LocalDate.parse((String) null); + } + + //----------------------------------------------------------------------- + // parse(DateTimeFormatter) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_parse_formatter() { + DateTimeFormatter f = DateTimeFormatters.pattern("y M d"); + LocalDate test = LocalDate.parse("2010 12 3", f); + assertEquals(test, LocalDate.of(2010, 12, 3)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_formatter_nullText() { + DateTimeFormatter f = DateTimeFormatters.pattern("y M d"); + LocalDate.parse((String) null, f); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_formatter_nullFormatter() { + LocalDate.parse("ANY", null); + } + + //----------------------------------------------------------------------- + // get(TemporalField) + //----------------------------------------------------------------------- + @Test + public void test_get_TemporalField() { + LocalDate test = LocalDate.of(2008, 6, 30); + assertEquals(test.get(ChronoField.YEAR), 2008); + assertEquals(test.get(ChronoField.MONTH_OF_YEAR), 6); + assertEquals(test.get(ChronoField.DAY_OF_MONTH), 30); + assertEquals(test.get(ChronoField.DAY_OF_WEEK), 1); + assertEquals(test.get(ChronoField.DAY_OF_YEAR), 182); + } + + @Test + public void test_getLong_TemporalField() { + LocalDate test = LocalDate.of(2008, 6, 30); + assertEquals(test.getLong(ChronoField.YEAR), 2008); + assertEquals(test.getLong(ChronoField.MONTH_OF_YEAR), 6); + assertEquals(test.getLong(ChronoField.DAY_OF_MONTH), 30); + assertEquals(test.getLong(ChronoField.DAY_OF_WEEK), 1); + assertEquals(test.getLong(ChronoField.DAY_OF_YEAR), 182); + } + + //----------------------------------------------------------------------- + // query(TemporalQuery) + //----------------------------------------------------------------------- + @Test + public void test_query_chrono() { + assertEquals(TEST_2007_07_15.query(Queries.chrono()), ISOChrono.INSTANCE); + assertEquals(Queries.chrono().queryFrom(TEST_2007_07_15), ISOChrono.INSTANCE); + } + + @Test + public void test_query_zoneId() { + assertEquals(TEST_2007_07_15.query(Queries.zoneId()), null); + assertEquals(Queries.zoneId().queryFrom(TEST_2007_07_15), null); + } + + @Test + public void test_query_precision() { + assertEquals(TEST_2007_07_15.query(Queries.precision()), ChronoUnit.DAYS); + assertEquals(Queries.precision().queryFrom(TEST_2007_07_15), ChronoUnit.DAYS); + } + + @Test + public void test_query_offset() { + assertEquals(TEST_2007_07_15.query(Queries.offset()), null); + assertEquals(Queries.offset().queryFrom(TEST_2007_07_15), null); + } + + @Test + public void test_query_zone() { + assertEquals(TEST_2007_07_15.query(Queries.zone()), null); + assertEquals(Queries.zone().queryFrom(TEST_2007_07_15), null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_query_null() { + TEST_2007_07_15.query(null); + } + + //----------------------------------------------------------------------- + // get*() + //----------------------------------------------------------------------- + @DataProvider(name="sampleDates") + Object[][] provider_sampleDates() { + return new Object[][] { + {2008, 7, 5}, + {2007, 7, 5}, + {2006, 7, 5}, + {2005, 7, 5}, + {2004, 1, 1}, + {-1, 1, 2}, + }; + } + + //----------------------------------------------------------------------- + @Test(dataProvider="sampleDates", groups={"tck"}) + public void test_get(int y, int m, int d) { + LocalDate a = LocalDate.of(y, m, d); + assertEquals(a.getYear(), y); + assertEquals(a.getMonth(), Month.of(m)); + assertEquals(a.getDayOfMonth(), d); + } + + @Test(dataProvider="sampleDates", groups={"tck"}) + public void test_getDOY(int y, int m, int d) { + LocalDate a = LocalDate.of(y, m, d); + int total = 0; + for (int i = 1; i < m; i++) { + total += Month.of(i).length(isIsoLeap(y)); + } + int doy = total + d; + assertEquals(a.getDayOfYear(), doy); + } + + @Test(groups={"tck"}) + public void test_getDayOfWeek() { + DayOfWeek dow = DayOfWeek.MONDAY; + for (Month month : Month.values()) { + int length = month.length(false); + for (int i = 1; i <= length; i++) { + LocalDate d = LocalDate.of(2007, month, i); + assertSame(d.getDayOfWeek(), dow); + dow = dow.plus(1); + } + } + } + + //----------------------------------------------------------------------- + // isLeapYear() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_isLeapYear() { + assertEquals(LocalDate.of(1999, 1, 1).isLeapYear(), false); + assertEquals(LocalDate.of(2000, 1, 1).isLeapYear(), true); + assertEquals(LocalDate.of(2001, 1, 1).isLeapYear(), false); + assertEquals(LocalDate.of(2002, 1, 1).isLeapYear(), false); + assertEquals(LocalDate.of(2003, 1, 1).isLeapYear(), false); + assertEquals(LocalDate.of(2004, 1, 1).isLeapYear(), true); + assertEquals(LocalDate.of(2005, 1, 1).isLeapYear(), false); + + assertEquals(LocalDate.of(1500, 1, 1).isLeapYear(), false); + assertEquals(LocalDate.of(1600, 1, 1).isLeapYear(), true); + assertEquals(LocalDate.of(1700, 1, 1).isLeapYear(), false); + assertEquals(LocalDate.of(1800, 1, 1).isLeapYear(), false); + assertEquals(LocalDate.of(1900, 1, 1).isLeapYear(), false); + } + + //----------------------------------------------------------------------- + // lengthOfMonth() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_lengthOfMonth_notLeapYear() { + assertEquals(LocalDate.of(2007, 1, 1).lengthOfMonth(), 31); + assertEquals(LocalDate.of(2007, 2, 1).lengthOfMonth(), 28); + assertEquals(LocalDate.of(2007, 3, 1).lengthOfMonth(), 31); + assertEquals(LocalDate.of(2007, 4, 1).lengthOfMonth(), 30); + assertEquals(LocalDate.of(2007, 5, 1).lengthOfMonth(), 31); + assertEquals(LocalDate.of(2007, 6, 1).lengthOfMonth(), 30); + assertEquals(LocalDate.of(2007, 7, 1).lengthOfMonth(), 31); + assertEquals(LocalDate.of(2007, 8, 1).lengthOfMonth(), 31); + assertEquals(LocalDate.of(2007, 9, 1).lengthOfMonth(), 30); + assertEquals(LocalDate.of(2007, 10, 1).lengthOfMonth(), 31); + assertEquals(LocalDate.of(2007, 11, 1).lengthOfMonth(), 30); + assertEquals(LocalDate.of(2007, 12, 1).lengthOfMonth(), 31); + } + + @Test(groups={"tck"}) + public void test_lengthOfMonth_leapYear() { + assertEquals(LocalDate.of(2008, 1, 1).lengthOfMonth(), 31); + assertEquals(LocalDate.of(2008, 2, 1).lengthOfMonth(), 29); + assertEquals(LocalDate.of(2008, 3, 1).lengthOfMonth(), 31); + assertEquals(LocalDate.of(2008, 4, 1).lengthOfMonth(), 30); + assertEquals(LocalDate.of(2008, 5, 1).lengthOfMonth(), 31); + assertEquals(LocalDate.of(2008, 6, 1).lengthOfMonth(), 30); + assertEquals(LocalDate.of(2008, 7, 1).lengthOfMonth(), 31); + assertEquals(LocalDate.of(2008, 8, 1).lengthOfMonth(), 31); + assertEquals(LocalDate.of(2008, 9, 1).lengthOfMonth(), 30); + assertEquals(LocalDate.of(2008, 10, 1).lengthOfMonth(), 31); + assertEquals(LocalDate.of(2008, 11, 1).lengthOfMonth(), 30); + assertEquals(LocalDate.of(2008, 12, 1).lengthOfMonth(), 31); + } + + //----------------------------------------------------------------------- + // lengthOfYear() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_lengthOfYear() { + assertEquals(LocalDate.of(2007, 1, 1).lengthOfYear(), 365); + assertEquals(LocalDate.of(2008, 1, 1).lengthOfYear(), 366); + } + + //----------------------------------------------------------------------- + // with() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_with_adjustment() { + final LocalDate sample = LocalDate.of(2012, 3, 4); + TemporalAdjuster adjuster = new TemporalAdjuster() { + @Override + public Temporal adjustInto(Temporal dateTime) { + return sample; + } + }; + assertEquals(TEST_2007_07_15.with(adjuster), sample); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_with_adjustment_null() { + TEST_2007_07_15.with((TemporalAdjuster) null); + } + + //----------------------------------------------------------------------- + // with(TemporalField,long) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_with_TemporalField_long_normal() { + LocalDate t = TEST_2007_07_15.with(YEAR, 2008); + assertEquals(t, LocalDate.of(2008, 7, 15)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"} ) + public void test_with_TemporalField_long_null() { + TEST_2007_07_15.with((TemporalField) null, 1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"} ) + public void test_with_TemporalField_long_invalidField() { + TEST_2007_07_15.with(MockFieldNoValue.INSTANCE, 1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"} ) + public void test_with_TemporalField_long_timeField() { + TEST_2007_07_15.with(ChronoField.AMPM_OF_DAY, 1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"} ) + public void test_with_TemporalField_long_invalidValue() { + TEST_2007_07_15.with(ChronoField.DAY_OF_WEEK, -1); + } + + //----------------------------------------------------------------------- + // withYear() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withYear_int_normal() { + LocalDate t = TEST_2007_07_15.withYear(2008); + assertEquals(t, LocalDate.of(2008, 7, 15)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withYear_int_invalid() { + TEST_2007_07_15.withYear(Year.MIN_VALUE - 1); + } + + @Test(groups={"tck"}) + public void test_withYear_int_adjustDay() { + LocalDate t = LocalDate.of(2008, 2, 29).withYear(2007); + LocalDate expected = LocalDate.of(2007, 2, 28); + assertEquals(t, expected); + } + + //----------------------------------------------------------------------- + // withMonth() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withMonth_int_normal() { + LocalDate t = TEST_2007_07_15.withMonth(1); + assertEquals(t, LocalDate.of(2007, 1, 15)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withMonth_int_invalid() { + TEST_2007_07_15.withMonth(13); + } + + @Test(groups={"tck"}) + public void test_withMonth_int_adjustDay() { + LocalDate t = LocalDate.of(2007, 12, 31).withMonth(11); + LocalDate expected = LocalDate.of(2007, 11, 30); + assertEquals(t, expected); + } + + //----------------------------------------------------------------------- + // withDayOfMonth() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withDayOfMonth_normal() { + LocalDate t = TEST_2007_07_15.withDayOfMonth(1); + assertEquals(t, LocalDate.of(2007, 7, 1)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withDayOfMonth_illegal() { + TEST_2007_07_15.withDayOfMonth(32); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withDayOfMonth_invalid() { + LocalDate.of(2007, 11, 30).withDayOfMonth(31); + } + + //----------------------------------------------------------------------- + // withDayOfYear(int) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withDayOfYear_normal() { + LocalDate t = TEST_2007_07_15.withDayOfYear(33); + assertEquals(t, LocalDate.of(2007, 2, 2)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withDayOfYear_illegal() { + TEST_2007_07_15.withDayOfYear(367); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withDayOfYear_invalid() { + TEST_2007_07_15.withDayOfYear(366); + } + + //----------------------------------------------------------------------- + // plus(Period) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plus_Period_positiveMonths() { + MockSimplePeriod period = MockSimplePeriod.of(7, ChronoUnit.MONTHS); + LocalDate t = TEST_2007_07_15.plus(period); + assertEquals(t, LocalDate.of(2008, 2, 15)); + } + + @Test(groups={"tck"}) + public void test_plus_Period_negativeDays() { + MockSimplePeriod period = MockSimplePeriod.of(-25, ChronoUnit.DAYS); + LocalDate t = TEST_2007_07_15.plus(period); + assertEquals(t, LocalDate.of(2007, 6, 20)); + } + + @Test(groups={"tck"}, expectedExceptions=DateTimeException.class) + public void test_plus_Period_timeNotAllowed() { + MockSimplePeriod period = MockSimplePeriod.of(7, ChronoUnit.HOURS); + TEST_2007_07_15.plus(period); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_plus_Period_null() { + TEST_2007_07_15.plus((MockSimplePeriod) null); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plus_Period_invalidTooLarge() { + MockSimplePeriod period = MockSimplePeriod.of(1, ChronoUnit.YEARS); + LocalDate.of(Year.MAX_VALUE, 1, 1).plus(period); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plus_Period_invalidTooSmall() { + MockSimplePeriod period = MockSimplePeriod.of(-1, ChronoUnit.YEARS); + LocalDate.of(Year.MIN_VALUE, 1, 1).plus(period); + } + + //----------------------------------------------------------------------- + // plus(long,TemporalUnit) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plus_longTemporalUnit_positiveMonths() { + LocalDate t = TEST_2007_07_15.plus(7, ChronoUnit.MONTHS); + assertEquals(t, LocalDate.of(2008, 2, 15)); + } + + @Test(groups={"tck"}) + public void test_plus_longTemporalUnit_negativeDays() { + LocalDate t = TEST_2007_07_15.plus(-25, ChronoUnit.DAYS); + assertEquals(t, LocalDate.of(2007, 6, 20)); + } + + @Test(groups={"tck"}, expectedExceptions=DateTimeException.class) + public void test_plus_longTemporalUnit_timeNotAllowed() { + TEST_2007_07_15.plus(7, ChronoUnit.HOURS); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_plus_longTemporalUnit_null() { + TEST_2007_07_15.plus(1, (TemporalUnit) null); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plus_longTemporalUnit_invalidTooLarge() { + LocalDate.of(Year.MAX_VALUE, 1, 1).plus(1, ChronoUnit.YEARS); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plus_longTemporalUnit_invalidTooSmall() { + LocalDate.of(Year.MIN_VALUE, 1, 1).plus(-1, ChronoUnit.YEARS); + } + + //----------------------------------------------------------------------- + // plusYears() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusYears_long_normal() { + LocalDate t = TEST_2007_07_15.plusYears(1); + assertEquals(t, LocalDate.of(2008, 7, 15)); + } + + @Test(groups={"tck"}) + public void test_plusYears_long_negative() { + LocalDate t = TEST_2007_07_15.plusYears(-1); + assertEquals(t, LocalDate.of(2006, 7, 15)); + } + + @Test(groups={"tck"}) + public void test_plusYears_long_adjustDay() { + LocalDate t = LocalDate.of(2008, 2, 29).plusYears(1); + LocalDate expected = LocalDate.of(2009, 2, 28); + assertEquals(t, expected); + } + + @Test(groups={"tck"}) + public void test_plusYears_long_big() { + long years = 20L + Year.MAX_VALUE; + LocalDate test = LocalDate.of(-40, 6, 1).plusYears(years); + assertEquals(test, LocalDate.of((int) (-40L + years), 6, 1)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusYears_long_invalidTooLarge() { + LocalDate test = LocalDate.of(Year.MAX_VALUE, 6, 1); + test.plusYears(1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusYears_long_invalidTooLargeMaxAddMax() { + LocalDate test = LocalDate.of(Year.MAX_VALUE, 12, 1); + test.plusYears(Long.MAX_VALUE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusYears_long_invalidTooLargeMaxAddMin() { + LocalDate test = LocalDate.of(Year.MAX_VALUE, 12, 1); + test.plusYears(Long.MIN_VALUE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusYears_long_invalidTooSmall_validInt() { + LocalDate.of(Year.MIN_VALUE, 1, 1).plusYears(-1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusYears_long_invalidTooSmall_invalidInt() { + LocalDate.of(Year.MIN_VALUE, 1, 1).plusYears(-10); + } + + //----------------------------------------------------------------------- + // plusMonths() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusMonths_long_normal() { + LocalDate t = TEST_2007_07_15.plusMonths(1); + assertEquals(t, LocalDate.of(2007, 8, 15)); + } + + @Test(groups={"tck"}) + public void test_plusMonths_long_overYears() { + LocalDate t = TEST_2007_07_15.plusMonths(25); + assertEquals(t, LocalDate.of(2009, 8, 15)); + } + + @Test(groups={"tck"}) + public void test_plusMonths_long_negative() { + LocalDate t = TEST_2007_07_15.plusMonths(-1); + assertEquals(t, LocalDate.of(2007, 6, 15)); + } + + @Test(groups={"tck"}) + public void test_plusMonths_long_negativeAcrossYear() { + LocalDate t = TEST_2007_07_15.plusMonths(-7); + assertEquals(t, LocalDate.of(2006, 12, 15)); + } + + @Test(groups={"tck"}) + public void test_plusMonths_long_negativeOverYears() { + LocalDate t = TEST_2007_07_15.plusMonths(-31); + assertEquals(t, LocalDate.of(2004, 12, 15)); + } + + @Test(groups={"tck"}) + public void test_plusMonths_long_adjustDayFromLeapYear() { + LocalDate t = LocalDate.of(2008, 2, 29).plusMonths(12); + LocalDate expected = LocalDate.of(2009, 2, 28); + assertEquals(t, expected); + } + + @Test(groups={"tck"}) + public void test_plusMonths_long_adjustDayFromMonthLength() { + LocalDate t = LocalDate.of(2007, 3, 31).plusMonths(1); + LocalDate expected = LocalDate.of(2007, 4, 30); + assertEquals(t, expected); + } + + @Test(groups={"tck"}) + public void test_plusMonths_long_big() { + long months = 20L + Integer.MAX_VALUE; + LocalDate test = LocalDate.of(-40, 6, 1).plusMonths(months); + assertEquals(test, LocalDate.of((int) (-40L + months / 12), 6 + (int) (months % 12), 1)); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_plusMonths_long_invalidTooLarge() { + LocalDate.of(Year.MAX_VALUE, 12, 1).plusMonths(1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusMonths_long_invalidTooLargeMaxAddMax() { + LocalDate test = LocalDate.of(Year.MAX_VALUE, 12, 1); + test.plusMonths(Long.MAX_VALUE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusMonths_long_invalidTooLargeMaxAddMin() { + LocalDate test = LocalDate.of(Year.MAX_VALUE, 12, 1); + test.plusMonths(Long.MIN_VALUE); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_plusMonths_long_invalidTooSmall() { + LocalDate.of(Year.MIN_VALUE, 1, 1).plusMonths(-1); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_normal() { + LocalDate t = TEST_2007_07_15.plusWeeks(1); + assertEquals(t, LocalDate.of(2007, 7, 22)); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_overMonths() { + LocalDate t = TEST_2007_07_15.plusWeeks(9); + assertEquals(t, LocalDate.of(2007, 9, 16)); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_overYears() { + LocalDate t = LocalDate.of(2006, 7, 16).plusWeeks(52); + assertEquals(t, TEST_2007_07_15); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_overLeapYears() { + LocalDate t = TEST_2007_07_15.plusYears(-1).plusWeeks(104); + assertEquals(t, LocalDate.of(2008, 7, 12)); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_negative() { + LocalDate t = TEST_2007_07_15.plusWeeks(-1); + assertEquals(t, LocalDate.of(2007, 7, 8)); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_negativeAcrossYear() { + LocalDate t = TEST_2007_07_15.plusWeeks(-28); + assertEquals(t, LocalDate.of(2006, 12, 31)); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_negativeOverYears() { + LocalDate t = TEST_2007_07_15.plusWeeks(-104); + assertEquals(t, LocalDate.of(2005, 7, 17)); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_maximum() { + LocalDate t = LocalDate.of(Year.MAX_VALUE, 12, 24).plusWeeks(1); + LocalDate expected = LocalDate.of(Year.MAX_VALUE, 12, 31); + assertEquals(t, expected); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_minimum() { + LocalDate t = LocalDate.of(Year.MIN_VALUE, 1, 8).plusWeeks(-1); + LocalDate expected = LocalDate.of(Year.MIN_VALUE, 1, 1); + assertEquals(t, expected); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_plusWeeks_invalidTooLarge() { + LocalDate.of(Year.MAX_VALUE, 12, 25).plusWeeks(1); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_plusWeeks_invalidTooSmall() { + LocalDate.of(Year.MIN_VALUE, 1, 7).plusWeeks(-1); + } + + @Test(expectedExceptions={ArithmeticException.class}, groups={"tck"}) + public void test_plusWeeks_invalidMaxMinusMax() { + LocalDate.of(Year.MAX_VALUE, 12, 25).plusWeeks(Long.MAX_VALUE); + } + + @Test(expectedExceptions={ArithmeticException.class}, groups={"tck"}) + public void test_plusWeeks_invalidMaxMinusMin() { + LocalDate.of(Year.MAX_VALUE, 12, 25).plusWeeks(Long.MIN_VALUE); + } + + @Test(groups={"tck"}) + public void test_plusDays_normal() { + LocalDate t = TEST_2007_07_15.plusDays(1); + assertEquals(t, LocalDate.of(2007, 7, 16)); + } + + @Test(groups={"tck"}) + public void test_plusDays_overMonths() { + LocalDate t = TEST_2007_07_15.plusDays(62); + assertEquals(t, LocalDate.of(2007, 9, 15)); + } + + @Test(groups={"tck"}) + public void test_plusDays_overYears() { + LocalDate t = LocalDate.of(2006, 7, 14).plusDays(366); + assertEquals(t, TEST_2007_07_15); + } + + @Test(groups={"tck"}) + public void test_plusDays_overLeapYears() { + LocalDate t = TEST_2007_07_15.plusYears(-1).plusDays(365 + 366); + assertEquals(t, LocalDate.of(2008, 7, 15)); + } + + @Test(groups={"tck"}) + public void test_plusDays_negative() { + LocalDate t = TEST_2007_07_15.plusDays(-1); + assertEquals(t, LocalDate.of(2007, 7, 14)); + } + + @Test(groups={"tck"}) + public void test_plusDays_negativeAcrossYear() { + LocalDate t = TEST_2007_07_15.plusDays(-196); + assertEquals(t, LocalDate.of(2006, 12, 31)); + } + + @Test(groups={"tck"}) + public void test_plusDays_negativeOverYears() { + LocalDate t = TEST_2007_07_15.plusDays(-730); + assertEquals(t, LocalDate.of(2005, 7, 15)); + } + + @Test(groups={"tck"}) + public void test_plusDays_maximum() { + LocalDate t = LocalDate.of(Year.MAX_VALUE, 12, 30).plusDays(1); + LocalDate expected = LocalDate.of(Year.MAX_VALUE, 12, 31); + assertEquals(t, expected); + } + + @Test(groups={"tck"}) + public void test_plusDays_minimum() { + LocalDate t = LocalDate.of(Year.MIN_VALUE, 1, 2).plusDays(-1); + LocalDate expected = LocalDate.of(Year.MIN_VALUE, 1, 1); + assertEquals(t, expected); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_plusDays_invalidTooLarge() { + LocalDate.of(Year.MAX_VALUE, 12, 31).plusDays(1); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_plusDays_invalidTooSmall() { + LocalDate.of(Year.MIN_VALUE, 1, 1).plusDays(-1); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void test_plusDays_overflowTooLarge() { + LocalDate.of(Year.MAX_VALUE, 12, 31).plusDays(Long.MAX_VALUE); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void test_plusDays_overflowTooSmall() { + LocalDate.of(Year.MIN_VALUE, 1, 1).plusDays(Long.MIN_VALUE); + } + + //----------------------------------------------------------------------- + // minus(Period) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minus_Period_positiveMonths() { + MockSimplePeriod period = MockSimplePeriod.of(7, ChronoUnit.MONTHS); + LocalDate t = TEST_2007_07_15.minus(period); + assertEquals(t, LocalDate.of(2006, 12, 15)); + } + + @Test(groups={"tck"}) + public void test_minus_Period_negativeDays() { + MockSimplePeriod period = MockSimplePeriod.of(-25, ChronoUnit.DAYS); + LocalDate t = TEST_2007_07_15.minus(period); + assertEquals(t, LocalDate.of(2007, 8, 9)); + } + + @Test(groups={"tck"}, expectedExceptions=DateTimeException.class) + public void test_minus_Period_timeNotAllowed() { + MockSimplePeriod period = MockSimplePeriod.of(7, ChronoUnit.HOURS); + TEST_2007_07_15.minus(period); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_minus_Period_null() { + TEST_2007_07_15.minus((MockSimplePeriod) null); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minus_Period_invalidTooLarge() { + MockSimplePeriod period = MockSimplePeriod.of(-1, ChronoUnit.YEARS); + LocalDate.of(Year.MAX_VALUE, 1, 1).minus(period); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minus_Period_invalidTooSmall() { + MockSimplePeriod period = MockSimplePeriod.of(1, ChronoUnit.YEARS); + LocalDate.of(Year.MIN_VALUE, 1, 1).minus(period); + } + + //----------------------------------------------------------------------- + // minus(long,TemporalUnit) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minus_longTemporalUnit_positiveMonths() { + LocalDate t = TEST_2007_07_15.minus(7, ChronoUnit.MONTHS); + assertEquals(t, LocalDate.of(2006, 12, 15)); + } + + @Test(groups={"tck"}) + public void test_minus_longTemporalUnit_negativeDays() { + LocalDate t = TEST_2007_07_15.minus(-25, ChronoUnit.DAYS); + assertEquals(t, LocalDate.of(2007, 8, 9)); + } + + @Test(groups={"tck"}, expectedExceptions=DateTimeException.class) + public void test_minus_longTemporalUnit_timeNotAllowed() { + TEST_2007_07_15.minus(7, ChronoUnit.HOURS); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_minus_longTemporalUnit_null() { + TEST_2007_07_15.minus(1, (TemporalUnit) null); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minus_longTemporalUnit_invalidTooLarge() { + LocalDate.of(Year.MAX_VALUE, 1, 1).minus(-1, ChronoUnit.YEARS); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minus_longTemporalUnit_invalidTooSmall() { + LocalDate.of(Year.MIN_VALUE, 1, 1).minus(1, ChronoUnit.YEARS); + } + + //----------------------------------------------------------------------- + // minusYears() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusYears_long_normal() { + LocalDate t = TEST_2007_07_15.minusYears(1); + assertEquals(t, LocalDate.of(2006, 7, 15)); + } + + @Test(groups={"tck"}) + public void test_minusYears_long_negative() { + LocalDate t = TEST_2007_07_15.minusYears(-1); + assertEquals(t, LocalDate.of(2008, 7, 15)); + } + + @Test(groups={"tck"}) + public void test_minusYears_long_adjustDay() { + LocalDate t = LocalDate.of(2008, 2, 29).minusYears(1); + LocalDate expected = LocalDate.of(2007, 2, 28); + assertEquals(t, expected); + } + + @Test(groups={"tck"}) + public void test_minusYears_long_big() { + long years = 20L + Year.MAX_VALUE; + LocalDate test = LocalDate.of(40, 6, 1).minusYears(years); + assertEquals(test, LocalDate.of((int) (40L - years), 6, 1)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusYears_long_invalidTooLarge() { + LocalDate test = LocalDate.of(Year.MAX_VALUE, 6, 1); + test.minusYears(-1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusYears_long_invalidTooLargeMaxAddMax() { + LocalDate test = LocalDate.of(Year.MAX_VALUE, 12, 1); + test.minusYears(Long.MAX_VALUE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusYears_long_invalidTooLargeMaxAddMin() { + LocalDate test = LocalDate.of(Year.MAX_VALUE, 12, 1); + test.minusYears(Long.MIN_VALUE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusYears_long_invalidTooSmall() { + LocalDate.of(Year.MIN_VALUE, 1, 1).minusYears(1); + } + + //----------------------------------------------------------------------- + // minusMonths() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusMonths_long_normal() { + LocalDate t = TEST_2007_07_15.minusMonths(1); + assertEquals(t, LocalDate.of(2007, 6, 15)); + } + + @Test(groups={"tck"}) + public void test_minusMonths_long_overYears() { + LocalDate t = TEST_2007_07_15.minusMonths(25); + assertEquals(t, LocalDate.of(2005, 6, 15)); + } + + @Test(groups={"tck"}) + public void test_minusMonths_long_negative() { + LocalDate t = TEST_2007_07_15.minusMonths(-1); + assertEquals(t, LocalDate.of(2007, 8, 15)); + } + + @Test(groups={"tck"}) + public void test_minusMonths_long_negativeAcrossYear() { + LocalDate t = TEST_2007_07_15.minusMonths(-7); + assertEquals(t, LocalDate.of(2008, 2, 15)); + } + + @Test(groups={"tck"}) + public void test_minusMonths_long_negativeOverYears() { + LocalDate t = TEST_2007_07_15.minusMonths(-31); + assertEquals(t, LocalDate.of(2010, 2, 15)); + } + + @Test(groups={"tck"}) + public void test_minusMonths_long_adjustDayFromLeapYear() { + LocalDate t = LocalDate.of(2008, 2, 29).minusMonths(12); + LocalDate expected = LocalDate.of(2007, 2, 28); + assertEquals(t, expected); + } + + @Test(groups={"tck"}) + public void test_minusMonths_long_adjustDayFromMonthLength() { + LocalDate t = LocalDate.of(2007, 3, 31).minusMonths(1); + LocalDate expected = LocalDate.of(2007, 2, 28); + assertEquals(t, expected); + } + + @Test(groups={"tck"}) + public void test_minusMonths_long_big() { + long months = 20L + Integer.MAX_VALUE; + LocalDate test = LocalDate.of(40, 6, 1).minusMonths(months); + assertEquals(test, LocalDate.of((int) (40L - months / 12), 6 - (int) (months % 12), 1)); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_minusMonths_long_invalidTooLarge() { + LocalDate.of(Year.MAX_VALUE, 12, 1).minusMonths(-1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusMonths_long_invalidTooLargeMaxAddMax() { + LocalDate test = LocalDate.of(Year.MAX_VALUE, 12, 1); + test.minusMonths(Long.MAX_VALUE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusMonths_long_invalidTooLargeMaxAddMin() { + LocalDate test = LocalDate.of(Year.MAX_VALUE, 12, 1); + test.minusMonths(Long.MIN_VALUE); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_minusMonths_long_invalidTooSmall() { + LocalDate.of(Year.MIN_VALUE, 1, 1).minusMonths(1); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_normal() { + LocalDate t = TEST_2007_07_15.minusWeeks(1); + assertEquals(t, LocalDate.of(2007, 7, 8)); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_overMonths() { + LocalDate t = TEST_2007_07_15.minusWeeks(9); + assertEquals(t, LocalDate.of(2007, 5, 13)); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_overYears() { + LocalDate t = LocalDate.of(2008, 7, 13).minusWeeks(52); + assertEquals(t, TEST_2007_07_15); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_overLeapYears() { + LocalDate t = TEST_2007_07_15.minusYears(-1).minusWeeks(104); + assertEquals(t, LocalDate.of(2006, 7, 18)); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_negative() { + LocalDate t = TEST_2007_07_15.minusWeeks(-1); + assertEquals(t, LocalDate.of(2007, 7, 22)); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_negativeAcrossYear() { + LocalDate t = TEST_2007_07_15.minusWeeks(-28); + assertEquals(t, LocalDate.of(2008, 1, 27)); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_negativeOverYears() { + LocalDate t = TEST_2007_07_15.minusWeeks(-104); + assertEquals(t, LocalDate.of(2009, 7, 12)); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_maximum() { + LocalDate t = LocalDate.of(Year.MAX_VALUE, 12, 24).minusWeeks(-1); + LocalDate expected = LocalDate.of(Year.MAX_VALUE, 12, 31); + assertEquals(t, expected); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_minimum() { + LocalDate t = LocalDate.of(Year.MIN_VALUE, 1, 8).minusWeeks(1); + LocalDate expected = LocalDate.of(Year.MIN_VALUE, 1, 1); + assertEquals(t, expected); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_minusWeeks_invalidTooLarge() { + LocalDate.of(Year.MAX_VALUE, 12, 25).minusWeeks(-1); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_minusWeeks_invalidTooSmall() { + LocalDate.of(Year.MIN_VALUE, 1, 7).minusWeeks(1); + } + + @Test(expectedExceptions={ArithmeticException.class}, groups={"tck"}) + public void test_minusWeeks_invalidMaxMinusMax() { + LocalDate.of(Year.MAX_VALUE, 12, 25).minusWeeks(Long.MAX_VALUE); + } + + @Test(expectedExceptions={ArithmeticException.class}, groups={"tck"}) + public void test_minusWeeks_invalidMaxMinusMin() { + LocalDate.of(Year.MAX_VALUE, 12, 25).minusWeeks(Long.MIN_VALUE); + } + + @Test(groups={"tck"}) + public void test_minusDays_normal() { + LocalDate t = TEST_2007_07_15.minusDays(1); + assertEquals(t, LocalDate.of(2007, 7, 14)); + } + + @Test(groups={"tck"}) + public void test_minusDays_overMonths() { + LocalDate t = TEST_2007_07_15.minusDays(62); + assertEquals(t, LocalDate.of(2007, 5, 14)); + } + + @Test(groups={"tck"}) + public void test_minusDays_overYears() { + LocalDate t = LocalDate.of(2008, 7, 16).minusDays(367); + assertEquals(t, TEST_2007_07_15); + } + + @Test(groups={"tck"}) + public void test_minusDays_overLeapYears() { + LocalDate t = TEST_2007_07_15.plusYears(2).minusDays(365 + 366); + assertEquals(t, TEST_2007_07_15); + } + + @Test(groups={"tck"}) + public void test_minusDays_negative() { + LocalDate t = TEST_2007_07_15.minusDays(-1); + assertEquals(t, LocalDate.of(2007, 7, 16)); + } + + @Test(groups={"tck"}) + public void test_minusDays_negativeAcrossYear() { + LocalDate t = TEST_2007_07_15.minusDays(-169); + assertEquals(t, LocalDate.of(2007, 12, 31)); + } + + @Test(groups={"tck"}) + public void test_minusDays_negativeOverYears() { + LocalDate t = TEST_2007_07_15.minusDays(-731); + assertEquals(t, LocalDate.of(2009, 7, 15)); + } + + @Test(groups={"tck"}) + public void test_minusDays_maximum() { + LocalDate t = LocalDate.of(Year.MAX_VALUE, 12, 30).minusDays(-1); + LocalDate expected = LocalDate.of(Year.MAX_VALUE, 12, 31); + assertEquals(t, expected); + } + + @Test(groups={"tck"}) + public void test_minusDays_minimum() { + LocalDate t = LocalDate.of(Year.MIN_VALUE, 1, 2).minusDays(1); + LocalDate expected = LocalDate.of(Year.MIN_VALUE, 1, 1); + assertEquals(t, expected); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_minusDays_invalidTooLarge() { + LocalDate.of(Year.MAX_VALUE, 12, 31).minusDays(-1); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_minusDays_invalidTooSmall() { + LocalDate.of(Year.MIN_VALUE, 1, 1).minusDays(1); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void test_minusDays_overflowTooLarge() { + LocalDate.of(Year.MAX_VALUE, 12, 31).minusDays(Long.MIN_VALUE); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void test_minusDays_overflowTooSmall() { + LocalDate.of(Year.MIN_VALUE, 1, 1).minusDays(Long.MAX_VALUE); + } + + //----------------------------------------------------------------------- + // atTime() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_atTime_LocalTime() { + LocalDate t = LocalDate.of(2008, 6, 30); + assertEquals(t.atTime(LocalTime.of(11, 30)), LocalDateTime.of(2008, 6, 30, 11, 30)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_atTime_LocalTime_null() { + LocalDate t = LocalDate.of(2008, 6, 30); + t.atTime((LocalTime) null); + } + + //------------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_atTime_int_int() { + LocalDate t = LocalDate.of(2008, 6, 30); + assertEquals(t.atTime(11, 30), LocalDateTime.of(2008, 6, 30, 11, 30)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atTime_int_int_hourTooSmall() { + LocalDate t = LocalDate.of(2008, 6, 30); + t.atTime(-1, 30); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atTime_int_int_hourTooBig() { + LocalDate t = LocalDate.of(2008, 6, 30); + t.atTime(24, 30); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atTime_int_int_minuteTooSmall() { + LocalDate t = LocalDate.of(2008, 6, 30); + t.atTime(11, -1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atTime_int_int_minuteTooBig() { + LocalDate t = LocalDate.of(2008, 6, 30); + t.atTime(11, 60); + } + + @Test(groups={"tck"}) + public void test_atTime_int_int_int() { + LocalDate t = LocalDate.of(2008, 6, 30); + assertEquals(t.atTime(11, 30, 40), LocalDateTime.of(2008, 6, 30, 11, 30, 40)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atTime_int_int_int_hourTooSmall() { + LocalDate t = LocalDate.of(2008, 6, 30); + t.atTime(-1, 30, 40); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atTime_int_int_int_hourTooBig() { + LocalDate t = LocalDate.of(2008, 6, 30); + t.atTime(24, 30, 40); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atTime_int_int_int_minuteTooSmall() { + LocalDate t = LocalDate.of(2008, 6, 30); + t.atTime(11, -1, 40); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atTime_int_int_int_minuteTooBig() { + LocalDate t = LocalDate.of(2008, 6, 30); + t.atTime(11, 60, 40); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atTime_int_int_int_secondTooSmall() { + LocalDate t = LocalDate.of(2008, 6, 30); + t.atTime(11, 30, -1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atTime_int_int_int_secondTooBig() { + LocalDate t = LocalDate.of(2008, 6, 30); + t.atTime(11, 30, 60); + } + + @Test(groups={"tck"}) + public void test_atTime_int_int_int_int() { + LocalDate t = LocalDate.of(2008, 6, 30); + assertEquals(t.atTime(11, 30, 40, 50), LocalDateTime.of(2008, 6, 30, 11, 30, 40, 50)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atTime_int_int_int_int_hourTooSmall() { + LocalDate t = LocalDate.of(2008, 6, 30); + t.atTime(-1, 30, 40, 50); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atTime_int_int_int_int_hourTooBig() { + LocalDate t = LocalDate.of(2008, 6, 30); + t.atTime(24, 30, 40, 50); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atTime_int_int_int_int_minuteTooSmall() { + LocalDate t = LocalDate.of(2008, 6, 30); + t.atTime(11, -1, 40, 50); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atTime_int_int_int_int_minuteTooBig() { + LocalDate t = LocalDate.of(2008, 6, 30); + t.atTime(11, 60, 40, 50); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atTime_int_int_int_int_secondTooSmall() { + LocalDate t = LocalDate.of(2008, 6, 30); + t.atTime(11, 30, -1, 50); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atTime_int_int_int_int_secondTooBig() { + LocalDate t = LocalDate.of(2008, 6, 30); + t.atTime(11, 30, 60, 50); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atTime_int_int_int_int_nanoTooSmall() { + LocalDate t = LocalDate.of(2008, 6, 30); + t.atTime(11, 30, 40, -1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atTime_int_int_int_int_nanoTooBig() { + LocalDate t = LocalDate.of(2008, 6, 30); + t.atTime(11, 30, 40, 1000000000); + } + + //----------------------------------------------------------------------- + // atOffset() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_atOffset() { + LocalDate t = LocalDate.of(2008, 6, 30); + assertEquals(t.atOffset(OFFSET_PTWO), OffsetDate.of(LocalDate.of(2008, 6, 30), OFFSET_PTWO)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_atOffset_nullZoneOffset() { + LocalDate t = LocalDate.of(2008, 6, 30); + t.atOffset((ZoneOffset) null); + } + + //----------------------------------------------------------------------- + // atStartOfDay() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_atStartOfDay() { + LocalDate t = LocalDate.of(2008, 6, 30); + assertEquals(t.atStartOfDay(ZONE_PARIS), + ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 0, 0), ZONE_PARIS)); + } + + @Test(groups={"tck"}) + public void test_atStartOfDay_dstGap() { + LocalDate t = LocalDate.of(2007, 4, 1); + assertEquals(t.atStartOfDay(ZONE_GAZA), + ZonedDateTime.of(LocalDateTime.of(2007, 4, 1, 1, 0), ZONE_GAZA)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_atStartOfDay_nullTimeZone() { + LocalDate t = LocalDate.of(2008, 6, 30); + t.atStartOfDay((ZoneId) null); + } + + //----------------------------------------------------------------------- + // toEpochDay() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toEpochDay() { + long date_0000_01_01 = -678941 - 40587; + + LocalDate test = LocalDate.of(0, 1, 1); + for (long i = date_0000_01_01; i < 700000; i++) { + assertEquals(test.toEpochDay(), i); + test = next(test); + } + test = LocalDate.of(0, 1, 1); + for (long i = date_0000_01_01; i > -2000000; i--) { + assertEquals(test.toEpochDay(), i); + test = previous(test); + } + + assertEquals(LocalDate.of(1858, 11, 17).toEpochDay(), -40587); + assertEquals(LocalDate.of(1, 1, 1).toEpochDay(), -678575 - 40587); + assertEquals(LocalDate.of(1995, 9, 27).toEpochDay(), 49987 - 40587); + assertEquals(LocalDate.of(1970, 1, 1).toEpochDay(), 0); + assertEquals(LocalDate.of(-1, 12, 31).toEpochDay(), -678942 - 40587); + } + + //----------------------------------------------------------------------- + // compareTo() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_comparisons() { + doTest_comparisons_LocalDate( + LocalDate.of(Year.MIN_VALUE, 1, 1), + LocalDate.of(Year.MIN_VALUE, 12, 31), + LocalDate.of(-1, 1, 1), + LocalDate.of(-1, 12, 31), + LocalDate.of(0, 1, 1), + LocalDate.of(0, 12, 31), + LocalDate.of(1, 1, 1), + LocalDate.of(1, 12, 31), + LocalDate.of(2006, 1, 1), + LocalDate.of(2006, 12, 31), + LocalDate.of(2007, 1, 1), + LocalDate.of(2007, 12, 31), + LocalDate.of(2008, 1, 1), + LocalDate.of(2008, 2, 29), + LocalDate.of(2008, 12, 31), + LocalDate.of(Year.MAX_VALUE, 1, 1), + LocalDate.of(Year.MAX_VALUE, 12, 31) + ); + } + + void doTest_comparisons_LocalDate(LocalDate... localDates) { + for (int i = 0; i < localDates.length; i++) { + LocalDate a = localDates[i]; + for (int j = 0; j < localDates.length; j++) { + LocalDate b = localDates[j]; + if (i < j) { + assertTrue(a.compareTo(b) < 0, a + " <=> " + b); + assertEquals(a.isBefore(b), true, a + " <=> " + b); + assertEquals(a.isAfter(b), false, a + " <=> " + b); + assertEquals(a.equals(b), false, a + " <=> " + b); + } else if (i > j) { + assertTrue(a.compareTo(b) > 0, a + " <=> " + b); + assertEquals(a.isBefore(b), false, a + " <=> " + b); + assertEquals(a.isAfter(b), true, a + " <=> " + b); + assertEquals(a.equals(b), false, a + " <=> " + b); + } else { + assertEquals(a.compareTo(b), 0, a + " <=> " + b); + assertEquals(a.isBefore(b), false, a + " <=> " + b); + assertEquals(a.isAfter(b), false, a + " <=> " + b); + assertEquals(a.equals(b), true, a + " <=> " + b); + } + } + } + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_compareTo_ObjectNull() { + TEST_2007_07_15.compareTo(null); + } + + @Test(groups={"tck"}) + public void test_isBefore() { + assertTrue(TEST_2007_07_15.isBefore(LocalDate.of(2007, 07, 16))); + assertFalse(TEST_2007_07_15.isBefore(LocalDate.of(2007, 07, 14))); + assertFalse(TEST_2007_07_15.isBefore(TEST_2007_07_15)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_isBefore_ObjectNull() { + TEST_2007_07_15.isBefore(null); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_isAfter_ObjectNull() { + TEST_2007_07_15.isAfter(null); + } + + @Test(groups={"tck"}) + public void test_isAfter() { + assertTrue(TEST_2007_07_15.isAfter(LocalDate.of(2007, 07, 14))); + assertFalse(TEST_2007_07_15.isAfter(LocalDate.of(2007, 07, 16))); + assertFalse(TEST_2007_07_15.isAfter(TEST_2007_07_15)); + } + + @Test(expectedExceptions=ClassCastException.class, groups={"tck"}) + @SuppressWarnings({"unchecked", "rawtypes"}) + public void compareToNonLocalDate() { + Comparable c = TEST_2007_07_15; + c.compareTo(new Object()); + } + + //----------------------------------------------------------------------- + // equals() + //----------------------------------------------------------------------- + @Test(dataProvider="sampleDates" , groups={"tck"}) + public void test_equals_true(int y, int m, int d) { + LocalDate a = LocalDate.of(y, m, d); + LocalDate b = LocalDate.of(y, m, d); + assertEquals(a.equals(b), true); + } + @Test(dataProvider="sampleDates", groups={"tck"}) + public void test_equals_false_year_differs(int y, int m, int d) { + LocalDate a = LocalDate.of(y, m, d); + LocalDate b = LocalDate.of(y + 1, m, d); + assertEquals(a.equals(b), false); + } + @Test(dataProvider="sampleDates", groups={"tck"}) + public void test_equals_false_month_differs(int y, int m, int d) { + LocalDate a = LocalDate.of(y, m, d); + LocalDate b = LocalDate.of(y, m + 1, d); + assertEquals(a.equals(b), false); + } + @Test(dataProvider="sampleDates", groups={"tck"}) + public void test_equals_false_day_differs(int y, int m, int d) { + LocalDate a = LocalDate.of(y, m, d); + LocalDate b = LocalDate.of(y, m, d + 1); + assertEquals(a.equals(b), false); + } + + @Test(groups={"tck"}) + public void test_equals_itself_true() { + assertEquals(TEST_2007_07_15.equals(TEST_2007_07_15), true); + } + + @Test(groups={"tck"}) + public void test_equals_string_false() { + assertEquals(TEST_2007_07_15.equals("2007-07-15"), false); + } + + @Test(groups={"tck"}) + public void test_equals_null_false() { + assertEquals(TEST_2007_07_15.equals(null), false); + } + + //----------------------------------------------------------------------- + // hashCode() + //----------------------------------------------------------------------- + @Test(dataProvider="sampleDates", groups={"tck"}) + public void test_hashCode(int y, int m, int d) { + LocalDate a = LocalDate.of(y, m, d); + assertEquals(a.hashCode(), a.hashCode()); + LocalDate b = LocalDate.of(y, m, d); + assertEquals(a.hashCode(), b.hashCode()); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @DataProvider(name="sampleToString") + Object[][] provider_sampleToString() { + return new Object[][] { + {2008, 7, 5, "2008-07-05"}, + {2007, 12, 31, "2007-12-31"}, + {999, 12, 31, "0999-12-31"}, + {-1, 1, 2, "-0001-01-02"}, + {9999, 12, 31, "9999-12-31"}, + {-9999, 12, 31, "-9999-12-31"}, + {10000, 1, 1, "+10000-01-01"}, + {-10000, 1, 1, "-10000-01-01"}, + {12345678, 1, 1, "+12345678-01-01"}, + {-12345678, 1, 1, "-12345678-01-01"}, + }; + } + + @Test(dataProvider="sampleToString", groups={"tck"}) + public void test_toString(int y, int m, int d, String expected) { + LocalDate t = LocalDate.of(y, m, d); + String str = t.toString(); + assertEquals(str, expected); + } + + //----------------------------------------------------------------------- + // toString(DateTimeFormatter) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toString_formatter() { + DateTimeFormatter f = DateTimeFormatters.pattern("y M d"); + String t = LocalDate.of(2010, 12, 3).toString(f); + assertEquals(t, "2010 12 3"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_toString_formatter_null() { + LocalDate.of(2010, 12, 3).toString(null); + } + +} diff --git a/jdk/test/java/time/tck/java/time/TCKLocalDateTime.java b/jdk/test/java/time/tck/java/time/TCKLocalDateTime.java new file mode 100644 index 00000000000..1fa08f3c93e --- /dev/null +++ b/jdk/test/java/time/tck/java/time/TCKLocalDateTime.java @@ -0,0 +1,3045 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time; + +import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; +import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; +import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; +import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; +import static java.time.temporal.ChronoField.AMPM_OF_DAY; +import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_AMPM; +import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_DAY; +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.DAY_OF_WEEK; +import static java.time.temporal.ChronoField.DAY_OF_YEAR; +import static java.time.temporal.ChronoField.EPOCH_DAY; +import static java.time.temporal.ChronoField.EPOCH_MONTH; +import static java.time.temporal.ChronoField.ERA; +import static java.time.temporal.ChronoField.HOUR_OF_AMPM; +import static java.time.temporal.ChronoField.HOUR_OF_DAY; +import static java.time.temporal.ChronoField.MICRO_OF_DAY; +import static java.time.temporal.ChronoField.MICRO_OF_SECOND; +import static java.time.temporal.ChronoField.MILLI_OF_DAY; +import static java.time.temporal.ChronoField.MILLI_OF_SECOND; +import static java.time.temporal.ChronoField.MINUTE_OF_DAY; +import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.NANO_OF_DAY; +import static java.time.temporal.ChronoField.NANO_OF_SECOND; +import static java.time.temporal.ChronoField.SECOND_OF_DAY; +import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; +import static java.time.temporal.ChronoField.YEAR; +import static java.time.temporal.ChronoField.YEAR_OF_ERA; +import static java.time.temporal.ChronoUnit.DAYS; +import static java.time.temporal.ChronoUnit.NANOS; +import static java.time.temporal.ChronoUnit.SECONDS; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import java.time.Clock; +import java.time.DateTimeException; +import java.time.DayOfWeek; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Month; +import java.time.Period; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatters; +import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoUnit; +import java.time.temporal.ISOChrono; +import java.time.temporal.JulianFields; +import java.time.temporal.OffsetDateTime; +import java.time.temporal.Queries; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalAdder; +import java.time.temporal.TemporalAdjuster; +import java.time.temporal.TemporalField; +import java.time.temporal.TemporalQuery; +import java.time.temporal.TemporalSubtractor; +import java.time.temporal.TemporalUnit; +import java.time.temporal.Year; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import test.java.time.MockSimplePeriod; + +/** + * Test LocalDateTime. + */ +@Test +public class TCKLocalDateTime extends AbstractDateTimeTest { + + private static final ZoneOffset OFFSET_PONE = ZoneOffset.ofHours(1); + private static final ZoneOffset OFFSET_PTWO = ZoneOffset.ofHours(2); + private static final ZoneOffset OFFSET_MTWO = ZoneOffset.ofHours(-2); + private static final ZoneId ZONE_PARIS = ZoneId.of("Europe/Paris"); + private static final ZoneId ZONE_GAZA = ZoneId.of("Asia/Gaza"); + + private LocalDateTime TEST_2007_07_15_12_30_40_987654321 = LocalDateTime.of(2007, 7, 15, 12, 30, 40, 987654321); + private LocalDateTime MAX_DATE_TIME; + private LocalDateTime MIN_DATE_TIME; + private Instant MAX_INSTANT; + private Instant MIN_INSTANT; + + @BeforeMethod(groups={"implementation","tck"}) + public void setUp() { + MAX_DATE_TIME = LocalDateTime.MAX; + MIN_DATE_TIME = LocalDateTime.MIN; + MAX_INSTANT = MAX_DATE_TIME.atZone(ZoneOffset.UTC).toInstant(); + MIN_INSTANT = MIN_DATE_TIME.atZone(ZoneOffset.UTC).toInstant(); + } + + //----------------------------------------------------------------------- + @Override + protected List samples() { + TemporalAccessor[] array = {TEST_2007_07_15_12_30_40_987654321, LocalDateTime.MAX, LocalDateTime.MIN, }; + return Arrays.asList(array); + } + + @Override + protected List validFields() { + TemporalField[] array = { + NANO_OF_SECOND, + NANO_OF_DAY, + MICRO_OF_SECOND, + MICRO_OF_DAY, + MILLI_OF_SECOND, + MILLI_OF_DAY, + SECOND_OF_MINUTE, + SECOND_OF_DAY, + MINUTE_OF_HOUR, + MINUTE_OF_DAY, + CLOCK_HOUR_OF_AMPM, + HOUR_OF_AMPM, + CLOCK_HOUR_OF_DAY, + HOUR_OF_DAY, + AMPM_OF_DAY, + DAY_OF_WEEK, + ALIGNED_DAY_OF_WEEK_IN_MONTH, + ALIGNED_DAY_OF_WEEK_IN_YEAR, + DAY_OF_MONTH, + DAY_OF_YEAR, + EPOCH_DAY, + ALIGNED_WEEK_OF_MONTH, + ALIGNED_WEEK_OF_YEAR, + MONTH_OF_YEAR, + EPOCH_MONTH, + YEAR_OF_ERA, + YEAR, + ERA, + JulianFields.JULIAN_DAY, + JulianFields.MODIFIED_JULIAN_DAY, + JulianFields.RATA_DIE, + }; + return Arrays.asList(array); + } + + @Override + protected List invalidFields() { + List list = new ArrayList<>(Arrays.asList(ChronoField.values())); + list.removeAll(validFields()); + return list; + } + + //----------------------------------------------------------------------- + private void check(LocalDateTime test, int y, int m, int d, int h, int mi, int s, int n) { + assertEquals(test.getYear(), y); + assertEquals(test.getMonth().getValue(), m); + assertEquals(test.getDayOfMonth(), d); + assertEquals(test.getHour(), h); + assertEquals(test.getMinute(), mi); + assertEquals(test.getSecond(), s); + assertEquals(test.getNano(), n); + assertEquals(test, test); + assertEquals(test.hashCode(), test.hashCode()); + assertEquals(LocalDateTime.of(y, m, d, h, mi, s, n), test); + } + + private LocalDateTime createDateMidnight(int year, int month, int day) { + return LocalDateTime.of(year, month, day, 0, 0); + } + + //----------------------------------------------------------------------- + @Test + public void test_serialization() throws Exception { + assertSerializable(TEST_2007_07_15_12_30_40_987654321); + assertSerializable(LocalDateTime.MIN); + assertSerializable(LocalDateTime.MAX); + } + + @Test + public void test_serialization_format() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baos) ) { + dos.writeByte(5); + dos.writeInt(2012); + dos.writeByte(9); + dos.writeByte(16); + dos.writeByte(22); + dos.writeByte(17); + dos.writeByte(59); + dos.writeInt(459_000_000); + } + byte[] bytes = baos.toByteArray(); + assertSerializedBySer(LocalDateTime.of(2012, 9, 16, 22, 17, 59, 459_000_000), bytes); + } + + //----------------------------------------------------------------------- + // constants + //----------------------------------------------------------------------- + @Test + public void constant_MIN() { + check(LocalDateTime.MIN, Year.MIN_VALUE, 1, 1, 0, 0, 0, 0); + } + + @Test + public void constant_MAX() { + check(LocalDateTime.MAX, Year.MAX_VALUE, 12, 31, 23, 59, 59, 999999999); + } + + //----------------------------------------------------------------------- + // now() + //----------------------------------------------------------------------- + @Test(timeOut=30000, groups={"tck"}) // TODO: remove when time zone loading is faster + public void now() { + LocalDateTime expected = LocalDateTime.now(Clock.systemDefaultZone()); + LocalDateTime test = LocalDateTime.now(); + long diff = Math.abs(test.getTime().toNanoOfDay() - expected.getTime().toNanoOfDay()); + if (diff >= 100000000) { + // may be date change + expected = LocalDateTime.now(Clock.systemDefaultZone()); + test = LocalDateTime.now(); + diff = Math.abs(test.getTime().toNanoOfDay() - expected.getTime().toNanoOfDay()); + } + assertTrue(diff < 100000000); // less than 0.1 secs + } + + //----------------------------------------------------------------------- + // now(ZoneId) + //----------------------------------------------------------------------- + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void now_ZoneId_nullZoneId() { + LocalDateTime.now((ZoneId) null); + } + + @Test(groups={"tck"}) + public void now_ZoneId() { + ZoneId zone = ZoneId.of("UTC+01:02:03"); + LocalDateTime expected = LocalDateTime.now(Clock.system(zone)); + LocalDateTime test = LocalDateTime.now(zone); + for (int i = 0; i < 100; i++) { + if (expected.equals(test)) { + return; + } + expected = LocalDateTime.now(Clock.system(zone)); + test = LocalDateTime.now(zone); + } + assertEquals(test, expected); + } + + //----------------------------------------------------------------------- + // now(Clock) + //----------------------------------------------------------------------- + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void now_Clock_nullClock() { + LocalDateTime.now((Clock) null); + } + + @Test(groups={"tck"}) + public void now_Clock_allSecsInDay_utc() { + for (int i = 0; i < (2 * 24 * 60 * 60); i++) { + Instant instant = Instant.ofEpochSecond(i).plusNanos(123456789L); + Clock clock = Clock.fixed(instant, ZoneOffset.UTC); + LocalDateTime test = LocalDateTime.now(clock); + assertEquals(test.getYear(), 1970); + assertEquals(test.getMonth(), Month.JANUARY); + assertEquals(test.getDayOfMonth(), (i < 24 * 60 * 60 ? 1 : 2)); + assertEquals(test.getHour(), (i / (60 * 60)) % 24); + assertEquals(test.getMinute(), (i / 60) % 60); + assertEquals(test.getSecond(), i % 60); + assertEquals(test.getNano(), 123456789); + } + } + + @Test(groups={"tck"}) + public void now_Clock_allSecsInDay_offset() { + for (int i = 0; i < (2 * 24 * 60 * 60); i++) { + Instant instant = Instant.ofEpochSecond(i).plusNanos(123456789L); + Clock clock = Clock.fixed(instant.minusSeconds(OFFSET_PONE.getTotalSeconds()), OFFSET_PONE); + LocalDateTime test = LocalDateTime.now(clock); + assertEquals(test.getYear(), 1970); + assertEquals(test.getMonth(), Month.JANUARY); + assertEquals(test.getDayOfMonth(), (i < 24 * 60 * 60) ? 1 : 2); + assertEquals(test.getHour(), (i / (60 * 60)) % 24); + assertEquals(test.getMinute(), (i / 60) % 60); + assertEquals(test.getSecond(), i % 60); + assertEquals(test.getNano(), 123456789); + } + } + + @Test(groups={"tck"}) + public void now_Clock_allSecsInDay_beforeEpoch() { + LocalTime expected = LocalTime.MIDNIGHT.plusNanos(123456789L); + for (int i =-1; i >= -(24 * 60 * 60); i--) { + Instant instant = Instant.ofEpochSecond(i).plusNanos(123456789L); + Clock clock = Clock.fixed(instant, ZoneOffset.UTC); + LocalDateTime test = LocalDateTime.now(clock); + assertEquals(test.getYear(), 1969); + assertEquals(test.getMonth(), Month.DECEMBER); + assertEquals(test.getDayOfMonth(), 31); + expected = expected.minusSeconds(1); + assertEquals(test.getTime(), expected); + } + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void now_Clock_maxYear() { + Clock clock = Clock.fixed(MAX_INSTANT, ZoneOffset.UTC); + LocalDateTime test = LocalDateTime.now(clock); + assertEquals(test, MAX_DATE_TIME); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void now_Clock_tooBig() { + Clock clock = Clock.fixed(MAX_INSTANT.plusSeconds(24 * 60 * 60), ZoneOffset.UTC); + LocalDateTime.now(clock); + } + + @Test(groups={"tck"}) + public void now_Clock_minYear() { + Clock clock = Clock.fixed(MIN_INSTANT, ZoneOffset.UTC); + LocalDateTime test = LocalDateTime.now(clock); + assertEquals(test, MIN_DATE_TIME); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void now_Clock_tooLow() { + Clock clock = Clock.fixed(MIN_INSTANT.minusNanos(1), ZoneOffset.UTC); + LocalDateTime.now(clock); + } + + //----------------------------------------------------------------------- + // of() factories + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_of_4intsMonth() { + LocalDateTime dateTime = LocalDateTime.of(2007, Month.JULY, 15, 12, 30); + check(dateTime, 2007, 7, 15, 12, 30, 0, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_4intsMonth_yearTooLow() { + LocalDateTime.of(Integer.MIN_VALUE, Month.JULY, 15, 12, 30); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_of_4intsMonth_nullMonth() { + LocalDateTime.of(2007, null, 15, 12, 30); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_4intsMonth_dayTooLow() { + LocalDateTime.of(2007, Month.JULY, -1, 12, 30); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_4intsMonth_dayTooHigh() { + LocalDateTime.of(2007, Month.JULY, 32, 12, 30); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_4intsMonth_hourTooLow() { + LocalDateTime.of(2007, Month.JULY, 15, -1, 30); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_4intsMonth_hourTooHigh() { + LocalDateTime.of(2007, Month.JULY, 15, 24, 30); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_4intsMonth_minuteTooLow() { + LocalDateTime.of(2007, Month.JULY, 15, 12, -1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_4intsMonth_minuteTooHigh() { + LocalDateTime.of(2007, Month.JULY, 15, 12, 60); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_of_5intsMonth() { + LocalDateTime dateTime = LocalDateTime.of(2007, Month.JULY, 15, 12, 30, 40); + check(dateTime, 2007, 7, 15, 12, 30, 40, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_5intsMonth_yearTooLow() { + LocalDateTime.of(Integer.MIN_VALUE, Month.JULY, 15, 12, 30, 40); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_of_5intsMonth_nullMonth() { + LocalDateTime.of(2007, null, 15, 12, 30, 40); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_5intsMonth_dayTooLow() { + LocalDateTime.of(2007, Month.JULY, -1, 12, 30, 40); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_5intsMonth_dayTooHigh() { + LocalDateTime.of(2007, Month.JULY, 32, 12, 30, 40); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_5intsMonth_hourTooLow() { + LocalDateTime.of(2007, Month.JULY, 15, -1, 30, 40); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_5intsMonth_hourTooHigh() { + LocalDateTime.of(2007, Month.JULY, 15, 24, 30, 40); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_5intsMonth_minuteTooLow() { + LocalDateTime.of(2007, Month.JULY, 15, 12, -1, 40); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_5intsMonth_minuteTooHigh() { + LocalDateTime.of(2007, Month.JULY, 15, 12, 60, 40); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_5intsMonth_secondTooLow() { + LocalDateTime.of(2007, Month.JULY, 15, 12, 30, -1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_5intsMonth_secondTooHigh() { + LocalDateTime.of(2007, Month.JULY, 15, 12, 30, 60); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_of_6intsMonth() { + LocalDateTime dateTime = LocalDateTime.of(2007, Month.JULY, 15, 12, 30, 40, 987654321); + check(dateTime, 2007, 7, 15, 12, 30, 40, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_6intsMonth_yearTooLow() { + LocalDateTime.of(Integer.MIN_VALUE, Month.JULY, 15, 12, 30, 40, 987654321); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_of_6intsMonth_nullMonth() { + LocalDateTime.of(2007, null, 15, 12, 30, 40, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_6intsMonth_dayTooLow() { + LocalDateTime.of(2007, Month.JULY, -1, 12, 30, 40, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_6intsMonth_dayTooHigh() { + LocalDateTime.of(2007, Month.JULY, 32, 12, 30, 40, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_6intsMonth_hourTooLow() { + LocalDateTime.of(2007, Month.JULY, 15, -1, 30, 40, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_6intsMonth_hourTooHigh() { + LocalDateTime.of(2007, Month.JULY, 15, 24, 30, 40, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_6intsMonth_minuteTooLow() { + LocalDateTime.of(2007, Month.JULY, 15, 12, -1, 40, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_6intsMonth_minuteTooHigh() { + LocalDateTime.of(2007, Month.JULY, 15, 12, 60, 40, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_6intsMonth_secondTooLow() { + LocalDateTime.of(2007, Month.JULY, 15, 12, 30, -1, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_6intsMonth_secondTooHigh() { + LocalDateTime.of(2007, Month.JULY, 15, 12, 30, 60, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_6intsMonth_nanoTooLow() { + LocalDateTime.of(2007, Month.JULY, 15, 12, 30, 40, -1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_6intsMonth_nanoTooHigh() { + LocalDateTime.of(2007, Month.JULY, 15, 12, 30, 40, 1000000000); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_of_5ints() { + LocalDateTime dateTime = LocalDateTime.of(2007, 7, 15, 12, 30); + check(dateTime, 2007, 7, 15, 12, 30, 0, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_5ints_yearTooLow() { + LocalDateTime.of(Integer.MIN_VALUE, 7, 15, 12, 30); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_5ints_monthTooLow() { + LocalDateTime.of(2007, 0, 15, 12, 30); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_5ints_monthTooHigh() { + LocalDateTime.of(2007, 13, 15, 12, 30); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_5ints_dayTooLow() { + LocalDateTime.of(2007, 7, -1, 12, 30); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_5ints_dayTooHigh() { + LocalDateTime.of(2007, 7, 32, 12, 30); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_5ints_hourTooLow() { + LocalDateTime.of(2007, 7, 15, -1, 30); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_5ints_hourTooHigh() { + LocalDateTime.of(2007, 7, 15, 24, 30); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_5ints_minuteTooLow() { + LocalDateTime.of(2007, 7, 15, 12, -1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_5ints_minuteTooHigh() { + LocalDateTime.of(2007, 7, 15, 12, 60); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_of_6ints() { + LocalDateTime dateTime = LocalDateTime.of(2007, 7, 15, 12, 30, 40); + check(dateTime, 2007, 7, 15, 12, 30, 40, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_6ints_yearTooLow() { + LocalDateTime.of(Integer.MIN_VALUE, 7, 15, 12, 30, 40); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_6ints_monthTooLow() { + LocalDateTime.of(2007, 0, 15, 12, 30, 40); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_6ints_monthTooHigh() { + LocalDateTime.of(2007, 13, 15, 12, 30, 40); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_6ints_dayTooLow() { + LocalDateTime.of(2007, 7, -1, 12, 30, 40); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_6ints_dayTooHigh() { + LocalDateTime.of(2007, 7, 32, 12, 30, 40); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_6ints_hourTooLow() { + LocalDateTime.of(2007, 7, 15, -1, 30, 40); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_6ints_hourTooHigh() { + LocalDateTime.of(2007, 7, 15, 24, 30, 40); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_6ints_minuteTooLow() { + LocalDateTime.of(2007, 7, 15, 12, -1, 40); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_6ints_minuteTooHigh() { + LocalDateTime.of(2007, 7, 15, 12, 60, 40); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_6ints_secondTooLow() { + LocalDateTime.of(2007, 7, 15, 12, 30, -1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_6ints_secondTooHigh() { + LocalDateTime.of(2007, 7, 15, 12, 30, 60); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_of_7ints() { + LocalDateTime dateTime = LocalDateTime.of(2007, 7, 15, 12, 30, 40, 987654321); + check(dateTime, 2007, 7, 15, 12, 30, 40, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_7ints_yearTooLow() { + LocalDateTime.of(Integer.MIN_VALUE, 7, 15, 12, 30, 40, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_7ints_monthTooLow() { + LocalDateTime.of(2007, 0, 15, 12, 30, 40, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_7ints_monthTooHigh() { + LocalDateTime.of(2007, 13, 15, 12, 30, 40, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_7ints_dayTooLow() { + LocalDateTime.of(2007, 7, -1, 12, 30, 40, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_7ints_dayTooHigh() { + LocalDateTime.of(2007, 7, 32, 12, 30, 40, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_7ints_hourTooLow() { + LocalDateTime.of(2007, 7, 15, -1, 30, 40, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_7ints_hourTooHigh() { + LocalDateTime.of(2007, 7, 15, 24, 30, 40, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_7ints_minuteTooLow() { + LocalDateTime.of(2007, 7, 15, 12, -1, 40, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_7ints_minuteTooHigh() { + LocalDateTime.of(2007, 7, 15, 12, 60, 40, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_7ints_secondTooLow() { + LocalDateTime.of(2007, 7, 15, 12, 30, -1, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_7ints_secondTooHigh() { + LocalDateTime.of(2007, 7, 15, 12, 30, 60, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_7ints_nanoTooLow() { + LocalDateTime.of(2007, 7, 15, 12, 30, 40, -1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_7ints_nanoTooHigh() { + LocalDateTime.of(2007, 7, 15, 12, 30, 40, 1000000000); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_of_LocalDate_LocalTime() { + LocalDateTime dateTime = LocalDateTime.of(LocalDate.of(2007, 7, 15), LocalTime.of(12, 30, 40, 987654321)); + check(dateTime, 2007, 7, 15, 12, 30, 40, 987654321); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_of_LocalDate_LocalTime_nullLocalDate() { + LocalDateTime.of(null, LocalTime.of(12, 30, 40, 987654321)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_of_LocalDate_LocalTime_nullLocalTime() { + LocalDateTime.of(LocalDate.of(2007, 7, 15), null); + } + + //----------------------------------------------------------------------- + // ofInstant() + //----------------------------------------------------------------------- + @DataProvider(name="instantFactory") + Object[][] data_instantFactory() { + return new Object[][] { + {Instant.ofEpochSecond(86400 + 3600 + 120 + 4, 500), ZONE_PARIS, LocalDateTime.of(1970, 1, 2, 2, 2, 4, 500)}, + {Instant.ofEpochSecond(86400 + 3600 + 120 + 4, 500), OFFSET_MTWO, LocalDateTime.of(1970, 1, 1, 23, 2, 4, 500)}, + {Instant.ofEpochSecond(-86400 + 4, 500), OFFSET_PTWO, LocalDateTime.of(1969, 12, 31, 2, 0, 4, 500)}, + {OffsetDateTime.of(LocalDateTime.of(Year.MIN_VALUE, 1, 1, 0, 0), ZoneOffset.UTC).toInstant(), + ZoneOffset.UTC, LocalDateTime.MIN}, + {OffsetDateTime.of(LocalDateTime.of(Year.MAX_VALUE, 12, 31, 23, 59, 59, 999_999_999), ZoneOffset.UTC).toInstant(), + ZoneOffset.UTC, LocalDateTime.MAX}, + }; + } + + @Test(dataProvider="instantFactory") + public void factory_ofInstant(Instant instant, ZoneId zone, LocalDateTime expected) { + LocalDateTime test = LocalDateTime.ofInstant(instant, zone); + assertEquals(test, expected); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofInstant_instantTooBig() { + LocalDateTime.ofInstant(Instant.MAX, OFFSET_PONE) ; + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofInstant_instantTooSmall() { + LocalDateTime.ofInstant(Instant.MIN, OFFSET_PONE) ; + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_ofInstant_nullInstant() { + LocalDateTime.ofInstant((Instant) null, ZONE_GAZA); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_ofInstant_nullZone() { + LocalDateTime.ofInstant(Instant.EPOCH, (ZoneId) null); + } + + //----------------------------------------------------------------------- + // ofEpochSecond() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_ofEpochSecond_longOffset_afterEpoch() { + LocalDateTime base = LocalDateTime.of(1970, 1, 1, 2, 0, 0, 500); + for (int i = 0; i < 100000; i++) { + LocalDateTime test = LocalDateTime.ofEpochSecond(i, 500, OFFSET_PTWO); + assertEquals(test, base.plusSeconds(i)); + } + } + + @Test(groups={"tck"}) + public void factory_ofEpochSecond_longOffset_beforeEpoch() { + LocalDateTime base = LocalDateTime.of(1970, 1, 1, 2, 0, 0, 500); + for (int i = 0; i < 100000; i++) { + LocalDateTime test = LocalDateTime.ofEpochSecond(-i, 500, OFFSET_PTWO); + assertEquals(test, base.minusSeconds(i)); + } + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofEpochSecond_longOffset_tooBig() { + LocalDateTime.ofEpochSecond(Long.MAX_VALUE, 500, OFFSET_PONE); // TODO: better test + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofEpochSecond_longOffset_tooSmall() { + LocalDateTime.ofEpochSecond(Long.MIN_VALUE, 500, OFFSET_PONE); // TODO: better test + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofEpochSecond_badNanos_toBig() { + LocalDateTime.ofEpochSecond(0, 1_000_000_000, OFFSET_PONE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofEpochSecond_badNanos_toSmall() { + LocalDateTime.ofEpochSecond(0, -1, OFFSET_PONE); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_ofEpochSecond_longOffset_nullOffset() { + LocalDateTime.ofEpochSecond(0L, 500, null); + } + + //----------------------------------------------------------------------- + // from() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_from_TemporalAccessor() { + LocalDateTime base = LocalDateTime.of(2007, 7, 15, 17, 30); + assertEquals(LocalDateTime.from(base), base); + assertEquals(LocalDateTime.from(ZonedDateTime.of(base, ZoneOffset.ofHours(2))), base); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_from_TemporalAccessor_invalid_noDerive() { + LocalDateTime.from(LocalTime.of(12, 30)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_from_TemporalAccessor_null() { + LocalDateTime.from((TemporalAccessor) null); + } + + //----------------------------------------------------------------------- + // parse() + //----------------------------------------------------------------------- + @Test(dataProvider="sampleToString", groups={"tck"}) + public void test_parse(int y, int month, int d, int h, int m, int s, int n, String text) { + LocalDateTime t = LocalDateTime.parse(text); + assertEquals(t.getYear(), y); + assertEquals(t.getMonth().getValue(), month); + assertEquals(t.getDayOfMonth(), d); + assertEquals(t.getHour(), h); + assertEquals(t.getMinute(), m); + assertEquals(t.getSecond(), s); + assertEquals(t.getNano(), n); + } + + @Test(expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_illegalValue() { + LocalDateTime.parse("2008-06-32T11:15"); + } + + @Test(expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_invalidValue() { + LocalDateTime.parse("2008-06-31T11:15"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_nullText() { + LocalDateTime.parse((String) null); + } + + //----------------------------------------------------------------------- + // parse(DateTimeFormatter) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_parse_formatter() { + DateTimeFormatter f = DateTimeFormatters.pattern("y M d H m s"); + LocalDateTime test = LocalDateTime.parse("2010 12 3 11 30 45", f); + assertEquals(test, LocalDateTime.of(2010, 12, 3, 11, 30, 45)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_formatter_nullText() { + DateTimeFormatter f = DateTimeFormatters.pattern("y M d H m s"); + LocalDateTime.parse((String) null, f); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_formatter_nullFormatter() { + LocalDateTime.parse("ANY", null); + } + + //----------------------------------------------------------------------- + // get(TemporalField) + //----------------------------------------------------------------------- + @Test + public void test_get_TemporalField() { + LocalDateTime test = LocalDateTime.of(2008, 6, 30, 12, 30, 40, 987654321); + assertEquals(test.get(ChronoField.YEAR), 2008); + assertEquals(test.get(ChronoField.MONTH_OF_YEAR), 6); + assertEquals(test.get(ChronoField.DAY_OF_MONTH), 30); + assertEquals(test.get(ChronoField.DAY_OF_WEEK), 1); + assertEquals(test.get(ChronoField.DAY_OF_YEAR), 182); + + assertEquals(test.get(ChronoField.HOUR_OF_DAY), 12); + assertEquals(test.get(ChronoField.MINUTE_OF_HOUR), 30); + assertEquals(test.get(ChronoField.SECOND_OF_MINUTE), 40); + assertEquals(test.get(ChronoField.NANO_OF_SECOND), 987654321); + assertEquals(test.get(ChronoField.HOUR_OF_AMPM), 0); + assertEquals(test.get(ChronoField.AMPM_OF_DAY), 1); + } + + @Test + public void test_getLong_TemporalField() { + LocalDateTime test = LocalDateTime.of(2008, 6, 30, 12, 30, 40, 987654321); + assertEquals(test.getLong(ChronoField.YEAR), 2008); + assertEquals(test.getLong(ChronoField.MONTH_OF_YEAR), 6); + assertEquals(test.getLong(ChronoField.DAY_OF_MONTH), 30); + assertEquals(test.getLong(ChronoField.DAY_OF_WEEK), 1); + assertEquals(test.getLong(ChronoField.DAY_OF_YEAR), 182); + + assertEquals(test.getLong(ChronoField.HOUR_OF_DAY), 12); + assertEquals(test.getLong(ChronoField.MINUTE_OF_HOUR), 30); + assertEquals(test.getLong(ChronoField.SECOND_OF_MINUTE), 40); + assertEquals(test.getLong(ChronoField.NANO_OF_SECOND), 987654321); + assertEquals(test.getLong(ChronoField.HOUR_OF_AMPM), 0); + assertEquals(test.getLong(ChronoField.AMPM_OF_DAY), 1); + } + + //----------------------------------------------------------------------- + // query(TemporalQuery) + //----------------------------------------------------------------------- + @Test + public void test_query_chrono() { + assertEquals(TEST_2007_07_15_12_30_40_987654321.query(Queries.chrono()), ISOChrono.INSTANCE); + assertEquals(Queries.chrono().queryFrom(TEST_2007_07_15_12_30_40_987654321), ISOChrono.INSTANCE); + } + + @Test + public void test_query_zoneId() { + assertEquals(TEST_2007_07_15_12_30_40_987654321.query(Queries.zoneId()), null); + assertEquals(Queries.zoneId().queryFrom(TEST_2007_07_15_12_30_40_987654321), null); + } + + @Test + public void test_query_precision() { + assertEquals(TEST_2007_07_15_12_30_40_987654321.query(Queries.precision()), NANOS); + assertEquals(Queries.precision().queryFrom(TEST_2007_07_15_12_30_40_987654321), NANOS); + } + + @Test + public void test_query_offset() { + assertEquals(TEST_2007_07_15_12_30_40_987654321.query(Queries.offset()), null); + assertEquals(Queries.offset().queryFrom(TEST_2007_07_15_12_30_40_987654321), null); + } + + @Test + public void test_query_zone() { + assertEquals(TEST_2007_07_15_12_30_40_987654321.query(Queries.zone()), null); + assertEquals(Queries.zone().queryFrom(TEST_2007_07_15_12_30_40_987654321), null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_query_null() { + TEST_2007_07_15_12_30_40_987654321.query(null); + } + + //----------------------------------------------------------------------- + @DataProvider(name="sampleDates") + Object[][] provider_sampleDates() { + return new Object[][] { + {2008, 7, 5}, + {2007, 7, 5}, + {2006, 7, 5}, + {2005, 7, 5}, + {2004, 1, 1}, + {-1, 1, 2}, + }; + } + + @DataProvider(name="sampleTimes") + Object[][] provider_sampleTimes() { + return new Object[][] { + {0, 0, 0, 0}, + {0, 0, 0, 1}, + {0, 0, 1, 0}, + {0, 0, 1, 1}, + {0, 1, 0, 0}, + {0, 1, 0, 1}, + {0, 1, 1, 0}, + {0, 1, 1, 1}, + {1, 0, 0, 0}, + {1, 0, 0, 1}, + {1, 0, 1, 0}, + {1, 0, 1, 1}, + {1, 1, 0, 0}, + {1, 1, 0, 1}, + {1, 1, 1, 0}, + {1, 1, 1, 1}, + }; + } + + //----------------------------------------------------------------------- + // get*() + //----------------------------------------------------------------------- + @Test(dataProvider="sampleDates", groups={"tck"}) + public void test_get_dates(int y, int m, int d) { + LocalDateTime a = LocalDateTime.of(y, m, d, 12, 30); + assertEquals(a.getYear(), y); + assertEquals(a.getMonth(), Month.of(m)); + assertEquals(a.getDayOfMonth(), d); + } + + @Test(dataProvider="sampleDates", groups={"tck"}) + public void test_getDOY(int y, int m, int d) { + LocalDateTime a = LocalDateTime.of(y, m, d, 12 ,30); + int total = 0; + for (int i = 1; i < m; i++) { + total += Month.of(i).length(isIsoLeap(y)); + } + int doy = total + d; + assertEquals(a.getDayOfYear(), doy); + } + + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_get_times(int h, int m, int s, int ns) { + LocalDateTime a = LocalDateTime.of(TEST_2007_07_15_12_30_40_987654321.getDate(), LocalTime.of(h, m, s, ns)); + assertEquals(a.getHour(), h); + assertEquals(a.getMinute(), m); + assertEquals(a.getSecond(), s); + assertEquals(a.getNano(), ns); + } + + //----------------------------------------------------------------------- + // getDayOfWeek() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_getDayOfWeek() { + DayOfWeek dow = DayOfWeek.MONDAY; + for (Month month : Month.values()) { + int length = month.length(false); + for (int i = 1; i <= length; i++) { + LocalDateTime d = LocalDateTime.of(LocalDate.of(2007, month, i), + TEST_2007_07_15_12_30_40_987654321.getTime()); + assertSame(d.getDayOfWeek(), dow); + dow = dow.plus(1); + } + } + } + + //----------------------------------------------------------------------- + // with() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_with_adjustment() { + final LocalDateTime sample = LocalDateTime.of(2012, 3, 4, 23, 5); + TemporalAdjuster adjuster = new TemporalAdjuster() { + @Override + public Temporal adjustInto(Temporal dateTime) { + return sample; + } + }; + assertEquals(TEST_2007_07_15_12_30_40_987654321.with(adjuster), sample); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_with_adjustment_null() { + TEST_2007_07_15_12_30_40_987654321.with((TemporalAdjuster) null); + } + + //----------------------------------------------------------------------- + // withYear() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withYear_int_normal() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.withYear(2008); + check(t, 2008, 7, 15, 12, 30, 40, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withYear_int_invalid() { + TEST_2007_07_15_12_30_40_987654321.withYear(Year.MIN_VALUE - 1); + } + + @Test(groups={"tck"}) + public void test_withYear_int_adjustDay() { + LocalDateTime t = LocalDateTime.of(2008, 2, 29, 12, 30).withYear(2007); + LocalDateTime expected = LocalDateTime.of(2007, 2, 28, 12, 30); + assertEquals(t, expected); + } + + //----------------------------------------------------------------------- + // withMonth() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withMonth_int_normal() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.withMonth(1); + check(t, 2007, 1, 15, 12, 30, 40, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withMonth_int_invalid() { + TEST_2007_07_15_12_30_40_987654321.withMonth(13); + } + + @Test(groups={"tck"}) + public void test_withMonth_int_adjustDay() { + LocalDateTime t = LocalDateTime.of(2007, 12, 31, 12, 30).withMonth(11); + LocalDateTime expected = LocalDateTime.of(2007, 11, 30, 12, 30); + assertEquals(t, expected); + } + + //----------------------------------------------------------------------- + // withDayOfMonth() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withDayOfMonth_normal() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.withDayOfMonth(1); + check(t, 2007, 7, 1, 12, 30, 40, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withDayOfMonth_invalid() { + LocalDateTime.of(2007, 11, 30, 12, 30).withDayOfMonth(32); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withDayOfMonth_invalidCombination() { + LocalDateTime.of(2007, 11, 30, 12, 30).withDayOfMonth(31); + } + + //----------------------------------------------------------------------- + // withDayOfYear(int) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withDayOfYear_normal() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.withDayOfYear(33); + assertEquals(t, LocalDateTime.of(2007, 2, 2, 12, 30, 40, 987654321)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withDayOfYear_illegal() { + TEST_2007_07_15_12_30_40_987654321.withDayOfYear(367); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withDayOfYear_invalid() { + TEST_2007_07_15_12_30_40_987654321.withDayOfYear(366); + } + + //----------------------------------------------------------------------- + // withHour() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withHour_normal() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321; + for (int i = 0; i < 24; i++) { + t = t.withHour(i); + assertEquals(t.getHour(), i); + } + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withHour_hourTooLow() { + TEST_2007_07_15_12_30_40_987654321.withHour(-1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withHour_hourTooHigh() { + TEST_2007_07_15_12_30_40_987654321.withHour(24); + } + + //----------------------------------------------------------------------- + // withMinute() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withMinute_normal() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321; + for (int i = 0; i < 60; i++) { + t = t.withMinute(i); + assertEquals(t.getMinute(), i); + } + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withMinute_minuteTooLow() { + TEST_2007_07_15_12_30_40_987654321.withMinute(-1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withMinute_minuteTooHigh() { + TEST_2007_07_15_12_30_40_987654321.withMinute(60); + } + + //----------------------------------------------------------------------- + // withSecond() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withSecond_normal() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321; + for (int i = 0; i < 60; i++) { + t = t.withSecond(i); + assertEquals(t.getSecond(), i); + } + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withSecond_secondTooLow() { + TEST_2007_07_15_12_30_40_987654321.withSecond(-1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withSecond_secondTooHigh() { + TEST_2007_07_15_12_30_40_987654321.withSecond(60); + } + + //----------------------------------------------------------------------- + // withNano() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withNanoOfSecond_normal() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321; + t = t.withNano(1); + assertEquals(t.getNano(), 1); + t = t.withNano(10); + assertEquals(t.getNano(), 10); + t = t.withNano(100); + assertEquals(t.getNano(), 100); + t = t.withNano(999999999); + assertEquals(t.getNano(), 999999999); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withNanoOfSecond_nanoTooLow() { + TEST_2007_07_15_12_30_40_987654321.withNano(-1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withNanoOfSecond_nanoTooHigh() { + TEST_2007_07_15_12_30_40_987654321.withNano(1000000000); + } + + //----------------------------------------------------------------------- + // truncatedTo(TemporalUnit) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_truncatedTo_normal() { + assertEquals(TEST_2007_07_15_12_30_40_987654321.truncatedTo(NANOS), TEST_2007_07_15_12_30_40_987654321); + assertEquals(TEST_2007_07_15_12_30_40_987654321.truncatedTo(SECONDS), TEST_2007_07_15_12_30_40_987654321.withNano(0)); + assertEquals(TEST_2007_07_15_12_30_40_987654321.truncatedTo(DAYS), TEST_2007_07_15_12_30_40_987654321.with(LocalTime.MIDNIGHT)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_truncatedTo_null() { + TEST_2007_07_15_12_30_40_987654321.truncatedTo(null); + } + + //----------------------------------------------------------------------- + // plus(adjuster) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plus_adjuster() { + Period p = Period.ofTime(0, 0, 62, 3); + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plus(p); + assertEquals(t, LocalDateTime.of(2007, 7, 15, 12, 31, 42, 987654324)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_plus_adjuster_null() { + TEST_2007_07_15_12_30_40_987654321.plus((TemporalAdder) null); + } + + //----------------------------------------------------------------------- + // plus(Period) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plus_Period_positiveMonths() { + MockSimplePeriod period = MockSimplePeriod.of(7, ChronoUnit.MONTHS); + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plus(period); + assertEquals(t, LocalDateTime.of(2008, 2, 15, 12, 30, 40, 987654321)); + } + + @Test(groups={"tck"}) + public void test_plus_Period_negativeDays() { + MockSimplePeriod period = MockSimplePeriod.of(-25, ChronoUnit.DAYS); + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plus(period); + assertEquals(t, LocalDateTime.of(2007, 6, 20, 12, 30, 40, 987654321)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_plus_Period_null() { + TEST_2007_07_15_12_30_40_987654321.plus((MockSimplePeriod) null); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plus_Period_invalidTooLarge() { + MockSimplePeriod period = MockSimplePeriod.of(1, ChronoUnit.YEARS); + LocalDateTime.of(Year.MAX_VALUE, 1, 1, 0, 0).plus(period); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plus_Period_invalidTooSmall() { + MockSimplePeriod period = MockSimplePeriod.of(-1, ChronoUnit.YEARS); + LocalDateTime.of(Year.MIN_VALUE, 1, 1, 0, 0).plus(period); + } + + //----------------------------------------------------------------------- + // plus(long,TemporalUnit) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plus_longTemporalUnit_positiveMonths() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plus(7, ChronoUnit.MONTHS); + assertEquals(t, LocalDateTime.of(2008, 2, 15, 12, 30, 40, 987654321)); + } + + @Test(groups={"tck"}) + public void test_plus_longTemporalUnit_negativeDays() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plus(-25, ChronoUnit.DAYS); + assertEquals(t, LocalDateTime.of(2007, 6, 20, 12, 30, 40, 987654321)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_plus_longTemporalUnit_null() { + TEST_2007_07_15_12_30_40_987654321.plus(1, (TemporalUnit) null); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plus_longTemporalUnit_invalidTooLarge() { + LocalDateTime.of(Year.MAX_VALUE, 1, 1, 0, 0).plus(1, ChronoUnit.YEARS); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plus_longTemporalUnit_invalidTooSmall() { + LocalDateTime.of(Year.MIN_VALUE, 1, 1, 0, 0).plus(-1, ChronoUnit.YEARS); + } + + //----------------------------------------------------------------------- + // plusYears() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusYears_int_normal() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusYears(1); + check(t, 2008, 7, 15, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_plusYears_int_negative() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusYears(-1); + check(t, 2006, 7, 15, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_plusYears_int_adjustDay() { + LocalDateTime t = createDateMidnight(2008, 2, 29).plusYears(1); + check(t, 2009, 2, 28, 0, 0, 0, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusYears_int_invalidTooLarge() { + createDateMidnight(Year.MAX_VALUE, 1, 1).plusYears(1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusYears_int_invalidTooSmall() { + LocalDate.of(Year.MIN_VALUE, 1, 1).plusYears(-1); + } + + //----------------------------------------------------------------------- + // plusMonths() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusMonths_int_normal() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusMonths(1); + check(t, 2007, 8, 15, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_plusMonths_int_overYears() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusMonths(25); + check(t, 2009, 8, 15, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_plusMonths_int_negative() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusMonths(-1); + check(t, 2007, 6, 15, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_plusMonths_int_negativeAcrossYear() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusMonths(-7); + check(t, 2006, 12, 15, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_plusMonths_int_negativeOverYears() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusMonths(-31); + check(t, 2004, 12, 15, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_plusMonths_int_adjustDayFromLeapYear() { + LocalDateTime t = createDateMidnight(2008, 2, 29).plusMonths(12); + check(t, 2009, 2, 28, 0, 0, 0, 0); + } + + @Test(groups={"tck"}) + public void test_plusMonths_int_adjustDayFromMonthLength() { + LocalDateTime t = createDateMidnight(2007, 3, 31).plusMonths(1); + check(t, 2007, 4, 30, 0, 0, 0, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusMonths_int_invalidTooLarge() { + createDateMidnight(Year.MAX_VALUE, 12, 1).plusMonths(1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusMonths_int_invalidTooSmall() { + createDateMidnight(Year.MIN_VALUE, 1, 1).plusMonths(-1); + } + + //----------------------------------------------------------------------- + // plusWeeks() + //----------------------------------------------------------------------- + @DataProvider(name="samplePlusWeeksSymmetry") + Object[][] provider_samplePlusWeeksSymmetry() { + return new Object[][] { + {createDateMidnight(-1, 1, 1)}, + {createDateMidnight(-1, 2, 28)}, + {createDateMidnight(-1, 3, 1)}, + {createDateMidnight(-1, 12, 31)}, + {createDateMidnight(0, 1, 1)}, + {createDateMidnight(0, 2, 28)}, + {createDateMidnight(0, 2, 29)}, + {createDateMidnight(0, 3, 1)}, + {createDateMidnight(0, 12, 31)}, + {createDateMidnight(2007, 1, 1)}, + {createDateMidnight(2007, 2, 28)}, + {createDateMidnight(2007, 3, 1)}, + {createDateMidnight(2007, 12, 31)}, + {createDateMidnight(2008, 1, 1)}, + {createDateMidnight(2008, 2, 28)}, + {createDateMidnight(2008, 2, 29)}, + {createDateMidnight(2008, 3, 1)}, + {createDateMidnight(2008, 12, 31)}, + {createDateMidnight(2099, 1, 1)}, + {createDateMidnight(2099, 2, 28)}, + {createDateMidnight(2099, 3, 1)}, + {createDateMidnight(2099, 12, 31)}, + {createDateMidnight(2100, 1, 1)}, + {createDateMidnight(2100, 2, 28)}, + {createDateMidnight(2100, 3, 1)}, + {createDateMidnight(2100, 12, 31)}, + }; + } + + @Test(dataProvider="samplePlusWeeksSymmetry", groups={"tck"}) + public void test_plusWeeks_symmetry(LocalDateTime reference) { + for (int weeks = 0; weeks < 365 * 8; weeks++) { + LocalDateTime t = reference.plusWeeks(weeks).plusWeeks(-weeks); + assertEquals(t, reference); + + t = reference.plusWeeks(-weeks).plusWeeks(weeks); + assertEquals(t, reference); + } + } + + @Test(groups={"tck"}) + public void test_plusWeeks_normal() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusWeeks(1); + check(t, 2007, 7, 22, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_overMonths() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusWeeks(9); + check(t, 2007, 9, 16, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_overYears() { + LocalDateTime t = LocalDateTime.of(2006, 7, 16, 12, 30, 40, 987654321).plusWeeks(52); + assertEquals(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_overLeapYears() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusYears(-1).plusWeeks(104); + check(t, 2008, 7, 12, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_negative() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusWeeks(-1); + check(t, 2007, 7, 8, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_negativeAcrossYear() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusWeeks(-28); + check(t, 2006, 12, 31, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_negativeOverYears() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusWeeks(-104); + check(t, 2005, 7, 17, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_maximum() { + LocalDateTime t = createDateMidnight(Year.MAX_VALUE, 12, 24).plusWeeks(1); + check(t, Year.MAX_VALUE, 12, 31, 0, 0, 0, 0); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_minimum() { + LocalDateTime t = createDateMidnight(Year.MIN_VALUE, 1, 8).plusWeeks(-1); + check(t, Year.MIN_VALUE, 1, 1, 0, 0, 0, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusWeeks_invalidTooLarge() { + createDateMidnight(Year.MAX_VALUE, 12, 25).plusWeeks(1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusWeeks_invalidTooSmall() { + createDateMidnight(Year.MIN_VALUE, 1, 7).plusWeeks(-1); + } + + //----------------------------------------------------------------------- + // plusDays() + //----------------------------------------------------------------------- + @DataProvider(name="samplePlusDaysSymmetry") + Object[][] provider_samplePlusDaysSymmetry() { + return new Object[][] { + {createDateMidnight(-1, 1, 1)}, + {createDateMidnight(-1, 2, 28)}, + {createDateMidnight(-1, 3, 1)}, + {createDateMidnight(-1, 12, 31)}, + {createDateMidnight(0, 1, 1)}, + {createDateMidnight(0, 2, 28)}, + {createDateMidnight(0, 2, 29)}, + {createDateMidnight(0, 3, 1)}, + {createDateMidnight(0, 12, 31)}, + {createDateMidnight(2007, 1, 1)}, + {createDateMidnight(2007, 2, 28)}, + {createDateMidnight(2007, 3, 1)}, + {createDateMidnight(2007, 12, 31)}, + {createDateMidnight(2008, 1, 1)}, + {createDateMidnight(2008, 2, 28)}, + {createDateMidnight(2008, 2, 29)}, + {createDateMidnight(2008, 3, 1)}, + {createDateMidnight(2008, 12, 31)}, + {createDateMidnight(2099, 1, 1)}, + {createDateMidnight(2099, 2, 28)}, + {createDateMidnight(2099, 3, 1)}, + {createDateMidnight(2099, 12, 31)}, + {createDateMidnight(2100, 1, 1)}, + {createDateMidnight(2100, 2, 28)}, + {createDateMidnight(2100, 3, 1)}, + {createDateMidnight(2100, 12, 31)}, + }; + } + + @Test(dataProvider="samplePlusDaysSymmetry", groups={"tck"}) + public void test_plusDays_symmetry(LocalDateTime reference) { + for (int days = 0; days < 365 * 8; days++) { + LocalDateTime t = reference.plusDays(days).plusDays(-days); + assertEquals(t, reference); + + t = reference.plusDays(-days).plusDays(days); + assertEquals(t, reference); + } + } + + @Test(groups={"tck"}) + public void test_plusDays_normal() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusDays(1); + check(t, 2007, 7, 16, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_plusDays_overMonths() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusDays(62); + check(t, 2007, 9, 15, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_plusDays_overYears() { + LocalDateTime t = LocalDateTime.of(2006, 7, 14, 12, 30, 40, 987654321).plusDays(366); + assertEquals(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_plusDays_overLeapYears() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusYears(-1).plusDays(365 + 366); + check(t, 2008, 7, 15, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_plusDays_negative() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusDays(-1); + check(t, 2007, 7, 14, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_plusDays_negativeAcrossYear() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusDays(-196); + check(t, 2006, 12, 31, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_plusDays_negativeOverYears() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusDays(-730); + check(t, 2005, 7, 15, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_plusDays_maximum() { + LocalDateTime t = createDateMidnight(Year.MAX_VALUE, 12, 30).plusDays(1); + check(t, Year.MAX_VALUE, 12, 31, 0, 0, 0, 0); + } + + @Test(groups={"tck"}) + public void test_plusDays_minimum() { + LocalDateTime t = createDateMidnight(Year.MIN_VALUE, 1, 2).plusDays(-1); + check(t, Year.MIN_VALUE, 1, 1, 0, 0, 0, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusDays_invalidTooLarge() { + createDateMidnight(Year.MAX_VALUE, 12, 31).plusDays(1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusDays_invalidTooSmall() { + createDateMidnight(Year.MIN_VALUE, 1, 1).plusDays(-1); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void test_plusDays_overflowTooLarge() { + createDateMidnight(Year.MAX_VALUE, 12, 31).plusDays(Long.MAX_VALUE); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void test_plusDays_overflowTooSmall() { + createDateMidnight(Year.MIN_VALUE, 1, 1).plusDays(Long.MIN_VALUE); + } + + //----------------------------------------------------------------------- + // plusHours() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusHours_one() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.MIDNIGHT); + LocalDate d = t.getDate(); + + for (int i = 0; i < 50; i++) { + t = t.plusHours(1); + + if ((i + 1) % 24 == 0) { + d = d.plusDays(1); + } + + assertEquals(t.getDate(), d); + assertEquals(t.getHour(), (i + 1) % 24); + } + } + + @Test(groups={"tck"}) + public void test_plusHours_fromZero() { + LocalDateTime base = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.MIDNIGHT); + LocalDate d = base.getDate().minusDays(3); + LocalTime t = LocalTime.of(21, 0); + + for (int i = -50; i < 50; i++) { + LocalDateTime dt = base.plusHours(i); + t = t.plusHours(1); + + if (t.getHour() == 0) { + d = d.plusDays(1); + } + + assertEquals(dt.getDate(), d); + assertEquals(dt.getTime(), t); + } + } + + @Test(groups={"tck"}) + public void test_plusHours_fromOne() { + LocalDateTime base = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(1, 0)); + LocalDate d = base.getDate().minusDays(3); + LocalTime t = LocalTime.of(22, 0); + + for (int i = -50; i < 50; i++) { + LocalDateTime dt = base.plusHours(i); + + t = t.plusHours(1); + + if (t.getHour() == 0) { + d = d.plusDays(1); + } + + assertEquals(dt.getDate(), d); + assertEquals(dt.getTime(), t); + } + } + + //----------------------------------------------------------------------- + // plusMinutes() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusMinutes_one() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.MIDNIGHT); + LocalDate d = t.getDate(); + + int hour = 0; + int min = 0; + + for (int i = 0; i < 70; i++) { + t = t.plusMinutes(1); + min++; + if (min == 60) { + hour++; + min = 0; + } + + assertEquals(t.getDate(), d); + assertEquals(t.getHour(), hour); + assertEquals(t.getMinute(), min); + } + } + + @Test(groups={"tck"}) + public void test_plusMinutes_fromZero() { + LocalDateTime base = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.MIDNIGHT); + LocalDate d = base.getDate().minusDays(1); + LocalTime t = LocalTime.of(22, 49); + + for (int i = -70; i < 70; i++) { + LocalDateTime dt = base.plusMinutes(i); + t = t.plusMinutes(1); + + if (t == LocalTime.MIDNIGHT) { + d = d.plusDays(1); + } + + assertEquals(dt.getDate(), d, String.valueOf(i)); + assertEquals(dt.getTime(), t, String.valueOf(i)); + } + } + + @Test(groups={"tck"}) + public void test_plusMinutes_noChange_oneDay() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusMinutes(24 * 60); + assertEquals(t.getDate(), TEST_2007_07_15_12_30_40_987654321.getDate().plusDays(1)); + } + + //----------------------------------------------------------------------- + // plusSeconds() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusSeconds_one() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.MIDNIGHT); + LocalDate d = t.getDate(); + + int hour = 0; + int min = 0; + int sec = 0; + + for (int i = 0; i < 3700; i++) { + t = t.plusSeconds(1); + sec++; + if (sec == 60) { + min++; + sec = 0; + } + if (min == 60) { + hour++; + min = 0; + } + + assertEquals(t.getDate(), d); + assertEquals(t.getHour(), hour); + assertEquals(t.getMinute(), min); + assertEquals(t.getSecond(), sec); + } + } + + @DataProvider(name="plusSeconds_fromZero") + Iterator plusSeconds_fromZero() { + return new Iterator() { + int delta = 30; + + int i = -3660; + LocalDate date = TEST_2007_07_15_12_30_40_987654321.getDate().minusDays(1); + int hour = 22; + int min = 59; + int sec = 0; + + public boolean hasNext() { + return i <= 3660; + } + + public Object[] next() { + final Object[] ret = new Object[] {i, date, hour, min, sec}; + i += delta; + sec += delta; + + if (sec >= 60) { + min++; + sec -= 60; + + if (min == 60) { + hour++; + min = 0; + + if (hour == 24) { + hour = 0; + } + } + } + + if (i == 0) { + date = date.plusDays(1); + } + + return ret; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + @Test(dataProvider="plusSeconds_fromZero", groups={"tck"}) + public void test_plusSeconds_fromZero(int seconds, LocalDate date, int hour, int min, int sec) { + LocalDateTime base = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.MIDNIGHT); + LocalDateTime t = base.plusSeconds(seconds); + + assertEquals(date, t.getDate()); + assertEquals(hour, t.getHour()); + assertEquals(min, t.getMinute()); + assertEquals(sec, t.getSecond()); + } + + @Test(groups={"tck"}) + public void test_plusSeconds_noChange_oneDay() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusSeconds(24 * 60 * 60); + assertEquals(t.getDate(), TEST_2007_07_15_12_30_40_987654321.getDate().plusDays(1)); + } + + //----------------------------------------------------------------------- + // plusNanos() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusNanos_halfABillion() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.MIDNIGHT); + LocalDate d = t.getDate(); + + int hour = 0; + int min = 0; + int sec = 0; + int nanos = 0; + + for (long i = 0; i < 3700 * 1000000000L; i+= 500000000) { + t = t.plusNanos(500000000); + nanos += 500000000; + if (nanos == 1000000000) { + sec++; + nanos = 0; + } + if (sec == 60) { + min++; + sec = 0; + } + if (min == 60) { + hour++; + min = 0; + } + + assertEquals(t.getDate(), d, String.valueOf(i)); + assertEquals(t.getHour(), hour); + assertEquals(t.getMinute(), min); + assertEquals(t.getSecond(), sec); + assertEquals(t.getNano(), nanos); + } + } + + @DataProvider(name="plusNanos_fromZero") + Iterator plusNanos_fromZero() { + return new Iterator() { + long delta = 7500000000L; + + long i = -3660 * 1000000000L; + LocalDate date = TEST_2007_07_15_12_30_40_987654321.getDate().minusDays(1); + int hour = 22; + int min = 59; + int sec = 0; + long nanos = 0; + + public boolean hasNext() { + return i <= 3660 * 1000000000L; + } + + public Object[] next() { + final Object[] ret = new Object[] {i, date, hour, min, sec, (int)nanos}; + i += delta; + nanos += delta; + + if (nanos >= 1000000000L) { + sec += nanos / 1000000000L; + nanos %= 1000000000L; + + if (sec >= 60) { + min++; + sec %= 60; + + if (min == 60) { + hour++; + min = 0; + + if (hour == 24) { + hour = 0; + date = date.plusDays(1); + } + } + } + } + + return ret; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + @Test(dataProvider="plusNanos_fromZero", groups={"tck"}) + public void test_plusNanos_fromZero(long nanoseconds, LocalDate date, int hour, int min, int sec, int nanos) { + LocalDateTime base = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.MIDNIGHT); + LocalDateTime t = base.plusNanos(nanoseconds); + + assertEquals(date, t.getDate()); + assertEquals(hour, t.getHour()); + assertEquals(min, t.getMinute()); + assertEquals(sec, t.getSecond()); + assertEquals(nanos, t.getNano()); + } + + @Test(groups={"tck"}) + public void test_plusNanos_noChange_oneDay() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusNanos(24 * 60 * 60 * 1000000000L); + assertEquals(t.getDate(), TEST_2007_07_15_12_30_40_987654321.getDate().plusDays(1)); + } + + //----------------------------------------------------------------------- + // minus(adjuster) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minus_adjuster() { + Period p = Period.ofTime(0, 0, 62, 3); + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minus(p); + assertEquals(t, LocalDateTime.of(2007, 7, 15, 12, 29, 38, 987654318)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_minus_adjuster_null() { + TEST_2007_07_15_12_30_40_987654321.minus((TemporalSubtractor) null); + } + + //----------------------------------------------------------------------- + // minus(Period) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minus_Period_positiveMonths() { + MockSimplePeriod period = MockSimplePeriod.of(7, ChronoUnit.MONTHS); + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minus(period); + assertEquals(t, LocalDateTime.of(2006, 12, 15, 12, 30, 40, 987654321)); + } + + @Test(groups={"tck"}) + public void test_minus_Period_negativeDays() { + MockSimplePeriod period = MockSimplePeriod.of(-25, ChronoUnit.DAYS); + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minus(period); + assertEquals(t, LocalDateTime.of(2007, 8, 9, 12, 30, 40, 987654321)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_minus_Period_null() { + TEST_2007_07_15_12_30_40_987654321.minus((MockSimplePeriod) null); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minus_Period_invalidTooLarge() { + MockSimplePeriod period = MockSimplePeriod.of(-1, ChronoUnit.YEARS); + LocalDateTime.of(Year.MAX_VALUE, 1, 1, 0, 0).minus(period); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minus_Period_invalidTooSmall() { + MockSimplePeriod period = MockSimplePeriod.of(1, ChronoUnit.YEARS); + LocalDateTime.of(Year.MIN_VALUE, 1, 1, 0, 0).minus(period); + } + + //----------------------------------------------------------------------- + // minus(long,TemporalUnit) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minus_longTemporalUnit_positiveMonths() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minus(7, ChronoUnit.MONTHS); + assertEquals(t, LocalDateTime.of(2006, 12, 15, 12, 30, 40, 987654321)); + } + + @Test(groups={"tck"}) + public void test_minus_longTemporalUnit_negativeDays() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minus(-25, ChronoUnit.DAYS); + assertEquals(t, LocalDateTime.of(2007, 8, 9, 12, 30, 40, 987654321)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_minus_longTemporalUnit_null() { + TEST_2007_07_15_12_30_40_987654321.minus(1, (TemporalUnit) null); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minus_longTemporalUnit_invalidTooLarge() { + LocalDateTime.of(Year.MAX_VALUE, 1, 1, 0, 0).minus(-1, ChronoUnit.YEARS); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minus_longTemporalUnit_invalidTooSmall() { + LocalDateTime.of(Year.MIN_VALUE, 1, 1, 0, 0).minus(1, ChronoUnit.YEARS); + } + + //----------------------------------------------------------------------- + // minusYears() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusYears_int_normal() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusYears(1); + check(t, 2006, 7, 15, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_minusYears_int_negative() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusYears(-1); + check(t, 2008, 7, 15, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_minusYears_int_adjustDay() { + LocalDateTime t = createDateMidnight(2008, 2, 29).minusYears(1); + check(t, 2007, 2, 28, 0, 0, 0, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusYears_int_invalidTooLarge() { + createDateMidnight(Year.MAX_VALUE, 1, 1).minusYears(-1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusYears_int_invalidTooSmall() { + createDateMidnight(Year.MIN_VALUE, 1, 1).minusYears(1); + } + + //----------------------------------------------------------------------- + // minusMonths() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusMonths_int_normal() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusMonths(1); + check(t, 2007, 6, 15, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_minusMonths_int_overYears() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusMonths(25); + check(t, 2005, 6, 15, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_minusMonths_int_negative() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusMonths(-1); + check(t, 2007, 8, 15, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_minusMonths_int_negativeAcrossYear() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusMonths(-7); + check(t, 2008, 2, 15, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_minusMonths_int_negativeOverYears() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusMonths(-31); + check(t, 2010, 2, 15, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_minusMonths_int_adjustDayFromLeapYear() { + LocalDateTime t = createDateMidnight(2008, 2, 29).minusMonths(12); + check(t, 2007, 2, 28, 0, 0, 0, 0); + } + + @Test(groups={"tck"}) + public void test_minusMonths_int_adjustDayFromMonthLength() { + LocalDateTime t = createDateMidnight(2007, 3, 31).minusMonths(1); + check(t, 2007, 2, 28, 0, 0, 0, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusMonths_int_invalidTooLarge() { + createDateMidnight(Year.MAX_VALUE, 12, 1).minusMonths(-1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusMonths_int_invalidTooSmall() { + createDateMidnight(Year.MIN_VALUE, 1, 1).minusMonths(1); + } + + //----------------------------------------------------------------------- + // minusWeeks() + //----------------------------------------------------------------------- + @DataProvider(name="sampleMinusWeeksSymmetry") + Object[][] provider_sampleMinusWeeksSymmetry() { + return new Object[][] { + {createDateMidnight(-1, 1, 1)}, + {createDateMidnight(-1, 2, 28)}, + {createDateMidnight(-1, 3, 1)}, + {createDateMidnight(-1, 12, 31)}, + {createDateMidnight(0, 1, 1)}, + {createDateMidnight(0, 2, 28)}, + {createDateMidnight(0, 2, 29)}, + {createDateMidnight(0, 3, 1)}, + {createDateMidnight(0, 12, 31)}, + {createDateMidnight(2007, 1, 1)}, + {createDateMidnight(2007, 2, 28)}, + {createDateMidnight(2007, 3, 1)}, + {createDateMidnight(2007, 12, 31)}, + {createDateMidnight(2008, 1, 1)}, + {createDateMidnight(2008, 2, 28)}, + {createDateMidnight(2008, 2, 29)}, + {createDateMidnight(2008, 3, 1)}, + {createDateMidnight(2008, 12, 31)}, + {createDateMidnight(2099, 1, 1)}, + {createDateMidnight(2099, 2, 28)}, + {createDateMidnight(2099, 3, 1)}, + {createDateMidnight(2099, 12, 31)}, + {createDateMidnight(2100, 1, 1)}, + {createDateMidnight(2100, 2, 28)}, + {createDateMidnight(2100, 3, 1)}, + {createDateMidnight(2100, 12, 31)}, + }; + } + + @Test(dataProvider="sampleMinusWeeksSymmetry", groups={"tck"}) + public void test_minusWeeks_symmetry(LocalDateTime reference) { + for (int weeks = 0; weeks < 365 * 8; weeks++) { + LocalDateTime t = reference.minusWeeks(weeks).minusWeeks(-weeks); + assertEquals(t, reference); + + t = reference.minusWeeks(-weeks).minusWeeks(weeks); + assertEquals(t, reference); + } + } + + @Test(groups={"tck"}) + public void test_minusWeeks_normal() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusWeeks(1); + check(t, 2007, 7, 8, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_overMonths() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusWeeks(9); + check(t, 2007, 5, 13, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_overYears() { + LocalDateTime t = LocalDateTime.of(2008, 7, 13, 12, 30, 40, 987654321).minusWeeks(52); + assertEquals(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_overLeapYears() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusYears(-1).minusWeeks(104); + check(t, 2006, 7, 18, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_negative() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusWeeks(-1); + check(t, 2007, 7, 22, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_negativeAcrossYear() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusWeeks(-28); + check(t, 2008, 1, 27, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_negativeOverYears() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusWeeks(-104); + check(t, 2009, 7, 12, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_maximum() { + LocalDateTime t = createDateMidnight(Year.MAX_VALUE, 12, 24).minusWeeks(-1); + check(t, Year.MAX_VALUE, 12, 31, 0, 0, 0, 0); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_minimum() { + LocalDateTime t = createDateMidnight(Year.MIN_VALUE, 1, 8).minusWeeks(1); + check(t, Year.MIN_VALUE, 1, 1, 0, 0, 0, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusWeeks_invalidTooLarge() { + createDateMidnight(Year.MAX_VALUE, 12, 25).minusWeeks(-1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusWeeks_invalidTooSmall() { + createDateMidnight(Year.MIN_VALUE, 1, 7).minusWeeks(1); + } + + //----------------------------------------------------------------------- + // minusDays() + //----------------------------------------------------------------------- + @DataProvider(name="sampleMinusDaysSymmetry") + Object[][] provider_sampleMinusDaysSymmetry() { + return new Object[][] { + {createDateMidnight(-1, 1, 1)}, + {createDateMidnight(-1, 2, 28)}, + {createDateMidnight(-1, 3, 1)}, + {createDateMidnight(-1, 12, 31)}, + {createDateMidnight(0, 1, 1)}, + {createDateMidnight(0, 2, 28)}, + {createDateMidnight(0, 2, 29)}, + {createDateMidnight(0, 3, 1)}, + {createDateMidnight(0, 12, 31)}, + {createDateMidnight(2007, 1, 1)}, + {createDateMidnight(2007, 2, 28)}, + {createDateMidnight(2007, 3, 1)}, + {createDateMidnight(2007, 12, 31)}, + {createDateMidnight(2008, 1, 1)}, + {createDateMidnight(2008, 2, 28)}, + {createDateMidnight(2008, 2, 29)}, + {createDateMidnight(2008, 3, 1)}, + {createDateMidnight(2008, 12, 31)}, + {createDateMidnight(2099, 1, 1)}, + {createDateMidnight(2099, 2, 28)}, + {createDateMidnight(2099, 3, 1)}, + {createDateMidnight(2099, 12, 31)}, + {createDateMidnight(2100, 1, 1)}, + {createDateMidnight(2100, 2, 28)}, + {createDateMidnight(2100, 3, 1)}, + {createDateMidnight(2100, 12, 31)}, + }; + } + + @Test(dataProvider="sampleMinusDaysSymmetry", groups={"tck"}) + public void test_minusDays_symmetry(LocalDateTime reference) { + for (int days = 0; days < 365 * 8; days++) { + LocalDateTime t = reference.minusDays(days).minusDays(-days); + assertEquals(t, reference); + + t = reference.minusDays(-days).minusDays(days); + assertEquals(t, reference); + } + } + + @Test(groups={"tck"}) + public void test_minusDays_normal() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusDays(1); + check(t, 2007, 7, 14, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_minusDays_overMonths() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusDays(62); + check(t, 2007, 5, 14, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_minusDays_overYears() { + LocalDateTime t = LocalDateTime.of(2008, 7, 16, 12, 30, 40, 987654321).minusDays(367); + assertEquals(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_minusDays_overLeapYears() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusYears(2).minusDays(365 + 366); + assertEquals(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_minusDays_negative() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusDays(-1); + check(t, 2007, 7, 16, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_minusDays_negativeAcrossYear() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusDays(-169); + check(t, 2007, 12, 31, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_minusDays_negativeOverYears() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusDays(-731); + check(t, 2009, 7, 15, 12, 30, 40, 987654321); + } + + @Test(groups={"tck"}) + public void test_minusDays_maximum() { + LocalDateTime t = createDateMidnight(Year.MAX_VALUE, 12, 30).minusDays(-1); + check(t, Year.MAX_VALUE, 12, 31, 0, 0, 0, 0); + } + + @Test(groups={"tck"}) + public void test_minusDays_minimum() { + LocalDateTime t = createDateMidnight(Year.MIN_VALUE, 1, 2).minusDays(1); + check(t, Year.MIN_VALUE, 1, 1, 0, 0, 0, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusDays_invalidTooLarge() { + createDateMidnight(Year.MAX_VALUE, 12, 31).minusDays(-1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusDays_invalidTooSmall() { + createDateMidnight(Year.MIN_VALUE, 1, 1).minusDays(1); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void test_minusDays_overflowTooLarge() { + createDateMidnight(Year.MAX_VALUE, 12, 31).minusDays(Long.MIN_VALUE); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void test_minusDays_overflowTooSmall() { + createDateMidnight(Year.MIN_VALUE, 1, 1).minusDays(Long.MAX_VALUE); + } + + //----------------------------------------------------------------------- + // minusHours() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusHours_one() { + LocalDateTime t =TEST_2007_07_15_12_30_40_987654321.with(LocalTime.MIDNIGHT); + LocalDate d = t.getDate(); + + for (int i = 0; i < 50; i++) { + t = t.minusHours(1); + + if (i % 24 == 0) { + d = d.minusDays(1); + } + + assertEquals(t.getDate(), d); + assertEquals(t.getHour(), (((-i + 23) % 24) + 24) % 24); + } + } + + @Test(groups={"tck"}) + public void test_minusHours_fromZero() { + LocalDateTime base = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.MIDNIGHT); + LocalDate d = base.getDate().plusDays(2); + LocalTime t = LocalTime.of(3, 0); + + for (int i = -50; i < 50; i++) { + LocalDateTime dt = base.minusHours(i); + t = t.minusHours(1); + + if (t.getHour() == 23) { + d = d.minusDays(1); + } + + assertEquals(dt.getDate(), d, String.valueOf(i)); + assertEquals(dt.getTime(), t); + } + } + + @Test(groups={"tck"}) + public void test_minusHours_fromOne() { + LocalDateTime base = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(1, 0)); + LocalDate d = base.getDate().plusDays(2); + LocalTime t = LocalTime.of(4, 0); + + for (int i = -50; i < 50; i++) { + LocalDateTime dt = base.minusHours(i); + + t = t.minusHours(1); + + if (t.getHour() == 23) { + d = d.minusDays(1); + } + + assertEquals(dt.getDate(), d, String.valueOf(i)); + assertEquals(dt.getTime(), t); + } + } + + //----------------------------------------------------------------------- + // minusMinutes() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusMinutes_one() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.MIDNIGHT); + LocalDate d = t.getDate().minusDays(1); + + int hour = 0; + int min = 0; + + for (int i = 0; i < 70; i++) { + t = t.minusMinutes(1); + min--; + if (min == -1) { + hour--; + min = 59; + + if (hour == -1) { + hour = 23; + } + } + assertEquals(t.getDate(), d); + assertEquals(t.getHour(), hour); + assertEquals(t.getMinute(), min); + } + } + + @Test(groups={"tck"}) + public void test_minusMinutes_fromZero() { + LocalDateTime base = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.MIDNIGHT); + LocalDate d = base.getDate().minusDays(1); + LocalTime t = LocalTime.of(22, 49); + + for (int i = 70; i > -70; i--) { + LocalDateTime dt = base.minusMinutes(i); + t = t.plusMinutes(1); + + if (t == LocalTime.MIDNIGHT) { + d = d.plusDays(1); + } + + assertEquals(dt.getDate(), d); + assertEquals(dt.getTime(), t); + } + } + + @Test(groups={"tck"}) + public void test_minusMinutes_noChange_oneDay() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusMinutes(24 * 60); + assertEquals(t.getDate(), TEST_2007_07_15_12_30_40_987654321.getDate().minusDays(1)); + } + + //----------------------------------------------------------------------- + // minusSeconds() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusSeconds_one() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.MIDNIGHT); + LocalDate d = t.getDate().minusDays(1); + + int hour = 0; + int min = 0; + int sec = 0; + + for (int i = 0; i < 3700; i++) { + t = t.minusSeconds(1); + sec--; + if (sec == -1) { + min--; + sec = 59; + + if (min == -1) { + hour--; + min = 59; + + if (hour == -1) { + hour = 23; + } + } + } + + assertEquals(t.getDate(), d); + assertEquals(t.getHour(), hour); + assertEquals(t.getMinute(), min); + assertEquals(t.getSecond(), sec); + } + } + + @DataProvider(name="minusSeconds_fromZero") + Iterator minusSeconds_fromZero() { + return new Iterator() { + int delta = 30; + + int i = 3660; + LocalDate date = TEST_2007_07_15_12_30_40_987654321.getDate().minusDays(1); + int hour = 22; + int min = 59; + int sec = 0; + + public boolean hasNext() { + return i >= -3660; + } + + public Object[] next() { + final Object[] ret = new Object[] {i, date, hour, min, sec}; + i -= delta; + sec += delta; + + if (sec >= 60) { + min++; + sec -= 60; + + if (min == 60) { + hour++; + min = 0; + + if (hour == 24) { + hour = 0; + } + } + } + + if (i == 0) { + date = date.plusDays(1); + } + + return ret; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + @Test(dataProvider="minusSeconds_fromZero", groups={"tck"}) + public void test_minusSeconds_fromZero(int seconds, LocalDate date, int hour, int min, int sec) { + LocalDateTime base = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.MIDNIGHT); + LocalDateTime t = base.minusSeconds(seconds); + + assertEquals(date, t.getDate()); + assertEquals(hour, t.getHour()); + assertEquals(min, t.getMinute()); + assertEquals(sec, t.getSecond()); + } + + //----------------------------------------------------------------------- + // minusNanos() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusNanos_halfABillion() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.MIDNIGHT); + LocalDate d = t.getDate().minusDays(1); + + int hour = 0; + int min = 0; + int sec = 0; + int nanos = 0; + + for (long i = 0; i < 3700 * 1000000000L; i+= 500000000) { + t = t.minusNanos(500000000); + nanos -= 500000000; + + if (nanos < 0) { + sec--; + nanos += 1000000000; + + if (sec == -1) { + min--; + sec += 60; + + if (min == -1) { + hour--; + min += 60; + + if (hour == -1) { + hour += 24; + } + } + } + } + + assertEquals(t.getDate(), d); + assertEquals(t.getHour(), hour); + assertEquals(t.getMinute(), min); + assertEquals(t.getSecond(), sec); + assertEquals(t.getNano(), nanos); + } + } + + @DataProvider(name="minusNanos_fromZero") + Iterator minusNanos_fromZero() { + return new Iterator() { + long delta = 7500000000L; + + long i = 3660 * 1000000000L; + LocalDate date = TEST_2007_07_15_12_30_40_987654321.getDate().minusDays(1); + int hour = 22; + int min = 59; + int sec = 0; + long nanos = 0; + + public boolean hasNext() { + return i >= -3660 * 1000000000L; + } + + public Object[] next() { + final Object[] ret = new Object[] {i, date, hour, min, sec, (int)nanos}; + i -= delta; + nanos += delta; + + if (nanos >= 1000000000L) { + sec += nanos / 1000000000L; + nanos %= 1000000000L; + + if (sec >= 60) { + min++; + sec %= 60; + + if (min == 60) { + hour++; + min = 0; + + if (hour == 24) { + hour = 0; + date = date.plusDays(1); + } + } + } + } + + return ret; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + @Test(dataProvider="minusNanos_fromZero", groups={"tck"}) + public void test_minusNanos_fromZero(long nanoseconds, LocalDate date, int hour, int min, int sec, int nanos) { + LocalDateTime base = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.MIDNIGHT); + LocalDateTime t = base.minusNanos(nanoseconds); + + assertEquals(date, t.getDate()); + assertEquals(hour, t.getHour()); + assertEquals(min, t.getMinute()); + assertEquals(sec, t.getSecond()); + assertEquals(nanos, t.getNano()); + } + + //----------------------------------------------------------------------- + // atOffset() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_atOffset() { + LocalDateTime t = LocalDateTime.of(2008, 6, 30, 11, 30); + assertEquals(t.atOffset(OFFSET_PTWO), OffsetDateTime.of(LocalDateTime.of(2008, 6, 30, 11, 30), OFFSET_PTWO)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_atOffset_nullZoneOffset() { + LocalDateTime t = LocalDateTime.of(2008, 6, 30, 11, 30); + t.atOffset((ZoneOffset) null); + } + + //----------------------------------------------------------------------- + // atZone() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_atZone() { + LocalDateTime t = LocalDateTime.of(2008, 6, 30, 11, 30); + assertEquals(t.atZone(ZONE_PARIS), + ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 11, 30), ZONE_PARIS)); + } + + @Test(groups={"tck"}) + public void test_atZone_Offset() { + LocalDateTime t = LocalDateTime.of(2008, 6, 30, 11, 30); + assertEquals(t.atZone(OFFSET_PTWO), ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 11, 30), OFFSET_PTWO)); + } + + @Test(groups={"tck"}) + public void test_atZone_dstGap() { + LocalDateTime t = LocalDateTime.of(2007, 4, 1, 0, 0); + assertEquals(t.atZone(ZONE_GAZA), + ZonedDateTime.of(LocalDateTime.of(2007, 4, 1, 1, 0), ZONE_GAZA)); + } + + @Test(groups={"tck"}) + public void test_atZone_dstOverlap() { + LocalDateTime t = LocalDateTime.of(2007, 10, 28, 2, 30); + assertEquals(t.atZone(ZONE_PARIS), + ZonedDateTime.ofStrict(LocalDateTime.of(2007, 10, 28, 2, 30), OFFSET_PTWO, ZONE_PARIS)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_atZone_nullTimeZone() { + LocalDateTime t = LocalDateTime.of(2008, 6, 30, 11, 30); + t.atZone((ZoneId) null); + } + + //----------------------------------------------------------------------- + // toEpochSecond() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toEpochSecond_afterEpoch() { + for (int i = -5; i < 5; i++) { + ZoneOffset offset = ZoneOffset.ofHours(i); + for (int j = 0; j < 100000; j++) { + LocalDateTime a = LocalDateTime.of(1970, 1, 1, 0, 0).plusSeconds(j); + assertEquals(a.toEpochSecond(offset), j - i * 3600); + } + } + } + + @Test(groups={"tck"}) + public void test_toEpochSecond_beforeEpoch() { + for (int i = 0; i < 100000; i++) { + LocalDateTime a = LocalDateTime.of(1970, 1, 1, 0, 0).minusSeconds(i); + assertEquals(a.toEpochSecond(ZoneOffset.UTC), -i); + } + } + + //----------------------------------------------------------------------- + // compareTo() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_comparisons() { + test_comparisons_LocalDateTime( + LocalDate.of(Year.MIN_VALUE, 1, 1), + LocalDate.of(Year.MIN_VALUE, 12, 31), + LocalDate.of(-1, 1, 1), + LocalDate.of(-1, 12, 31), + LocalDate.of(0, 1, 1), + LocalDate.of(0, 12, 31), + LocalDate.of(1, 1, 1), + LocalDate.of(1, 12, 31), + LocalDate.of(2008, 1, 1), + LocalDate.of(2008, 2, 29), + LocalDate.of(2008, 12, 31), + LocalDate.of(Year.MAX_VALUE, 1, 1), + LocalDate.of(Year.MAX_VALUE, 12, 31) + ); + } + + void test_comparisons_LocalDateTime(LocalDate... localDates) { + test_comparisons_LocalDateTime( + localDates, + LocalTime.MIDNIGHT, + LocalTime.of(0, 0, 0, 999999999), + LocalTime.of(0, 0, 59, 0), + LocalTime.of(0, 0, 59, 999999999), + LocalTime.of(0, 59, 0, 0), + LocalTime.of(0, 59, 59, 999999999), + LocalTime.NOON, + LocalTime.of(12, 0, 0, 999999999), + LocalTime.of(12, 0, 59, 0), + LocalTime.of(12, 0, 59, 999999999), + LocalTime.of(12, 59, 0, 0), + LocalTime.of(12, 59, 59, 999999999), + LocalTime.of(23, 0, 0, 0), + LocalTime.of(23, 0, 0, 999999999), + LocalTime.of(23, 0, 59, 0), + LocalTime.of(23, 0, 59, 999999999), + LocalTime.of(23, 59, 0, 0), + LocalTime.of(23, 59, 59, 999999999) + ); + } + + void test_comparisons_LocalDateTime(LocalDate[] localDates, LocalTime... localTimes) { + LocalDateTime[] localDateTimes = new LocalDateTime[localDates.length * localTimes.length]; + int i = 0; + + for (LocalDate localDate : localDates) { + for (LocalTime localTime : localTimes) { + localDateTimes[i++] = LocalDateTime.of(localDate, localTime); + } + } + + doTest_comparisons_LocalDateTime(localDateTimes); + } + + void doTest_comparisons_LocalDateTime(LocalDateTime[] localDateTimes) { + for (int i = 0; i < localDateTimes.length; i++) { + LocalDateTime a = localDateTimes[i]; + for (int j = 0; j < localDateTimes.length; j++) { + LocalDateTime b = localDateTimes[j]; + if (i < j) { + assertTrue(a.compareTo(b) < 0, a + " <=> " + b); + assertEquals(a.isBefore(b), true, a + " <=> " + b); + assertEquals(a.isAfter(b), false, a + " <=> " + b); + assertEquals(a.equals(b), false, a + " <=> " + b); + } else if (i > j) { + assertTrue(a.compareTo(b) > 0, a + " <=> " + b); + assertEquals(a.isBefore(b), false, a + " <=> " + b); + assertEquals(a.isAfter(b), true, a + " <=> " + b); + assertEquals(a.equals(b), false, a + " <=> " + b); + } else { + assertEquals(a.compareTo(b), 0, a + " <=> " + b); + assertEquals(a.isBefore(b), false, a + " <=> " + b); + assertEquals(a.isAfter(b), false, a + " <=> " + b); + assertEquals(a.equals(b), true, a + " <=> " + b); + } + } + } + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_compareTo_ObjectNull() { + TEST_2007_07_15_12_30_40_987654321.compareTo(null); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_isBefore_ObjectNull() { + TEST_2007_07_15_12_30_40_987654321.isBefore(null); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_isAfter_ObjectNull() { + TEST_2007_07_15_12_30_40_987654321.isAfter(null); + } + + @Test(expectedExceptions=ClassCastException.class, groups={"tck"}) + @SuppressWarnings({"unchecked", "rawtypes"}) + public void compareToNonLocalDateTime() { + Comparable c = TEST_2007_07_15_12_30_40_987654321; + c.compareTo(new Object()); + } + + //----------------------------------------------------------------------- + // equals() + //----------------------------------------------------------------------- + @DataProvider(name="sampleDateTimes") + Iterator provider_sampleDateTimes() { + return new Iterator() { + Object[][] sampleDates = provider_sampleDates(); + Object[][] sampleTimes = provider_sampleTimes(); + int datesIndex = 0; + int timesIndex = 0; + + public boolean hasNext() { + return datesIndex < sampleDates.length; + } + + public Object[] next() { + Object[] sampleDate = sampleDates[datesIndex]; + Object[] sampleTime = sampleTimes[timesIndex]; + + Object[] ret = new Object[sampleDate.length + sampleTime.length]; + + System.arraycopy(sampleDate, 0, ret, 0, sampleDate.length); + System.arraycopy(sampleTime, 0, ret, sampleDate.length, sampleTime.length); + + if (++timesIndex == sampleTimes.length) { + datesIndex++; + timesIndex = 0; + } + + return ret; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + @Test(dataProvider="sampleDateTimes", groups={"tck"}) + public void test_equals_true(int y, int m, int d, int h, int mi, int s, int n) { + LocalDateTime a = LocalDateTime.of(y, m, d, h, mi, s, n); + LocalDateTime b = LocalDateTime.of(y, m, d, h, mi, s, n); + assertTrue(a.equals(b)); + } + + @Test(dataProvider="sampleDateTimes", groups={"tck"}) + public void test_equals_false_year_differs(int y, int m, int d, int h, int mi, int s, int n) { + LocalDateTime a = LocalDateTime.of(y, m, d, h, mi, s, n); + LocalDateTime b = LocalDateTime.of(y + 1, m, d, h, mi, s, n); + assertFalse(a.equals(b)); + } + + @Test(dataProvider="sampleDateTimes", groups={"tck"}) + public void test_equals_false_month_differs(int y, int m, int d, int h, int mi, int s, int n) { + LocalDateTime a = LocalDateTime.of(y, m, d, h, mi, s, n); + LocalDateTime b = LocalDateTime.of(y, m + 1, d, h, mi, s, n); + assertFalse(a.equals(b)); + } + + @Test(dataProvider="sampleDateTimes", groups={"tck"}) + public void test_equals_false_day_differs(int y, int m, int d, int h, int mi, int s, int n) { + LocalDateTime a = LocalDateTime.of(y, m, d, h, mi, s, n); + LocalDateTime b = LocalDateTime.of(y, m, d + 1, h, mi, s, n); + assertFalse(a.equals(b)); + } + + @Test(dataProvider="sampleDateTimes", groups={"tck"}) + public void test_equals_false_hour_differs(int y, int m, int d, int h, int mi, int s, int n) { + LocalDateTime a = LocalDateTime.of(y, m, d, h, mi, s, n); + LocalDateTime b = LocalDateTime.of(y, m, d, h + 1, mi, s, n); + assertFalse(a.equals(b)); + } + + @Test(dataProvider="sampleDateTimes", groups={"tck"}) + public void test_equals_false_minute_differs(int y, int m, int d, int h, int mi, int s, int n) { + LocalDateTime a = LocalDateTime.of(y, m, d, h, mi, s, n); + LocalDateTime b = LocalDateTime.of(y, m, d, h, mi + 1, s, n); + assertFalse(a.equals(b)); + } + + @Test(dataProvider="sampleDateTimes", groups={"tck"}) + public void test_equals_false_second_differs(int y, int m, int d, int h, int mi, int s, int n) { + LocalDateTime a = LocalDateTime.of(y, m, d, h, mi, s, n); + LocalDateTime b = LocalDateTime.of(y, m, d, h, mi, s + 1, n); + assertFalse(a.equals(b)); + } + + @Test(dataProvider="sampleDateTimes", groups={"tck"}) + public void test_equals_false_nano_differs(int y, int m, int d, int h, int mi, int s, int n) { + LocalDateTime a = LocalDateTime.of(y, m, d, h, mi, s, n); + LocalDateTime b = LocalDateTime.of(y, m, d, h, mi, s, n + 1); + assertFalse(a.equals(b)); + } + + @Test(groups={"tck"}) + public void test_equals_itself_true() { + assertEquals(TEST_2007_07_15_12_30_40_987654321.equals(TEST_2007_07_15_12_30_40_987654321), true); + } + + @Test(groups={"tck"}) + public void test_equals_string_false() { + assertEquals(TEST_2007_07_15_12_30_40_987654321.equals("2007-07-15T12:30:40.987654321"), false); + } + + @Test(groups={"tck"}) + public void test_equals_null_false() { + assertEquals(TEST_2007_07_15_12_30_40_987654321.equals(null), false); + } + + //----------------------------------------------------------------------- + // hashCode() + //----------------------------------------------------------------------- + @Test(dataProvider="sampleDateTimes", groups={"tck"}) + public void test_hashCode(int y, int m, int d, int h, int mi, int s, int n) { + LocalDateTime a = LocalDateTime.of(y, m, d, h, mi, s, n); + assertEquals(a.hashCode(), a.hashCode()); + LocalDateTime b = LocalDateTime.of(y, m, d, h, mi, s, n); + assertEquals(a.hashCode(), b.hashCode()); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @DataProvider(name="sampleToString") + Object[][] provider_sampleToString() { + return new Object[][] { + {2008, 7, 5, 2, 1, 0, 0, "2008-07-05T02:01"}, + {2007, 12, 31, 23, 59, 1, 0, "2007-12-31T23:59:01"}, + {999, 12, 31, 23, 59, 59, 990000000, "0999-12-31T23:59:59.990"}, + {-1, 1, 2, 23, 59, 59, 999990000, "-0001-01-02T23:59:59.999990"}, + {-2008, 1, 2, 23, 59, 59, 999999990, "-2008-01-02T23:59:59.999999990"}, + }; + } + + @Test(dataProvider="sampleToString", groups={"tck"}) + public void test_toString(int y, int m, int d, int h, int mi, int s, int n, String expected) { + LocalDateTime t = LocalDateTime.of(y, m, d, h, mi, s, n); + String str = t.toString(); + assertEquals(str, expected); + } + + //----------------------------------------------------------------------- + // toString(DateTimeFormatter) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toString_formatter() { + DateTimeFormatter f = DateTimeFormatters.pattern("y M d H m s"); + String t = LocalDateTime.of(2010, 12, 3, 11, 30, 45).toString(f); + assertEquals(t, "2010 12 3 11 30 45"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_toString_formatter_null() { + LocalDateTime.of(2010, 12, 3, 11, 30, 45).toString(null); + } + +} diff --git a/jdk/test/java/time/tck/java/time/TCKLocalTime.java b/jdk/test/java/time/tck/java/time/TCKLocalTime.java new file mode 100644 index 00000000000..1cc5486c7e2 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/TCKLocalTime.java @@ -0,0 +1,2226 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time; + +import static java.time.temporal.ChronoField.AMPM_OF_DAY; +import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_AMPM; +import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_DAY; +import static java.time.temporal.ChronoField.HOUR_OF_AMPM; +import static java.time.temporal.ChronoField.HOUR_OF_DAY; +import static java.time.temporal.ChronoField.MICRO_OF_DAY; +import static java.time.temporal.ChronoField.MICRO_OF_SECOND; +import static java.time.temporal.ChronoField.MILLI_OF_DAY; +import static java.time.temporal.ChronoField.MILLI_OF_SECOND; +import static java.time.temporal.ChronoField.MINUTE_OF_DAY; +import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; +import static java.time.temporal.ChronoField.NANO_OF_DAY; +import static java.time.temporal.ChronoField.NANO_OF_SECOND; +import static java.time.temporal.ChronoField.SECOND_OF_DAY; +import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; +import static java.time.temporal.ChronoUnit.DAYS; +import static java.time.temporal.ChronoUnit.FOREVER; +import static java.time.temporal.ChronoUnit.HOURS; +import static java.time.temporal.ChronoUnit.MICROS; +import static java.time.temporal.ChronoUnit.MILLIS; +import static java.time.temporal.ChronoUnit.MINUTES; +import static java.time.temporal.ChronoUnit.MONTHS; +import static java.time.temporal.ChronoUnit.NANOS; +import static java.time.temporal.ChronoUnit.SECONDS; +import static java.time.temporal.ChronoUnit.WEEKS; +import static java.time.temporal.ChronoUnit.YEARS; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.List; + +import java.time.Clock; +import java.time.DateTimeException; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Period; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatters; +import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoUnit; +import java.time.temporal.JulianFields; +import java.time.temporal.OffsetTime; +import java.time.temporal.Queries; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalAdder; +import java.time.temporal.TemporalAdjuster; +import java.time.temporal.TemporalField; +import java.time.temporal.TemporalSubtractor; +import java.time.temporal.TemporalUnit; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import test.java.time.MockSimplePeriod; + +/** + * Test LocalTime. + */ +@Test +public class TCKLocalTime extends AbstractDateTimeTest { + + private static final ZoneOffset OFFSET_PTWO = ZoneOffset.ofHours(2); + + private LocalTime TEST_12_30_40_987654321; + + private static final TemporalUnit[] INVALID_UNITS; + static { + EnumSet set = EnumSet.range(WEEKS, FOREVER); + INVALID_UNITS = (TemporalUnit[]) set.toArray(new TemporalUnit[set.size()]); + } + + @BeforeMethod(groups={"tck","implementation"}) + public void setUp() { + TEST_12_30_40_987654321 = LocalTime.of(12, 30, 40, 987654321); + } + + //----------------------------------------------------------------------- + @Override + protected List samples() { + TemporalAccessor[] array = {TEST_12_30_40_987654321, LocalTime.MIN, LocalTime.MAX, LocalTime.MIDNIGHT, LocalTime.NOON}; + return Arrays.asList(array); + } + + @Override + protected List validFields() { + TemporalField[] array = { + NANO_OF_SECOND, + NANO_OF_DAY, + MICRO_OF_SECOND, + MICRO_OF_DAY, + MILLI_OF_SECOND, + MILLI_OF_DAY, + SECOND_OF_MINUTE, + SECOND_OF_DAY, + MINUTE_OF_HOUR, + MINUTE_OF_DAY, + CLOCK_HOUR_OF_AMPM, + HOUR_OF_AMPM, + CLOCK_HOUR_OF_DAY, + HOUR_OF_DAY, + AMPM_OF_DAY, + }; + return Arrays.asList(array); + } + + @Override + protected List invalidFields() { + List list = new ArrayList<>(Arrays.asList(ChronoField.values())); + list.removeAll(validFields()); + list.add(JulianFields.JULIAN_DAY); + list.add(JulianFields.MODIFIED_JULIAN_DAY); + list.add(JulianFields.RATA_DIE); + return list; + } + + //----------------------------------------------------------------------- + @Test + public void test_serialization() throws Exception { + assertSerializable(TEST_12_30_40_987654321); + assertSerializable(LocalTime.MIN); + assertSerializable(LocalTime.MAX); + } + + @Test + public void test_serialization_format_h() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baos) ) { + dos.writeByte(4); + dos.writeByte(-1 - 22); + } + byte[] bytes = baos.toByteArray(); + assertSerializedBySer(LocalTime.of(22, 0), bytes); + } + + @Test + public void test_serialization_format_hm() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baos) ) { + dos.writeByte(4); + dos.writeByte(22); + dos.writeByte(-1 - 17); + } + byte[] bytes = baos.toByteArray(); + assertSerializedBySer(LocalTime.of(22, 17), bytes); + } + + @Test + public void test_serialization_format_hms() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baos) ) { + dos.writeByte(4); + dos.writeByte(22); + dos.writeByte(17); + dos.writeByte(-1 - 59); + } + byte[] bytes = baos.toByteArray(); + assertSerializedBySer(LocalTime.of(22, 17, 59), bytes); + } + + @Test + public void test_serialization_format_hmsn() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baos) ) { + dos.writeByte(4); + dos.writeByte(22); + dos.writeByte(17); + dos.writeByte(59); + dos.writeInt(459_000_000); + } + byte[] bytes = baos.toByteArray(); + assertSerializedBySer(LocalTime.of(22, 17, 59, 459_000_000), bytes); + } + + //----------------------------------------------------------------------- + private void check(LocalTime test, int h, int m, int s, int n) { + assertEquals(test.getHour(), h); + assertEquals(test.getMinute(), m); + assertEquals(test.getSecond(), s); + assertEquals(test.getNano(), n); + assertEquals(test, test); + assertEquals(test.hashCode(), test.hashCode()); + assertEquals(LocalTime.of(h, m, s, n), test); + } + + //----------------------------------------------------------------------- + // constants + //----------------------------------------------------------------------- + @Test(groups={"tck","implementation"}) + public void constant_MIDNIGHT() { + check(LocalTime.MIDNIGHT, 0, 0, 0, 0); + } + + @Test + public void constant_MIDDAY() { + check(LocalTime.NOON, 12, 0, 0, 0); + } + + @Test + public void constant_MIN() { + check(LocalTime.MIN, 0, 0, 0, 0); + } + + @Test + public void constant_MAX() { + check(LocalTime.MAX, 23, 59, 59, 999999999); + } + + //----------------------------------------------------------------------- + // now() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void now() { + LocalTime expected = LocalTime.now(Clock.systemDefaultZone()); + LocalTime test = LocalTime.now(); + long diff = Math.abs(test.toNanoOfDay() - expected.toNanoOfDay()); + assertTrue(diff < 100000000); // less than 0.1 secs + } + + //----------------------------------------------------------------------- + // now(ZoneId) + //----------------------------------------------------------------------- + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void now_ZoneId_nullZoneId() { + LocalTime.now((ZoneId) null); + } + + @Test(groups={"tck"}) + public void now_ZoneId() { + ZoneId zone = ZoneId.of("UTC+01:02:03"); + LocalTime expected = LocalTime.now(Clock.system(zone)); + LocalTime test = LocalTime.now(zone); + for (int i = 0; i < 100; i++) { + if (expected.equals(test)) { + return; + } + expected = LocalTime.now(Clock.system(zone)); + test = LocalTime.now(zone); + } + assertEquals(test, expected); + } + + //----------------------------------------------------------------------- + // now(Clock) + //----------------------------------------------------------------------- + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void now_Clock_nullClock() { + LocalTime.now((Clock) null); + } + + @Test(groups={"tck"}) + public void now_Clock_allSecsInDay() { + for (int i = 0; i < (2 * 24 * 60 * 60); i++) { + Instant instant = Instant.ofEpochSecond(i, 8); + Clock clock = Clock.fixed(instant, ZoneOffset.UTC); + LocalTime test = LocalTime.now(clock); + assertEquals(test.getHour(), (i / (60 * 60)) % 24); + assertEquals(test.getMinute(), (i / 60) % 60); + assertEquals(test.getSecond(), i % 60); + assertEquals(test.getNano(), 8); + } + } + + @Test(groups={"tck"}) + public void now_Clock_beforeEpoch() { + for (int i =-1; i >= -(24 * 60 * 60); i--) { + Instant instant = Instant.ofEpochSecond(i, 8); + Clock clock = Clock.fixed(instant, ZoneOffset.UTC); + LocalTime test = LocalTime.now(clock); + assertEquals(test.getHour(), ((i + 24 * 60 * 60) / (60 * 60)) % 24); + assertEquals(test.getMinute(), ((i + 24 * 60 * 60) / 60) % 60); + assertEquals(test.getSecond(), (i + 24 * 60 * 60) % 60); + assertEquals(test.getNano(), 8); + } + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void now_Clock_max() { + Clock clock = Clock.fixed(Instant.MAX, ZoneOffset.UTC); + LocalTime test = LocalTime.now(clock); + assertEquals(test.getHour(), 23); + assertEquals(test.getMinute(), 59); + assertEquals(test.getSecond(), 59); + assertEquals(test.getNano(), 999_999_999); + } + + @Test(groups={"tck"}) + public void now_Clock_min() { + Clock clock = Clock.fixed(Instant.MIN, ZoneOffset.UTC); + LocalTime test = LocalTime.now(clock); + assertEquals(test.getHour(), 0); + assertEquals(test.getMinute(), 0); + assertEquals(test.getSecond(), 0); + assertEquals(test.getNano(), 0); + } + + //----------------------------------------------------------------------- + // of() factories + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_time_2ints() { + LocalTime test = LocalTime.of(12, 30); + check(test, 12, 30, 0, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_time_2ints_hourTooLow() { + LocalTime.of(-1, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_time_2ints_hourTooHigh() { + LocalTime.of(24, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_time_2ints_minuteTooLow() { + LocalTime.of(0, -1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_time_2ints_minuteTooHigh() { + LocalTime.of(0, 60); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_time_3ints() { + LocalTime test = LocalTime.of(12, 30, 40); + check(test, 12, 30, 40, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_time_3ints_hourTooLow() { + LocalTime.of(-1, 0, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_time_3ints_hourTooHigh() { + LocalTime.of(24, 0, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_time_3ints_minuteTooLow() { + LocalTime.of(0, -1, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_time_3ints_minuteTooHigh() { + LocalTime.of(0, 60, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_time_3ints_secondTooLow() { + LocalTime.of(0, 0, -1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_time_3ints_secondTooHigh() { + LocalTime.of(0, 0, 60); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_time_4ints() { + LocalTime test = LocalTime.of(12, 30, 40, 987654321); + check(test, 12, 30, 40, 987654321); + test = LocalTime.of(12, 0, 40, 987654321); + check(test, 12, 0, 40, 987654321); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_time_4ints_hourTooLow() { + LocalTime.of(-1, 0, 0, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_time_4ints_hourTooHigh() { + LocalTime.of(24, 0, 0, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_time_4ints_minuteTooLow() { + LocalTime.of(0, -1, 0, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_time_4ints_minuteTooHigh() { + LocalTime.of(0, 60, 0, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_time_4ints_secondTooLow() { + LocalTime.of(0, 0, -1, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_time_4ints_secondTooHigh() { + LocalTime.of(0, 0, 60, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_time_4ints_nanoTooLow() { + LocalTime.of(0, 0, 0, -1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_time_4ints_nanoTooHigh() { + LocalTime.of(0, 0, 0, 1000000000); + } + + //----------------------------------------------------------------------- + // ofSecondOfDay(long) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_ofSecondOfDay() { + LocalTime localTime = LocalTime.ofSecondOfDay(2 * 60 * 60 + 17 * 60 + 23); + check(localTime, 2, 17, 23, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofSecondOfDay_tooLow() { + LocalTime.ofSecondOfDay(-1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofSecondOfDay_tooHigh() { + LocalTime.ofSecondOfDay(24 * 60 * 60); + } + + //----------------------------------------------------------------------- + // ofSecondOfDay(long, int) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_ofSecondOfDay_long_int() { + LocalTime localTime = LocalTime.ofSecondOfDay(2 * 60 * 60 + 17 * 60 + 23, 987); + check(localTime, 2, 17, 23, 987); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofSecondOfDay_long_int_tooLowSecs() { + LocalTime.ofSecondOfDay(-1, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofSecondOfDay_long_int_tooHighSecs() { + LocalTime.ofSecondOfDay(24 * 60 * 60, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofSecondOfDay_long_int_tooLowNanos() { + LocalTime.ofSecondOfDay(0, -1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofSecondOfDay_long_int_tooHighNanos() { + LocalTime.ofSecondOfDay(0, 1000000000); + } + + //----------------------------------------------------------------------- + // ofNanoOfDay(long) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_ofNanoOfDay() { + LocalTime localTime = LocalTime.ofNanoOfDay(60 * 60 * 1000000000L + 17); + check(localTime, 1, 0, 0, 17); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofNanoOfDay_tooLow() { + LocalTime.ofNanoOfDay(-1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofNanoOfDay_tooHigh() { + LocalTime.ofNanoOfDay(24 * 60 * 60 * 1000000000L); + } + + //----------------------------------------------------------------------- + // from() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_from_TemporalAccessor() { + assertEquals(LocalTime.from(LocalTime.of(17, 30)), LocalTime.of(17, 30)); + assertEquals(LocalTime.from(LocalDateTime.of(2012, 5, 1, 17, 30)), LocalTime.of(17, 30)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_from_TemporalAccessor_invalid_noDerive() { + LocalTime.from(LocalDate.of(2007, 7, 15)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_from_TemporalAccessor_null() { + LocalTime.from((TemporalAccessor) null); + } + + //----------------------------------------------------------------------- + // parse() + //----------------------------------------------------------------------- + @Test(dataProvider = "sampleToString", groups={"tck"}) + public void factory_parse_validText(int h, int m, int s, int n, String parsable) { + LocalTime t = LocalTime.parse(parsable); + assertNotNull(t, parsable); + assertEquals(t.getHour(), h); + assertEquals(t.getMinute(), m); + assertEquals(t.getSecond(), s); + assertEquals(t.getNano(), n); + } + + @DataProvider(name="sampleBadParse") + Object[][] provider_sampleBadParse() { + return new Object[][]{ + {"00;00"}, + {"12-00"}, + {"-01:00"}, + {"00:00:00-09"}, + {"00:00:00,09"}, + {"00:00:abs"}, + {"11"}, + {"11:30+01:00"}, + {"11:30+01:00[Europe/Paris]"}, + }; + } + + @Test(dataProvider = "sampleBadParse", expectedExceptions={DateTimeParseException.class}, groups={"tck"}) + public void factory_parse_invalidText(String unparsable) { + LocalTime.parse(unparsable); + } + + //-----------------------------------------------------------------------s + @Test(expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_illegalHour() { + LocalTime.parse("25:00"); + } + + @Test(expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_illegalMinute() { + LocalTime.parse("12:60"); + } + + @Test(expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_illegalSecond() { + LocalTime.parse("12:12:60"); + } + + //-----------------------------------------------------------------------s + @Test(expectedExceptions = {NullPointerException.class}, groups={"tck"}) + public void factory_parse_nullTest() { + LocalTime.parse((String) null); + } + + //----------------------------------------------------------------------- + // parse(DateTimeFormatter) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_parse_formatter() { + DateTimeFormatter f = DateTimeFormatters.pattern("H m s"); + LocalTime test = LocalTime.parse("14 30 40", f); + assertEquals(test, LocalTime.of(14, 30, 40)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_formatter_nullText() { + DateTimeFormatter f = DateTimeFormatters.pattern("H m s"); + LocalTime.parse((String) null, f); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_formatter_nullFormatter() { + LocalTime.parse("ANY", null); + } + + //----------------------------------------------------------------------- + // get(TemporalField) + //----------------------------------------------------------------------- + @Test + public void test_get_TemporalField() { + LocalTime test = TEST_12_30_40_987654321; + assertEquals(test.get(ChronoField.HOUR_OF_DAY), 12); + assertEquals(test.get(ChronoField.MINUTE_OF_HOUR), 30); + assertEquals(test.get(ChronoField.SECOND_OF_MINUTE), 40); + assertEquals(test.get(ChronoField.NANO_OF_SECOND), 987654321); + + assertEquals(test.get(ChronoField.SECOND_OF_DAY), 12 * 3600 + 30 * 60 + 40); + assertEquals(test.get(ChronoField.MINUTE_OF_DAY), 12 * 60 + 30); + assertEquals(test.get(ChronoField.HOUR_OF_AMPM), 0); + assertEquals(test.get(ChronoField.CLOCK_HOUR_OF_AMPM), 12); + assertEquals(test.get(ChronoField.CLOCK_HOUR_OF_DAY), 12); + assertEquals(test.get(ChronoField.AMPM_OF_DAY), 1); + } + + @Test + public void test_getLong_TemporalField() { + LocalTime test = TEST_12_30_40_987654321; + assertEquals(test.getLong(ChronoField.HOUR_OF_DAY), 12); + assertEquals(test.getLong(ChronoField.MINUTE_OF_HOUR), 30); + assertEquals(test.getLong(ChronoField.SECOND_OF_MINUTE), 40); + assertEquals(test.getLong(ChronoField.NANO_OF_SECOND), 987654321); + + assertEquals(test.getLong(ChronoField.SECOND_OF_DAY), 12 * 3600 + 30 * 60 + 40); + assertEquals(test.getLong(ChronoField.MINUTE_OF_DAY), 12 * 60 + 30); + assertEquals(test.getLong(ChronoField.HOUR_OF_AMPM), 0); + assertEquals(test.getLong(ChronoField.CLOCK_HOUR_OF_AMPM), 12); + assertEquals(test.getLong(ChronoField.CLOCK_HOUR_OF_DAY), 12); + assertEquals(test.getLong(ChronoField.AMPM_OF_DAY), 1); + } + + //----------------------------------------------------------------------- + // query(TemporalQuery) + //----------------------------------------------------------------------- + @Test + public void test_query_chrono() { + assertEquals(TEST_12_30_40_987654321.query(Queries.chrono()), null); + assertEquals(Queries.chrono().queryFrom(TEST_12_30_40_987654321), null); + } + + @Test + public void test_query_zoneId() { + assertEquals(TEST_12_30_40_987654321.query(Queries.zoneId()), null); + assertEquals(Queries.zoneId().queryFrom(TEST_12_30_40_987654321), null); + } + + @Test + public void test_query_precision() { + assertEquals(TEST_12_30_40_987654321.query(Queries.precision()), NANOS); + assertEquals(Queries.precision().queryFrom(TEST_12_30_40_987654321), NANOS); + } + + @Test + public void test_query_offset() { + assertEquals(TEST_12_30_40_987654321.query(Queries.offset()), null); + assertEquals(Queries.offset().queryFrom(TEST_12_30_40_987654321), null); + } + + @Test + public void test_query_zone() { + assertEquals(TEST_12_30_40_987654321.query(Queries.zone()), null); + assertEquals(Queries.zone().queryFrom(TEST_12_30_40_987654321), null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_query_null() { + TEST_12_30_40_987654321.query(null); + } + + //----------------------------------------------------------------------- + // get*() + //----------------------------------------------------------------------- + @DataProvider(name="sampleTimes") + Object[][] provider_sampleTimes() { + return new Object[][] { + {0, 0, 0, 0}, + {0, 0, 0, 1}, + {0, 0, 1, 0}, + {0, 0, 1, 1}, + {0, 1, 0, 0}, + {0, 1, 0, 1}, + {0, 1, 1, 0}, + {0, 1, 1, 1}, + {1, 0, 0, 0}, + {1, 0, 0, 1}, + {1, 0, 1, 0}, + {1, 0, 1, 1}, + {1, 1, 0, 0}, + {1, 1, 0, 1}, + {1, 1, 1, 0}, + {1, 1, 1, 1}, + }; + } + + //----------------------------------------------------------------------- + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_get(int h, int m, int s, int ns) { + LocalTime a = LocalTime.of(h, m, s, ns); + assertEquals(a.getHour(), h); + assertEquals(a.getMinute(), m); + assertEquals(a.getSecond(), s); + assertEquals(a.getNano(), ns); + } + + //----------------------------------------------------------------------- + // with() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_with_adjustment() { + final LocalTime sample = LocalTime.of(23, 5); + TemporalAdjuster adjuster = new TemporalAdjuster() { + @Override + public Temporal adjustInto(Temporal dateTime) { + return sample; + } + }; + assertEquals(TEST_12_30_40_987654321.with(adjuster), sample); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_with_adjustment_null() { + TEST_12_30_40_987654321.with((TemporalAdjuster) null); + } + + //----------------------------------------------------------------------- + // withHour() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withHour_normal() { + LocalTime t = TEST_12_30_40_987654321; + for (int i = 0; i < 24; i++) { + t = t.withHour(i); + assertEquals(t.getHour(), i); + } + } + + @Test(groups={"tck"}) + public void test_withHour_noChange_equal() { + LocalTime t = TEST_12_30_40_987654321.withHour(12); + assertEquals(t, TEST_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_withHour_toMidnight_equal() { + LocalTime t = LocalTime.of(1, 0).withHour(0); + assertEquals(t, LocalTime.MIDNIGHT); + } + + @Test(groups={"tck"}) + public void test_withHour_toMidday_equal() { + LocalTime t = LocalTime.of(1, 0).withHour(12); + assertEquals(t, LocalTime.NOON); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withHour_hourTooLow() { + TEST_12_30_40_987654321.withHour(-1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withHour_hourTooHigh() { + TEST_12_30_40_987654321.withHour(24); + } + + //----------------------------------------------------------------------- + // withMinute() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withMinute_normal() { + LocalTime t = TEST_12_30_40_987654321; + for (int i = 0; i < 60; i++) { + t = t.withMinute(i); + assertEquals(t.getMinute(), i); + } + } + + @Test(groups={"tck"}) + public void test_withMinute_noChange_equal() { + LocalTime t = TEST_12_30_40_987654321.withMinute(30); + assertEquals(t, TEST_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_withMinute_toMidnight_equal() { + LocalTime t = LocalTime.of(0, 1).withMinute(0); + assertEquals(t, LocalTime.MIDNIGHT); + } + + @Test(groups={"tck"}) + public void test_withMinute_toMidday_equals() { + LocalTime t = LocalTime.of(12, 1).withMinute(0); + assertEquals(t, LocalTime.NOON); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withMinute_minuteTooLow() { + TEST_12_30_40_987654321.withMinute(-1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withMinute_minuteTooHigh() { + TEST_12_30_40_987654321.withMinute(60); + } + + //----------------------------------------------------------------------- + // withSecond() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withSecond_normal() { + LocalTime t = TEST_12_30_40_987654321; + for (int i = 0; i < 60; i++) { + t = t.withSecond(i); + assertEquals(t.getSecond(), i); + } + } + + @Test(groups={"tck"}) + public void test_withSecond_noChange_equal() { + LocalTime t = TEST_12_30_40_987654321.withSecond(40); + assertEquals(t, TEST_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_withSecond_toMidnight_equal() { + LocalTime t = LocalTime.of(0, 0, 1).withSecond(0); + assertEquals(t, LocalTime.MIDNIGHT); + } + + @Test(groups={"tck"}) + public void test_withSecond_toMidday_equal() { + LocalTime t = LocalTime.of(12, 0, 1).withSecond(0); + assertEquals(t, LocalTime.NOON); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withSecond_secondTooLow() { + TEST_12_30_40_987654321.withSecond(-1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withSecond_secondTooHigh() { + TEST_12_30_40_987654321.withSecond(60); + } + + //----------------------------------------------------------------------- + // withNano() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withNanoOfSecond_normal() { + LocalTime t = TEST_12_30_40_987654321; + t = t.withNano(1); + assertEquals(t.getNano(), 1); + t = t.withNano(10); + assertEquals(t.getNano(), 10); + t = t.withNano(100); + assertEquals(t.getNano(), 100); + t = t.withNano(999999999); + assertEquals(t.getNano(), 999999999); + } + + @Test(groups={"tck"}) + public void test_withNanoOfSecond_noChange_equal() { + LocalTime t = TEST_12_30_40_987654321.withNano(987654321); + assertEquals(t, TEST_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_withNanoOfSecond_toMidnight_equal() { + LocalTime t = LocalTime.of(0, 0, 0, 1).withNano(0); + assertEquals(t, LocalTime.MIDNIGHT); + } + + @Test(groups={"tck"}) + public void test_withNanoOfSecond_toMidday_equal() { + LocalTime t = LocalTime.of(12, 0, 0, 1).withNano(0); + assertEquals(t, LocalTime.NOON); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withNanoOfSecond_nanoTooLow() { + TEST_12_30_40_987654321.withNano(-1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withNanoOfSecond_nanoTooHigh() { + TEST_12_30_40_987654321.withNano(1000000000); + } + + //----------------------------------------------------------------------- + // truncated(TemporalUnit) + //----------------------------------------------------------------------- + @DataProvider(name="truncatedToValid") + Object[][] data_truncatedToValid() { + return new Object[][] { + {LocalTime.of(1, 2, 3, 123_456_789), NANOS, LocalTime.of(1, 2, 3, 123_456_789)}, + {LocalTime.of(1, 2, 3, 123_456_789), MICROS, LocalTime.of(1, 2, 3, 123_456_000)}, + {LocalTime.of(1, 2, 3, 123_456_789), MILLIS, LocalTime.of(1, 2, 3, 1230_00_000)}, + {LocalTime.of(1, 2, 3, 123_456_789), SECONDS, LocalTime.of(1, 2, 3)}, + {LocalTime.of(1, 2, 3, 123_456_789), MINUTES, LocalTime.of(1, 2)}, + {LocalTime.of(1, 2, 3, 123_456_789), HOURS, LocalTime.of(1, 0)}, + {LocalTime.of(1, 2, 3, 123_456_789), DAYS, LocalTime.MIDNIGHT}, + }; + } + + @Test(groups={"tck"}, dataProvider="truncatedToValid") + public void test_truncatedTo_valid(LocalTime input, TemporalUnit unit, LocalTime expected) { + assertEquals(input.truncatedTo(unit), expected); + } + + @DataProvider(name="truncatedToInvalid") + Object[][] data_truncatedToInvalid() { + return new Object[][] { + {LocalTime.of(1, 2, 3, 123_456_789), WEEKS}, + {LocalTime.of(1, 2, 3, 123_456_789), MONTHS}, + {LocalTime.of(1, 2, 3, 123_456_789), YEARS}, + }; + } + + @Test(groups={"tck"}, dataProvider="truncatedToInvalid", expectedExceptions=DateTimeException.class) + public void test_truncatedTo_invalid(LocalTime input, TemporalUnit unit) { + input.truncatedTo(unit); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_truncatedTo_null() { + TEST_12_30_40_987654321.truncatedTo(null); + } + + //----------------------------------------------------------------------- + // plus(PlusAdjuster) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plus_Adjuster_positiveHours() { + TemporalAdder period = MockSimplePeriod.of(7, ChronoUnit.HOURS); + LocalTime t = TEST_12_30_40_987654321.plus(period); + assertEquals(t, LocalTime.of(19, 30, 40, 987654321)); + } + + @Test(groups={"tck"}) + public void test_plus_Adjuster_negativeMinutes() { + TemporalAdder period = MockSimplePeriod.of(-25, ChronoUnit.MINUTES); + LocalTime t = TEST_12_30_40_987654321.plus(period); + assertEquals(t, LocalTime.of(12, 5, 40, 987654321)); + } + + @Test(groups={"tck"}) + public void test_plus_Adjuster_zero() { + TemporalAdder period = Period.ZERO; + LocalTime t = TEST_12_30_40_987654321.plus(period); + assertEquals(t, TEST_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_plus_Adjuster_wrap() { + TemporalAdder p = Period.ofTime(1, 0, 0); + LocalTime t = LocalTime.of(23, 30).plus(p); + assertEquals(t, LocalTime.of(0, 30)); + } + + @Test(groups={"tck"}, expectedExceptions=DateTimeException.class) + public void test_plus_Adjuster_dateNotAllowed() { + TemporalAdder period = MockSimplePeriod.of(7, ChronoUnit.MONTHS); + TEST_12_30_40_987654321.plus(period); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_plus_Adjuster_null() { + TEST_12_30_40_987654321.plus((TemporalAdder) null); + } + + //----------------------------------------------------------------------- + // plus(long,TemporalUnit) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plus_longTemporalUnit_positiveHours() { + LocalTime t = TEST_12_30_40_987654321.plus(7, ChronoUnit.HOURS); + assertEquals(t, LocalTime.of(19, 30, 40, 987654321)); + } + + @Test(groups={"tck"}) + public void test_plus_longTemporalUnit_negativeMinutes() { + LocalTime t = TEST_12_30_40_987654321.plus(-25, ChronoUnit.MINUTES); + assertEquals(t, LocalTime.of(12, 5, 40, 987654321)); + } + + @Test(groups={"tck"}) + public void test_plus_longTemporalUnit_zero() { + LocalTime t = TEST_12_30_40_987654321.plus(0, ChronoUnit.MINUTES); + assertEquals(t, TEST_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_plus_longTemporalUnit_invalidUnit() { + for (TemporalUnit unit : INVALID_UNITS) { + try { + TEST_12_30_40_987654321.plus(1, unit); + fail("Unit should not be allowed " + unit); + } catch (DateTimeException ex) { + // expected + } + } + } + + @Test(groups={"tck"}) + public void test_plus_longTemporalUnit_multiples() { + assertEquals(TEST_12_30_40_987654321.plus(0, DAYS), TEST_12_30_40_987654321); + assertEquals(TEST_12_30_40_987654321.plus(1, DAYS), TEST_12_30_40_987654321); + assertEquals(TEST_12_30_40_987654321.plus(2, DAYS), TEST_12_30_40_987654321); + assertEquals(TEST_12_30_40_987654321.plus(-3, DAYS), TEST_12_30_40_987654321); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_plus_longTemporalUnit_null() { + TEST_12_30_40_987654321.plus(1, (TemporalUnit) null); + } + + //----------------------------------------------------------------------- + // plus(adjuster) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plus_adjuster() { + Period p = Period.ofTime(0, 0, 62, 3); + LocalTime t = TEST_12_30_40_987654321.plus(p); + assertEquals(t, LocalTime.of(12, 31, 42, 987654324)); + } + + @Test(groups={"tck"}) + public void test_plus_adjuster_big() { + Period p = Period.ofTime(0, 0, 0, Long.MAX_VALUE); + LocalTime t = TEST_12_30_40_987654321.plus(p); + assertEquals(t, TEST_12_30_40_987654321.plusNanos(Long.MAX_VALUE)); + } + + @Test(groups={"tck"}) + public void test_plus_adjuster_zero_equal() { + LocalTime t = TEST_12_30_40_987654321.plus(Period.ZERO); + assertEquals(t, TEST_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_plus_adjuster_wrap() { + Period p = Period.ofTime(1, 0, 0); + LocalTime t = LocalTime.of(23, 30).plus(p); + assertEquals(t, LocalTime.of(0, 30)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_plus_adjuster_null() { + TEST_12_30_40_987654321.plus((TemporalAdder) null); + } + + //----------------------------------------------------------------------- + // plusHours() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusHours_one() { + LocalTime t = LocalTime.MIDNIGHT; + for (int i = 0; i < 50; i++) { + t = t.plusHours(1); + assertEquals(t.getHour(), (i + 1) % 24); + } + } + + @Test(groups={"tck"}) + public void test_plusHours_fromZero() { + LocalTime base = LocalTime.MIDNIGHT; + for (int i = -50; i < 50; i++) { + LocalTime t = base.plusHours(i); + assertEquals(t.getHour(), (i + 72) % 24); + } + } + + @Test(groups={"tck"}) + public void test_plusHours_fromOne() { + LocalTime base = LocalTime.of(1, 0); + for (int i = -50; i < 50; i++) { + LocalTime t = base.plusHours(i); + assertEquals(t.getHour(), (1 + i + 72) % 24); + } + } + + @Test(groups={"tck"}) + public void test_plusHours_noChange_equal() { + LocalTime t = TEST_12_30_40_987654321.plusHours(0); + assertEquals(t, TEST_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_plusHours_toMidnight_equal() { + LocalTime t = LocalTime.of(23, 0).plusHours(1); + assertEquals(t, LocalTime.MIDNIGHT); + } + + @Test(groups={"tck"}) + public void test_plusHours_toMidday_equal() { + LocalTime t = LocalTime.of(11, 0).plusHours(1); + assertEquals(t, LocalTime.NOON); + } + + @Test(groups={"tck"}) + public void test_plusHours_big() { + LocalTime t = LocalTime.of(2, 30).plusHours(Long.MAX_VALUE); + int hours = (int) (Long.MAX_VALUE % 24L); + assertEquals(t, LocalTime.of(2, 30).plusHours(hours)); + } + + //----------------------------------------------------------------------- + // plusMinutes() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusMinutes_one() { + LocalTime t = LocalTime.MIDNIGHT; + int hour = 0; + int min = 0; + for (int i = 0; i < 70; i++) { + t = t.plusMinutes(1); + min++; + if (min == 60) { + hour++; + min = 0; + } + assertEquals(t.getHour(), hour); + assertEquals(t.getMinute(), min); + } + } + + @Test(groups={"tck"}) + public void test_plusMinutes_fromZero() { + LocalTime base = LocalTime.MIDNIGHT; + int hour; + int min; + for (int i = -70; i < 70; i++) { + LocalTime t = base.plusMinutes(i); + if (i < -60) { + hour = 22; + min = i + 120; + } else if (i < 0) { + hour = 23; + min = i + 60; + } else if (i >= 60) { + hour = 1; + min = i - 60; + } else { + hour = 0; + min = i; + } + assertEquals(t.getHour(), hour); + assertEquals(t.getMinute(), min); + } + } + + @Test(groups={"tck"}) + public void test_plusMinutes_noChange_equal() { + LocalTime t = TEST_12_30_40_987654321.plusMinutes(0); + assertEquals(t, TEST_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_plusMinutes_noChange_oneDay_equal() { + LocalTime t = TEST_12_30_40_987654321.plusMinutes(24 * 60); + assertEquals(t, TEST_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_plusMinutes_toMidnight_equal() { + LocalTime t = LocalTime.of(23, 59).plusMinutes(1); + assertEquals(t, LocalTime.MIDNIGHT); + } + + @Test(groups={"tck"}) + public void test_plusMinutes_toMidday_equal() { + LocalTime t = LocalTime.of(11, 59).plusMinutes(1); + assertEquals(t, LocalTime.NOON); + } + + @Test(groups={"tck"}) + public void test_plusMinutes_big() { + LocalTime t = LocalTime.of(2, 30).plusMinutes(Long.MAX_VALUE); + int mins = (int) (Long.MAX_VALUE % (24L * 60L)); + assertEquals(t, LocalTime.of(2, 30).plusMinutes(mins)); + } + + //----------------------------------------------------------------------- + // plusSeconds() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusSeconds_one() { + LocalTime t = LocalTime.MIDNIGHT; + int hour = 0; + int min = 0; + int sec = 0; + for (int i = 0; i < 3700; i++) { + t = t.plusSeconds(1); + sec++; + if (sec == 60) { + min++; + sec = 0; + } + if (min == 60) { + hour++; + min = 0; + } + assertEquals(t.getHour(), hour); + assertEquals(t.getMinute(), min); + assertEquals(t.getSecond(), sec); + } + } + + @DataProvider(name="plusSeconds_fromZero") + Iterator plusSeconds_fromZero() { + return new Iterator() { + int delta = 30; + int i = -3660; + int hour = 22; + int min = 59; + int sec = 0; + + public boolean hasNext() { + return i <= 3660; + } + + public Object[] next() { + final Object[] ret = new Object[] {i, hour, min, sec}; + i += delta; + sec += delta; + + if (sec >= 60) { + min++; + sec -= 60; + + if (min == 60) { + hour++; + min = 0; + + if (hour == 24) { + hour = 0; + } + } + } + + return ret; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + @Test(dataProvider="plusSeconds_fromZero", groups={"tck"}) + public void test_plusSeconds_fromZero(int seconds, int hour, int min, int sec) { + LocalTime base = LocalTime.MIDNIGHT; + LocalTime t = base.plusSeconds(seconds); + + assertEquals(hour, t.getHour()); + assertEquals(min, t.getMinute()); + assertEquals(sec, t.getSecond()); + } + + @Test(groups={"tck"}) + public void test_plusSeconds_noChange_equal() { + LocalTime t = TEST_12_30_40_987654321.plusSeconds(0); + assertEquals(t, TEST_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_plusSeconds_noChange_oneDay_equal() { + LocalTime t = TEST_12_30_40_987654321.plusSeconds(24 * 60 * 60); + assertEquals(t, TEST_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_plusSeconds_toMidnight_equal() { + LocalTime t = LocalTime.of(23, 59, 59).plusSeconds(1); + assertEquals(t, LocalTime.MIDNIGHT); + } + + @Test(groups={"tck"}) + public void test_plusSeconds_toMidday_equal() { + LocalTime t = LocalTime.of(11, 59, 59).plusSeconds(1); + assertEquals(t, LocalTime.NOON); + } + + //----------------------------------------------------------------------- + // plusNanos() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusNanos_halfABillion() { + LocalTime t = LocalTime.MIDNIGHT; + int hour = 0; + int min = 0; + int sec = 0; + int nanos = 0; + for (long i = 0; i < 3700 * 1000000000L; i+= 500000000) { + t = t.plusNanos(500000000); + nanos += 500000000; + if (nanos == 1000000000) { + sec++; + nanos = 0; + } + if (sec == 60) { + min++; + sec = 0; + } + if (min == 60) { + hour++; + min = 0; + } + assertEquals(t.getHour(), hour); + assertEquals(t.getMinute(), min); + assertEquals(t.getSecond(), sec); + assertEquals(t.getNano(), nanos); + } + } + + @DataProvider(name="plusNanos_fromZero") + Iterator plusNanos_fromZero() { + return new Iterator() { + long delta = 7500000000L; + long i = -3660 * 1000000000L; + int hour = 22; + int min = 59; + int sec = 0; + long nanos = 0; + + public boolean hasNext() { + return i <= 3660 * 1000000000L; + } + + public Object[] next() { + final Object[] ret = new Object[] {i, hour, min, sec, (int)nanos}; + i += delta; + nanos += delta; + + if (nanos >= 1000000000L) { + sec += nanos / 1000000000L; + nanos %= 1000000000L; + + if (sec >= 60) { + min++; + sec %= 60; + + if (min == 60) { + hour++; + min = 0; + + if (hour == 24) { + hour = 0; + } + } + } + } + + return ret; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + @Test(dataProvider="plusNanos_fromZero", groups={"tck"}) + public void test_plusNanos_fromZero(long nanoseconds, int hour, int min, int sec, int nanos) { + LocalTime base = LocalTime.MIDNIGHT; + LocalTime t = base.plusNanos(nanoseconds); + + assertEquals(hour, t.getHour()); + assertEquals(min, t.getMinute()); + assertEquals(sec, t.getSecond()); + assertEquals(nanos, t.getNano()); + } + + @Test(groups={"tck"}) + public void test_plusNanos_noChange_equal() { + LocalTime t = TEST_12_30_40_987654321.plusNanos(0); + assertEquals(t, TEST_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_plusNanos_noChange_oneDay_equal() { + LocalTime t = TEST_12_30_40_987654321.plusNanos(24 * 60 * 60 * 1000000000L); + assertEquals(t, TEST_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_plusNanos_toMidnight_equal() { + LocalTime t = LocalTime.of(23, 59, 59, 999999999).plusNanos(1); + assertEquals(t, LocalTime.MIDNIGHT); + } + + @Test(groups={"tck"}) + public void test_plusNanos_toMidday_equal() { + LocalTime t = LocalTime.of(11, 59, 59, 999999999).plusNanos(1); + assertEquals(t, LocalTime.NOON); + } + + //----------------------------------------------------------------------- + // minus(MinusAdjuster) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minus_Adjuster() { + TemporalSubtractor p = Period.ofTime(0, 0, 62, 3); + LocalTime t = TEST_12_30_40_987654321.minus(p); + assertEquals(t, LocalTime.of(12, 29, 38, 987654318)); + } + + @Test(groups={"tck"}) + public void test_minus_Adjuster_positiveHours() { + TemporalSubtractor period = MockSimplePeriod.of(7, ChronoUnit.HOURS); + LocalTime t = TEST_12_30_40_987654321.minus(period); + assertEquals(t, LocalTime.of(5, 30, 40, 987654321)); + } + + @Test(groups={"tck"}) + public void test_minus_Adjuster_negativeMinutes() { + TemporalSubtractor period = MockSimplePeriod.of(-25, ChronoUnit.MINUTES); + LocalTime t = TEST_12_30_40_987654321.minus(period); + assertEquals(t, LocalTime.of(12, 55, 40, 987654321)); + } + + @Test(groups={"tck"}) + public void test_minus_Adjuster_big1() { + TemporalSubtractor p = Period.ofTime(0, 0, 0, Long.MAX_VALUE); + LocalTime t = TEST_12_30_40_987654321.minus(p); + assertEquals(t, TEST_12_30_40_987654321.minusNanos(Long.MAX_VALUE)); + } + + @Test(groups={"tck"}) + public void test_minus_Adjuster_zero() { + TemporalSubtractor p = Period.ZERO; + LocalTime t = TEST_12_30_40_987654321.minus(p); + assertEquals(t, TEST_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_minus_Adjuster_wrap() { + TemporalSubtractor p = Period.ofTime(1, 0, 0); + LocalTime t = LocalTime.of(0, 30).minus(p); + assertEquals(t, LocalTime.of(23, 30)); + } + + @Test(groups={"tck"}, expectedExceptions=DateTimeException.class) + public void test_minus_Adjuster_dateNotAllowed() { + TemporalSubtractor period = MockSimplePeriod.of(7, ChronoUnit.MONTHS); + TEST_12_30_40_987654321.minus(period); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_minus_Adjuster_null() { + TEST_12_30_40_987654321.minus((TemporalSubtractor) null); + } + + //----------------------------------------------------------------------- + // minus(long,TemporalUnit) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minus_longTemporalUnit_positiveHours() { + LocalTime t = TEST_12_30_40_987654321.minus(7, ChronoUnit.HOURS); + assertEquals(t, LocalTime.of(5, 30, 40, 987654321)); + } + + @Test(groups={"tck"}) + public void test_minus_longTemporalUnit_negativeMinutes() { + LocalTime t = TEST_12_30_40_987654321.minus(-25, ChronoUnit.MINUTES); + assertEquals(t, LocalTime.of(12, 55, 40, 987654321)); + } + + @Test(groups={"tck"}) + public void test_minus_longTemporalUnit_zero() { + LocalTime t = TEST_12_30_40_987654321.minus(0, ChronoUnit.MINUTES); + assertEquals(t, TEST_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_minus_longTemporalUnit_invalidUnit() { + for (TemporalUnit unit : INVALID_UNITS) { + try { + TEST_12_30_40_987654321.minus(1, unit); + fail("Unit should not be allowed " + unit); + } catch (DateTimeException ex) { + // expected + } + } + } + + @Test(groups={"tck"}) + public void test_minus_longTemporalUnit_long_multiples() { + assertEquals(TEST_12_30_40_987654321.minus(0, DAYS), TEST_12_30_40_987654321); + assertEquals(TEST_12_30_40_987654321.minus(1, DAYS), TEST_12_30_40_987654321); + assertEquals(TEST_12_30_40_987654321.minus(2, DAYS), TEST_12_30_40_987654321); + assertEquals(TEST_12_30_40_987654321.minus(-3, DAYS), TEST_12_30_40_987654321); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_minus_longTemporalUnit_null() { + TEST_12_30_40_987654321.minus(1, (TemporalUnit) null); + } + + //----------------------------------------------------------------------- + // minusHours() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusHours_one() { + LocalTime t = LocalTime.MIDNIGHT; + for (int i = 0; i < 50; i++) { + t = t.minusHours(1); + assertEquals(t.getHour(), (((-i + 23) % 24) + 24) % 24, String.valueOf(i)); + } + } + + @Test(groups={"tck"}) + public void test_minusHours_fromZero() { + LocalTime base = LocalTime.MIDNIGHT; + for (int i = -50; i < 50; i++) { + LocalTime t = base.minusHours(i); + assertEquals(t.getHour(), ((-i % 24) + 24) % 24); + } + } + + @Test(groups={"tck"}) + public void test_minusHours_fromOne() { + LocalTime base = LocalTime.of(1, 0); + for (int i = -50; i < 50; i++) { + LocalTime t = base.minusHours(i); + assertEquals(t.getHour(), (1 + (-i % 24) + 24) % 24); + } + } + + @Test(groups={"tck"}) + public void test_minusHours_noChange_equal() { + LocalTime t = TEST_12_30_40_987654321.minusHours(0); + assertEquals(t, TEST_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_minusHours_toMidnight_equal() { + LocalTime t = LocalTime.of(1, 0).minusHours(1); + assertEquals(t, LocalTime.MIDNIGHT); + } + + @Test(groups={"tck"}) + public void test_minusHours_toMidday_equal() { + LocalTime t = LocalTime.of(13, 0).minusHours(1); + assertEquals(t, LocalTime.NOON); + } + + @Test(groups={"tck"}) + public void test_minusHours_big() { + LocalTime t = LocalTime.of(2, 30).minusHours(Long.MAX_VALUE); + int hours = (int) (Long.MAX_VALUE % 24L); + assertEquals(t, LocalTime.of(2, 30).minusHours(hours)); + } + + //----------------------------------------------------------------------- + // minusMinutes() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusMinutes_one() { + LocalTime t = LocalTime.MIDNIGHT; + int hour = 0; + int min = 0; + for (int i = 0; i < 70; i++) { + t = t.minusMinutes(1); + min--; + if (min == -1) { + hour--; + min = 59; + + if (hour == -1) { + hour = 23; + } + } + assertEquals(t.getHour(), hour); + assertEquals(t.getMinute(), min); + } + } + + @Test(groups={"tck"}) + public void test_minusMinutes_fromZero() { + LocalTime base = LocalTime.MIDNIGHT; + int hour = 22; + int min = 49; + for (int i = 70; i > -70; i--) { + LocalTime t = base.minusMinutes(i); + min++; + + if (min == 60) { + hour++; + min = 0; + + if (hour == 24) { + hour = 0; + } + } + + assertEquals(t.getHour(), hour); + assertEquals(t.getMinute(), min); + } + } + + @Test(groups={"tck"}) + public void test_minusMinutes_noChange_equal() { + LocalTime t = TEST_12_30_40_987654321.minusMinutes(0); + assertEquals(t, TEST_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_minusMinutes_noChange_oneDay_equal() { + LocalTime t = TEST_12_30_40_987654321.minusMinutes(24 * 60); + assertEquals(t, TEST_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_minusMinutes_toMidnight_equal() { + LocalTime t = LocalTime.of(0, 1).minusMinutes(1); + assertEquals(t, LocalTime.MIDNIGHT); + } + + @Test(groups={"tck"}) + public void test_minusMinutes_toMidday_equals() { + LocalTime t = LocalTime.of(12, 1).minusMinutes(1); + assertEquals(t, LocalTime.NOON); + } + + @Test(groups={"tck"}) + public void test_minusMinutes_big() { + LocalTime t = LocalTime.of(2, 30).minusMinutes(Long.MAX_VALUE); + int mins = (int) (Long.MAX_VALUE % (24L * 60L)); + assertEquals(t, LocalTime.of(2, 30).minusMinutes(mins)); + } + + //----------------------------------------------------------------------- + // minusSeconds() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusSeconds_one() { + LocalTime t = LocalTime.MIDNIGHT; + int hour = 0; + int min = 0; + int sec = 0; + for (int i = 0; i < 3700; i++) { + t = t.minusSeconds(1); + sec--; + if (sec == -1) { + min--; + sec = 59; + + if (min == -1) { + hour--; + min = 59; + + if (hour == -1) { + hour = 23; + } + } + } + assertEquals(t.getHour(), hour); + assertEquals(t.getMinute(), min); + assertEquals(t.getSecond(), sec); + } + } + + @DataProvider(name="minusSeconds_fromZero") + Iterator minusSeconds_fromZero() { + return new Iterator() { + int delta = 30; + int i = 3660; + int hour = 22; + int min = 59; + int sec = 0; + + public boolean hasNext() { + return i >= -3660; + } + + public Object[] next() { + final Object[] ret = new Object[] {i, hour, min, sec}; + i -= delta; + sec += delta; + + if (sec >= 60) { + min++; + sec -= 60; + + if (min == 60) { + hour++; + min = 0; + + if (hour == 24) { + hour = 0; + } + } + } + + return ret; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + @Test(dataProvider="minusSeconds_fromZero", groups={"tck"}) + public void test_minusSeconds_fromZero(int seconds, int hour, int min, int sec) { + LocalTime base = LocalTime.MIDNIGHT; + LocalTime t = base.minusSeconds(seconds); + + assertEquals(t.getHour(), hour); + assertEquals(t.getMinute(), min); + assertEquals(t.getSecond(), sec); + } + + @Test(groups={"tck"}) + public void test_minusSeconds_noChange_equal() { + LocalTime t = TEST_12_30_40_987654321.minusSeconds(0); + assertEquals(t, TEST_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_minusSeconds_noChange_oneDay_equal() { + LocalTime t = TEST_12_30_40_987654321.minusSeconds(24 * 60 * 60); + assertEquals(t, TEST_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_minusSeconds_toMidnight_equal() { + LocalTime t = LocalTime.of(0, 0, 1).minusSeconds(1); + assertEquals(t, LocalTime.MIDNIGHT); + } + + @Test(groups={"tck"}) + public void test_minusSeconds_toMidday_equal() { + LocalTime t = LocalTime.of(12, 0, 1).minusSeconds(1); + assertEquals(t, LocalTime.NOON); + } + + @Test(groups={"tck"}) + public void test_minusSeconds_big() { + LocalTime t = LocalTime.of(2, 30).minusSeconds(Long.MAX_VALUE); + int secs = (int) (Long.MAX_VALUE % (24L * 60L * 60L)); + assertEquals(t, LocalTime.of(2, 30).minusSeconds(secs)); + } + + //----------------------------------------------------------------------- + // minusNanos() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusNanos_halfABillion() { + LocalTime t = LocalTime.MIDNIGHT; + int hour = 0; + int min = 0; + int sec = 0; + int nanos = 0; + for (long i = 0; i < 3700 * 1000000000L; i+= 500000000) { + t = t.minusNanos(500000000); + nanos -= 500000000; + + if (nanos < 0) { + sec--; + nanos += 1000000000; + + if (sec == -1) { + min--; + sec += 60; + + if (min == -1) { + hour--; + min += 60; + + if (hour == -1) { + hour += 24; + } + } + } + } + + assertEquals(t.getHour(), hour); + assertEquals(t.getMinute(), min); + assertEquals(t.getSecond(), sec); + assertEquals(t.getNano(), nanos); + } + } + + @DataProvider(name="minusNanos_fromZero") + Iterator minusNanos_fromZero() { + return new Iterator() { + long delta = 7500000000L; + long i = 3660 * 1000000000L; + int hour = 22; + int min = 59; + int sec = 0; + long nanos = 0; + + public boolean hasNext() { + return i >= -3660 * 1000000000L; + } + + public Object[] next() { + final Object[] ret = new Object[] {i, hour, min, sec, (int)nanos}; + i -= delta; + nanos += delta; + + if (nanos >= 1000000000L) { + sec += nanos / 1000000000L; + nanos %= 1000000000L; + + if (sec >= 60) { + min++; + sec %= 60; + + if (min == 60) { + hour++; + min = 0; + + if (hour == 24) { + hour = 0; + } + } + } + } + + return ret; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + @Test(dataProvider="minusNanos_fromZero", groups={"tck"}) + public void test_minusNanos_fromZero(long nanoseconds, int hour, int min, int sec, int nanos) { + LocalTime base = LocalTime.MIDNIGHT; + LocalTime t = base.minusNanos(nanoseconds); + + assertEquals(hour, t.getHour()); + assertEquals(min, t.getMinute()); + assertEquals(sec, t.getSecond()); + assertEquals(nanos, t.getNano()); + } + + @Test(groups={"tck"}) + public void test_minusNanos_noChange_equal() { + LocalTime t = TEST_12_30_40_987654321.minusNanos(0); + assertEquals(t, TEST_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_minusNanos_noChange_oneDay_equal() { + LocalTime t = TEST_12_30_40_987654321.minusNanos(24 * 60 * 60 * 1000000000L); + assertEquals(t, TEST_12_30_40_987654321); + } + + @Test(groups={"tck"}) + public void test_minusNanos_toMidnight_equal() { + LocalTime t = LocalTime.of(0, 0, 0, 1).minusNanos(1); + assertEquals(t, LocalTime.MIDNIGHT); + } + + @Test(groups={"tck"}) + public void test_minusNanos_toMidday_equal() { + LocalTime t = LocalTime.of(12, 0, 0, 1).minusNanos(1); + assertEquals(t, LocalTime.NOON); + } + + //----------------------------------------------------------------------- + // atDate() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_atDate() { + LocalTime t = LocalTime.of(11, 30); + assertEquals(t.atDate(LocalDate.of(2012, 6, 30)), LocalDateTime.of(2012, 6, 30, 11, 30)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_atDate_nullDate() { + TEST_12_30_40_987654321.atDate((LocalDate) null); + } + + //----------------------------------------------------------------------- + // atOffset() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_atOffset() { + LocalTime t = LocalTime.of(11, 30); + assertEquals(t.atOffset(OFFSET_PTWO), OffsetTime.of(LocalTime.of(11, 30), OFFSET_PTWO)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_atOffset_nullZoneOffset() { + LocalTime t = LocalTime.of(11, 30); + t.atOffset((ZoneOffset) null); + } + + //----------------------------------------------------------------------- + // toSecondOfDay() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toSecondOfDay() { + LocalTime t = LocalTime.of(0, 0); + for (int i = 0; i < 24 * 60 * 60; i++) { + assertEquals(t.toSecondOfDay(), i); + t = t.plusSeconds(1); + } + } + + @Test(groups={"tck"}) + public void test_toSecondOfDay_fromNanoOfDay_symmetry() { + LocalTime t = LocalTime.of(0, 0); + for (int i = 0; i < 24 * 60 * 60; i++) { + assertEquals(LocalTime.ofSecondOfDay(t.toSecondOfDay()), t); + t = t.plusSeconds(1); + } + } + + //----------------------------------------------------------------------- + // toNanoOfDay() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toNanoOfDay() { + LocalTime t = LocalTime.of(0, 0); + for (int i = 0; i < 1000000; i++) { + assertEquals(t.toNanoOfDay(), i); + t = t.plusNanos(1); + } + t = LocalTime.of(0, 0); + for (int i = 1; i <= 1000000; i++) { + t = t.minusNanos(1); + assertEquals(t.toNanoOfDay(), 24 * 60 * 60 * 1000000000L - i); + } + } + + @Test(groups={"tck"}) + public void test_toNanoOfDay_fromNanoOfDay_symmetry() { + LocalTime t = LocalTime.of(0, 0); + for (int i = 0; i < 1000000; i++) { + assertEquals(LocalTime.ofNanoOfDay(t.toNanoOfDay()), t); + t = t.plusNanos(1); + } + t = LocalTime.of(0, 0); + for (int i = 1; i <= 1000000; i++) { + t = t.minusNanos(1); + assertEquals(LocalTime.ofNanoOfDay(t.toNanoOfDay()), t); + } + } + + //----------------------------------------------------------------------- + // compareTo() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_comparisons() { + doTest_comparisons_LocalTime( + LocalTime.MIDNIGHT, + LocalTime.of(0, 0, 0, 999999999), + LocalTime.of(0, 0, 59, 0), + LocalTime.of(0, 0, 59, 999999999), + LocalTime.of(0, 59, 0, 0), + LocalTime.of(0, 59, 0, 999999999), + LocalTime.of(0, 59, 59, 0), + LocalTime.of(0, 59, 59, 999999999), + LocalTime.NOON, + LocalTime.of(12, 0, 0, 999999999), + LocalTime.of(12, 0, 59, 0), + LocalTime.of(12, 0, 59, 999999999), + LocalTime.of(12, 59, 0, 0), + LocalTime.of(12, 59, 0, 999999999), + LocalTime.of(12, 59, 59, 0), + LocalTime.of(12, 59, 59, 999999999), + LocalTime.of(23, 0, 0, 0), + LocalTime.of(23, 0, 0, 999999999), + LocalTime.of(23, 0, 59, 0), + LocalTime.of(23, 0, 59, 999999999), + LocalTime.of(23, 59, 0, 0), + LocalTime.of(23, 59, 0, 999999999), + LocalTime.of(23, 59, 59, 0), + LocalTime.of(23, 59, 59, 999999999) + ); + } + + void doTest_comparisons_LocalTime(LocalTime... localTimes) { + for (int i = 0; i < localTimes.length; i++) { + LocalTime a = localTimes[i]; + for (int j = 0; j < localTimes.length; j++) { + LocalTime b = localTimes[j]; + if (i < j) { + assertTrue(a.compareTo(b) < 0, a + " <=> " + b); + assertEquals(a.isBefore(b), true, a + " <=> " + b); + assertEquals(a.isAfter(b), false, a + " <=> " + b); + assertEquals(a.equals(b), false, a + " <=> " + b); + } else if (i > j) { + assertTrue(a.compareTo(b) > 0, a + " <=> " + b); + assertEquals(a.isBefore(b), false, a + " <=> " + b); + assertEquals(a.isAfter(b), true, a + " <=> " + b); + assertEquals(a.equals(b), false, a + " <=> " + b); + } else { + assertEquals(a.compareTo(b), 0, a + " <=> " + b); + assertEquals(a.isBefore(b), false, a + " <=> " + b); + assertEquals(a.isAfter(b), false, a + " <=> " + b); + assertEquals(a.equals(b), true, a + " <=> " + b); + } + } + } + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_compareTo_ObjectNull() { + TEST_12_30_40_987654321.compareTo(null); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_isBefore_ObjectNull() { + TEST_12_30_40_987654321.isBefore(null); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_isAfter_ObjectNull() { + TEST_12_30_40_987654321.isAfter(null); + } + + @Test(expectedExceptions=ClassCastException.class, groups={"tck"}) + @SuppressWarnings({"unchecked", "rawtypes"}) + public void compareToNonLocalTime() { + Comparable c = TEST_12_30_40_987654321; + c.compareTo(new Object()); + } + + //----------------------------------------------------------------------- + // equals() + //----------------------------------------------------------------------- + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_true(int h, int m, int s, int n) { + LocalTime a = LocalTime.of(h, m, s, n); + LocalTime b = LocalTime.of(h, m, s, n); + assertEquals(a.equals(b), true); + } + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_false_hour_differs(int h, int m, int s, int n) { + LocalTime a = LocalTime.of(h, m, s, n); + LocalTime b = LocalTime.of(h + 1, m, s, n); + assertEquals(a.equals(b), false); + } + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_false_minute_differs(int h, int m, int s, int n) { + LocalTime a = LocalTime.of(h, m, s, n); + LocalTime b = LocalTime.of(h, m + 1, s, n); + assertEquals(a.equals(b), false); + } + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_false_second_differs(int h, int m, int s, int n) { + LocalTime a = LocalTime.of(h, m, s, n); + LocalTime b = LocalTime.of(h, m, s + 1, n); + assertEquals(a.equals(b), false); + } + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_false_nano_differs(int h, int m, int s, int n) { + LocalTime a = LocalTime.of(h, m, s, n); + LocalTime b = LocalTime.of(h, m, s, n + 1); + assertEquals(a.equals(b), false); + } + + @Test(groups={"tck"}) + public void test_equals_itself_true() { + assertEquals(TEST_12_30_40_987654321.equals(TEST_12_30_40_987654321), true); + } + + @Test(groups={"tck"}) + public void test_equals_string_false() { + assertEquals(TEST_12_30_40_987654321.equals("2007-07-15"), false); + } + + @Test(groups={"tck"}) + public void test_equals_null_false() { + assertEquals(TEST_12_30_40_987654321.equals(null), false); + } + + //----------------------------------------------------------------------- + // hashCode() + //----------------------------------------------------------------------- + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_hashCode_same(int h, int m, int s, int n) { + LocalTime a = LocalTime.of(h, m, s, n); + LocalTime b = LocalTime.of(h, m, s, n); + assertEquals(a.hashCode(), b.hashCode()); + } + + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_hashCode_hour_differs(int h, int m, int s, int n) { + LocalTime a = LocalTime.of(h, m, s, n); + LocalTime b = LocalTime.of(h + 1, m, s, n); + assertEquals(a.hashCode() == b.hashCode(), false); + } + + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_hashCode_minute_differs(int h, int m, int s, int n) { + LocalTime a = LocalTime.of(h, m, s, n); + LocalTime b = LocalTime.of(h, m + 1, s, n); + assertEquals(a.hashCode() == b.hashCode(), false); + } + + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_hashCode_second_differs(int h, int m, int s, int n) { + LocalTime a = LocalTime.of(h, m, s, n); + LocalTime b = LocalTime.of(h, m, s + 1, n); + assertEquals(a.hashCode() == b.hashCode(), false); + } + + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_hashCode_nano_differs(int h, int m, int s, int n) { + LocalTime a = LocalTime.of(h, m, s, n); + LocalTime b = LocalTime.of(h, m, s, n + 1); + assertEquals(a.hashCode() == b.hashCode(), false); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @DataProvider(name="sampleToString") + Object[][] provider_sampleToString() { + return new Object[][] { + {0, 0, 0, 0, "00:00"}, + {1, 0, 0, 0, "01:00"}, + {23, 0, 0, 0, "23:00"}, + {0, 1, 0, 0, "00:01"}, + {12, 30, 0, 0, "12:30"}, + {23, 59, 0, 0, "23:59"}, + {0, 0, 1, 0, "00:00:01"}, + {0, 0, 59, 0, "00:00:59"}, + {0, 0, 0, 100000000, "00:00:00.100"}, + {0, 0, 0, 10000000, "00:00:00.010"}, + {0, 0, 0, 1000000, "00:00:00.001"}, + {0, 0, 0, 100000, "00:00:00.000100"}, + {0, 0, 0, 10000, "00:00:00.000010"}, + {0, 0, 0, 1000, "00:00:00.000001"}, + {0, 0, 0, 100, "00:00:00.000000100"}, + {0, 0, 0, 10, "00:00:00.000000010"}, + {0, 0, 0, 1, "00:00:00.000000001"}, + {0, 0, 0, 999999999, "00:00:00.999999999"}, + {0, 0, 0, 99999999, "00:00:00.099999999"}, + {0, 0, 0, 9999999, "00:00:00.009999999"}, + {0, 0, 0, 999999, "00:00:00.000999999"}, + {0, 0, 0, 99999, "00:00:00.000099999"}, + {0, 0, 0, 9999, "00:00:00.000009999"}, + {0, 0, 0, 999, "00:00:00.000000999"}, + {0, 0, 0, 99, "00:00:00.000000099"}, + {0, 0, 0, 9, "00:00:00.000000009"}, + }; + } + + @Test(dataProvider="sampleToString", groups={"tck"}) + public void test_toString(int h, int m, int s, int n, String expected) { + LocalTime t = LocalTime.of(h, m, s, n); + String str = t.toString(); + assertEquals(str, expected); + } + + //----------------------------------------------------------------------- + // toString(DateTimeFormatter) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toString_formatter() { + DateTimeFormatter f = DateTimeFormatters.pattern("H m s"); + String t = LocalTime.of(11, 30, 45).toString(f); + assertEquals(t, "11 30 45"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_toString_formatter_null() { + LocalTime.of(11, 30, 45).toString(null); + } + +} diff --git a/jdk/test/java/time/tck/java/time/TCKMonth.java b/jdk/test/java/time/tck/java/time/TCKMonth.java new file mode 100644 index 00000000000..6f6170d06f8 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/TCKMonth.java @@ -0,0 +1,486 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time; + +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static org.testng.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.Month; +import java.time.format.TextStyle; +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoUnit; +import java.time.temporal.ISOChrono; +import java.time.temporal.JulianFields; +import java.time.temporal.Queries; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalField; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test Month. + */ +@Test +public class TCKMonth extends AbstractDateTimeTest { + + private static final int MAX_LENGTH = 12; + + //----------------------------------------------------------------------- + @Override + protected List samples() { + TemporalAccessor[] array = {Month.JANUARY, Month.JUNE, Month.DECEMBER, }; + return Arrays.asList(array); + } + + @Override + protected List validFields() { + TemporalField[] array = { + MONTH_OF_YEAR, + }; + return Arrays.asList(array); + } + + @Override + protected List invalidFields() { + List list = new ArrayList<>(Arrays.asList(ChronoField.values())); + list.removeAll(validFields()); + list.add(JulianFields.JULIAN_DAY); + list.add(JulianFields.MODIFIED_JULIAN_DAY); + list.add(JulianFields.RATA_DIE); + return list; + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_factory_int_singleton() { + for (int i = 1; i <= MAX_LENGTH; i++) { + Month test = Month.of(i); + assertEquals(test.getValue(), i); + } + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_int_tooLow() { + Month.of(0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_int_tooHigh() { + Month.of(13); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_factory_CalendricalObject() { + assertEquals(Month.from(LocalDate.of(2011, 6, 6)), Month.JUNE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_CalendricalObject_invalid_noDerive() { + Month.from(LocalTime.of(12, 30)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_factory_CalendricalObject_null() { + Month.from((TemporalAccessor) null); + } + + //----------------------------------------------------------------------- + // get(TemporalField) + //----------------------------------------------------------------------- + @Test + public void test_get_TemporalField() { + assertEquals(Month.JULY.get(ChronoField.MONTH_OF_YEAR), 7); + } + + @Test + public void test_getLong_TemporalField() { + assertEquals(Month.JULY.getLong(ChronoField.MONTH_OF_YEAR), 7); + } + + //----------------------------------------------------------------------- + // query(TemporalQuery) + //----------------------------------------------------------------------- + @Test + public void test_query_chrono() { + assertEquals(Month.JUNE.query(Queries.chrono()), ISOChrono.INSTANCE); + assertEquals(Queries.chrono().queryFrom(Month.JUNE), ISOChrono.INSTANCE); + } + + @Test + public void test_query_zoneId() { + assertEquals(Month.JUNE.query(Queries.zoneId()), null); + assertEquals(Queries.zoneId().queryFrom(Month.JUNE), null); + } + + @Test + public void test_query_precision() { + assertEquals(Month.JUNE.query(Queries.precision()), ChronoUnit.MONTHS); + assertEquals(Queries.precision().queryFrom(Month.JUNE), ChronoUnit.MONTHS); + } + + @Test + public void test_query_offset() { + assertEquals(Month.JUNE.query(Queries.offset()), null); + assertEquals(Queries.offset().queryFrom(Month.JUNE), null); + } + + @Test + public void test_query_zone() { + assertEquals(Month.JUNE.query(Queries.zone()), null); + assertEquals(Queries.zone().queryFrom(Month.JUNE), null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_query_null() { + Month.JUNE.query(null); + } + + //----------------------------------------------------------------------- + // getText() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_getText() { + assertEquals(Month.JANUARY.getText(TextStyle.SHORT, Locale.US), "Jan"); + } + + @Test(expectedExceptions = NullPointerException.class, groups={"tck"}) + public void test_getText_nullStyle() { + Month.JANUARY.getText(null, Locale.US); + } + + @Test(expectedExceptions = NullPointerException.class, groups={"tck"}) + public void test_getText_nullLocale() { + Month.JANUARY.getText(TextStyle.FULL, null); + } + + //----------------------------------------------------------------------- + // plus(long), plus(long,unit) + //----------------------------------------------------------------------- + @DataProvider(name="plus") + Object[][] data_plus() { + return new Object[][] { + {1, -13, 12}, + {1, -12, 1}, + {1, -11, 2}, + {1, -10, 3}, + {1, -9, 4}, + {1, -8, 5}, + {1, -7, 6}, + {1, -6, 7}, + {1, -5, 8}, + {1, -4, 9}, + {1, -3, 10}, + {1, -2, 11}, + {1, -1, 12}, + {1, 0, 1}, + {1, 1, 2}, + {1, 2, 3}, + {1, 3, 4}, + {1, 4, 5}, + {1, 5, 6}, + {1, 6, 7}, + {1, 7, 8}, + {1, 8, 9}, + {1, 9, 10}, + {1, 10, 11}, + {1, 11, 12}, + {1, 12, 1}, + {1, 13, 2}, + + {1, 1, 2}, + {2, 1, 3}, + {3, 1, 4}, + {4, 1, 5}, + {5, 1, 6}, + {6, 1, 7}, + {7, 1, 8}, + {8, 1, 9}, + {9, 1, 10}, + {10, 1, 11}, + {11, 1, 12}, + {12, 1, 1}, + + {1, -1, 12}, + {2, -1, 1}, + {3, -1, 2}, + {4, -1, 3}, + {5, -1, 4}, + {6, -1, 5}, + {7, -1, 6}, + {8, -1, 7}, + {9, -1, 8}, + {10, -1, 9}, + {11, -1, 10}, + {12, -1, 11}, + }; + } + + @Test(dataProvider="plus", groups={"tck"}) + public void test_plus_long(int base, long amount, int expected) { + assertEquals(Month.of(base).plus(amount), Month.of(expected)); + } + + //----------------------------------------------------------------------- + // minus(long), minus(long,unit) + //----------------------------------------------------------------------- + @DataProvider(name="minus") + Object[][] data_minus() { + return new Object[][] { + {1, -13, 2}, + {1, -12, 1}, + {1, -11, 12}, + {1, -10, 11}, + {1, -9, 10}, + {1, -8, 9}, + {1, -7, 8}, + {1, -6, 7}, + {1, -5, 6}, + {1, -4, 5}, + {1, -3, 4}, + {1, -2, 3}, + {1, -1, 2}, + {1, 0, 1}, + {1, 1, 12}, + {1, 2, 11}, + {1, 3, 10}, + {1, 4, 9}, + {1, 5, 8}, + {1, 6, 7}, + {1, 7, 6}, + {1, 8, 5}, + {1, 9, 4}, + {1, 10, 3}, + {1, 11, 2}, + {1, 12, 1}, + {1, 13, 12}, + }; + } + + @Test(dataProvider="minus", groups={"tck"}) + public void test_minus_long(int base, long amount, int expected) { + assertEquals(Month.of(base).minus(amount), Month.of(expected)); + } + + //----------------------------------------------------------------------- + // length(boolean) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_length_boolean_notLeapYear() { + assertEquals(Month.JANUARY.length(false), 31); + assertEquals(Month.FEBRUARY.length(false), 28); + assertEquals(Month.MARCH.length(false), 31); + assertEquals(Month.APRIL.length(false), 30); + assertEquals(Month.MAY.length(false), 31); + assertEquals(Month.JUNE.length(false), 30); + assertEquals(Month.JULY.length(false), 31); + assertEquals(Month.AUGUST.length(false), 31); + assertEquals(Month.SEPTEMBER.length(false), 30); + assertEquals(Month.OCTOBER.length(false), 31); + assertEquals(Month.NOVEMBER.length(false), 30); + assertEquals(Month.DECEMBER.length(false), 31); + } + + @Test(groups={"tck"}) + public void test_length_boolean_leapYear() { + assertEquals(Month.JANUARY.length(true), 31); + assertEquals(Month.FEBRUARY.length(true), 29); + assertEquals(Month.MARCH.length(true), 31); + assertEquals(Month.APRIL.length(true), 30); + assertEquals(Month.MAY.length(true), 31); + assertEquals(Month.JUNE.length(true), 30); + assertEquals(Month.JULY.length(true), 31); + assertEquals(Month.AUGUST.length(true), 31); + assertEquals(Month.SEPTEMBER.length(true), 30); + assertEquals(Month.OCTOBER.length(true), 31); + assertEquals(Month.NOVEMBER.length(true), 30); + assertEquals(Month.DECEMBER.length(true), 31); + } + + //----------------------------------------------------------------------- + // minLength() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minLength() { + assertEquals(Month.JANUARY.minLength(), 31); + assertEquals(Month.FEBRUARY.minLength(), 28); + assertEquals(Month.MARCH.minLength(), 31); + assertEquals(Month.APRIL.minLength(), 30); + assertEquals(Month.MAY.minLength(), 31); + assertEquals(Month.JUNE.minLength(), 30); + assertEquals(Month.JULY.minLength(), 31); + assertEquals(Month.AUGUST.minLength(), 31); + assertEquals(Month.SEPTEMBER.minLength(), 30); + assertEquals(Month.OCTOBER.minLength(), 31); + assertEquals(Month.NOVEMBER.minLength(), 30); + assertEquals(Month.DECEMBER.minLength(), 31); + } + + //----------------------------------------------------------------------- + // maxLength() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_maxLength() { + assertEquals(Month.JANUARY.maxLength(), 31); + assertEquals(Month.FEBRUARY.maxLength(), 29); + assertEquals(Month.MARCH.maxLength(), 31); + assertEquals(Month.APRIL.maxLength(), 30); + assertEquals(Month.MAY.maxLength(), 31); + assertEquals(Month.JUNE.maxLength(), 30); + assertEquals(Month.JULY.maxLength(), 31); + assertEquals(Month.AUGUST.maxLength(), 31); + assertEquals(Month.SEPTEMBER.maxLength(), 30); + assertEquals(Month.OCTOBER.maxLength(), 31); + assertEquals(Month.NOVEMBER.maxLength(), 30); + assertEquals(Month.DECEMBER.maxLength(), 31); + } + + //----------------------------------------------------------------------- + // firstDayOfYear(boolean) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_firstDayOfYear_notLeapYear() { + assertEquals(Month.JANUARY.firstDayOfYear(false), 1); + assertEquals(Month.FEBRUARY.firstDayOfYear(false), 1 + 31); + assertEquals(Month.MARCH.firstDayOfYear(false), 1 + 31 + 28); + assertEquals(Month.APRIL.firstDayOfYear(false), 1 + 31 + 28 + 31); + assertEquals(Month.MAY.firstDayOfYear(false), 1 + 31 + 28 + 31 + 30); + assertEquals(Month.JUNE.firstDayOfYear(false), 1 + 31 + 28 + 31 + 30 + 31); + assertEquals(Month.JULY.firstDayOfYear(false), 1 + 31 + 28 + 31 + 30 + 31 + 30); + assertEquals(Month.AUGUST.firstDayOfYear(false), 1 + 31 + 28 + 31 + 30 + 31 + 30 + 31); + assertEquals(Month.SEPTEMBER.firstDayOfYear(false), 1 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31); + assertEquals(Month.OCTOBER.firstDayOfYear(false), 1 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30); + assertEquals(Month.NOVEMBER.firstDayOfYear(false), 1 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31); + assertEquals(Month.DECEMBER.firstDayOfYear(false), 1 + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30); + } + + @Test(groups={"tck"}) + public void test_firstDayOfYear_leapYear() { + assertEquals(Month.JANUARY.firstDayOfYear(true), 1); + assertEquals(Month.FEBRUARY.firstDayOfYear(true), 1 + 31); + assertEquals(Month.MARCH.firstDayOfYear(true), 1 + 31 + 29); + assertEquals(Month.APRIL.firstDayOfYear(true), 1 + 31 + 29 + 31); + assertEquals(Month.MAY.firstDayOfYear(true), 1 + 31 + 29 + 31 + 30); + assertEquals(Month.JUNE.firstDayOfYear(true), 1 + 31 + 29 + 31 + 30 + 31); + assertEquals(Month.JULY.firstDayOfYear(true), 1 + 31 + 29 + 31 + 30 + 31 + 30); + assertEquals(Month.AUGUST.firstDayOfYear(true), 1 + 31 + 29 + 31 + 30 + 31 + 30 + 31); + assertEquals(Month.SEPTEMBER.firstDayOfYear(true), 1 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31); + assertEquals(Month.OCTOBER.firstDayOfYear(true), 1 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30); + assertEquals(Month.NOVEMBER.firstDayOfYear(true), 1 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31); + assertEquals(Month.DECEMBER.firstDayOfYear(true), 1 + 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30); + } + + //----------------------------------------------------------------------- + // firstMonthOfQuarter() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_firstMonthOfQuarter() { + assertEquals(Month.JANUARY.firstMonthOfQuarter(), Month.JANUARY); + assertEquals(Month.FEBRUARY.firstMonthOfQuarter(), Month.JANUARY); + assertEquals(Month.MARCH.firstMonthOfQuarter(), Month.JANUARY); + assertEquals(Month.APRIL.firstMonthOfQuarter(), Month.APRIL); + assertEquals(Month.MAY.firstMonthOfQuarter(), Month.APRIL); + assertEquals(Month.JUNE.firstMonthOfQuarter(), Month.APRIL); + assertEquals(Month.JULY.firstMonthOfQuarter(), Month.JULY); + assertEquals(Month.AUGUST.firstMonthOfQuarter(), Month.JULY); + assertEquals(Month.SEPTEMBER.firstMonthOfQuarter(), Month.JULY); + assertEquals(Month.OCTOBER.firstMonthOfQuarter(), Month.OCTOBER); + assertEquals(Month.NOVEMBER.firstMonthOfQuarter(), Month.OCTOBER); + assertEquals(Month.DECEMBER.firstMonthOfQuarter(), Month.OCTOBER); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toString() { + assertEquals(Month.JANUARY.toString(), "JANUARY"); + assertEquals(Month.FEBRUARY.toString(), "FEBRUARY"); + assertEquals(Month.MARCH.toString(), "MARCH"); + assertEquals(Month.APRIL.toString(), "APRIL"); + assertEquals(Month.MAY.toString(), "MAY"); + assertEquals(Month.JUNE.toString(), "JUNE"); + assertEquals(Month.JULY.toString(), "JULY"); + assertEquals(Month.AUGUST.toString(), "AUGUST"); + assertEquals(Month.SEPTEMBER.toString(), "SEPTEMBER"); + assertEquals(Month.OCTOBER.toString(), "OCTOBER"); + assertEquals(Month.NOVEMBER.toString(), "NOVEMBER"); + assertEquals(Month.DECEMBER.toString(), "DECEMBER"); + } + + //----------------------------------------------------------------------- + // generated methods + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_enum() { + assertEquals(Month.valueOf("JANUARY"), Month.JANUARY); + assertEquals(Month.values()[0], Month.JANUARY); + } + +} diff --git a/jdk/test/java/time/tck/java/time/TCKZoneId.java b/jdk/test/java/time/tck/java/time/TCKZoneId.java new file mode 100644 index 00000000000..adc2bb6ee2f --- /dev/null +++ b/jdk/test/java/time/tck/java/time/TCKZoneId.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; + +import java.time.ZoneId; + +import org.testng.annotations.Test; + +/** + * Test ZoneId. + */ +@Test +public class TCKZoneId extends AbstractTCKTest { + + //----------------------------------------------------------------------- + @Test + public void test_serialization() throws Exception { + assertSerializable(ZoneId.of("Europe/London")); + } + + @Test + public void test_serialization_format() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baos) ) { + dos.writeByte(7); + dos.writeUTF("Europe/London"); + } + byte[] bytes = baos.toByteArray(); + assertSerializedBySer(ZoneId.of("Europe/London"), bytes); + } + +} diff --git a/jdk/test/java/time/tck/java/time/TCKZoneOffset.java b/jdk/test/java/time/tck/java/time/TCKZoneOffset.java new file mode 100644 index 00000000000..4cfd40d9115 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/TCKZoneOffset.java @@ -0,0 +1,680 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time; + +import static java.time.temporal.ChronoField.OFFSET_SECONDS; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import java.time.DateTimeException; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoField; +import java.time.temporal.JulianFields; +import java.time.temporal.OffsetDate; +import java.time.temporal.Queries; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalField; + +import org.testng.annotations.Test; + +/** + * Test ZoneOffset. + */ +@Test +public class TCKZoneOffset extends AbstractDateTimeTest { + + //----------------------------------------------------------------------- + @Override + protected List samples() { + TemporalAccessor[] array = {ZoneOffset.ofHours(1), ZoneOffset.ofHoursMinutesSeconds(-5, -6, -30) }; + return Arrays.asList(array); + } + + @Override + protected List validFields() { + TemporalField[] array = { + OFFSET_SECONDS, + }; + return Arrays.asList(array); + } + + @Override + protected List invalidFields() { + List list = new ArrayList<>(Arrays.asList(ChronoField.values())); + list.removeAll(validFields()); + list.add(JulianFields.JULIAN_DAY); + list.add(JulianFields.MODIFIED_JULIAN_DAY); + list.add(JulianFields.RATA_DIE); + return list; + } + + //----------------------------------------------------------------------- + @Test + public void test_serialization() throws Exception { + assertSerializable(ZoneOffset.of("+01:30")); + } + + @Test + public void test_serialization_format_quarterPositive() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baos) ) { + dos.writeByte(8); + dos.writeByte(6); // stored as quarter hours + } + byte[] bytes = baos.toByteArray(); + assertSerializedBySer(ZoneOffset.ofHoursMinutes(1, 30), bytes); + } + + @Test + public void test_serialization_format_quarterNegative() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baos) ) { + dos.writeByte(8); + dos.writeByte(-10); // stored as quarter hours + } + byte[] bytes = baos.toByteArray(); + assertSerializedBySer(ZoneOffset.ofHoursMinutes(-2, -30), bytes); + } + + @Test + public void test_serialization_format_full() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baos) ) { + dos.writeByte(8); + dos.writeByte(127); + dos.writeInt(53265); + } + byte[] bytes = baos.toByteArray(); + assertSerializedBySer(ZoneOffset.ofTotalSeconds(53265), bytes); + } + + //----------------------------------------------------------------------- + // constants + //----------------------------------------------------------------------- + @Test + public void test_constant_UTC() { + ZoneOffset test = ZoneOffset.UTC; + doTestOffset(test, 0, 0, 0); + } + + @Test + public void test_constant_MIN() { + ZoneOffset test = ZoneOffset.MIN; + doTestOffset(test, -18, 0, 0); + } + + @Test + public void test_constant_MAX() { + ZoneOffset test = ZoneOffset.MAX; + doTestOffset(test, 18, 0, 0); + } + + //----------------------------------------------------------------------- + // of(String) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_factory_string_UTC() { + String[] values = new String[] { + "Z", "+0", + "+00","+0000","+00:00","+000000","+00:00:00", + "-00","-0000","-00:00","-000000","-00:00:00", + }; + for (int i = 0; i < values.length; i++) { + ZoneOffset test = ZoneOffset.of(values[i]); + assertSame(test, ZoneOffset.UTC); + } + } + + @Test(groups={"tck"}) + public void test_factory_string_invalid() { + String[] values = new String[] { + "","A","B","C","D","E","F","G","H","I","J","K","L","M", + "N","O","P","Q","R","S","T","U","V","W","X","Y","ZZ", + "0", "+0:00","+00:0","+0:0", + "+000","+00000", + "+0:00:00","+00:0:00","+00:00:0","+0:0:0","+0:0:00","+00:0:0","+0:00:0", + "1", "+01_00","+01;00","+01@00","+01:AA", + "+19","+19:00","+18:01","+18:00:01","+1801","+180001", + "-0:00","-00:0","-0:0", + "-000","-00000", + "-0:00:00","-00:0:00","-00:00:0","-0:0:0","-0:0:00","-00:0:0","-0:00:0", + "-19","-19:00","-18:01","-18:00:01","-1801","-180001", + "-01_00","-01;00","-01@00","-01:AA", + "@01:00", + }; + for (int i = 0; i < values.length; i++) { + try { + ZoneOffset.of(values[i]); + fail("Should have failed:" + values[i]); + } catch (DateTimeException ex) { + // expected + } + } + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_factory_string_null() { + ZoneOffset.of((String) null); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_factory_string_singleDigitHours() { + for (int i = -9; i <= 9; i++) { + String str = (i < 0 ? "-" : "+") + Math.abs(i); + ZoneOffset test = ZoneOffset.of(str); + doTestOffset(test, i, 0, 0); + } + } + + @Test(groups={"tck"}) + public void test_factory_string_hours() { + for (int i = -18; i <= 18; i++) { + String str = (i < 0 ? "-" : "+") + Integer.toString(Math.abs(i) + 100).substring(1); + ZoneOffset test = ZoneOffset.of(str); + doTestOffset(test, i, 0, 0); + } + } + + @Test(groups={"tck"}) + public void test_factory_string_hours_minutes_noColon() { + for (int i = -17; i <= 17; i++) { + for (int j = -59; j <= 59; j++) { + if ((i < 0 && j <= 0) || (i > 0 && j >= 0) || i == 0) { + String str = (i < 0 || j < 0 ? "-" : "+") + + Integer.toString(Math.abs(i) + 100).substring(1) + + Integer.toString(Math.abs(j) + 100).substring(1); + ZoneOffset test = ZoneOffset.of(str); + doTestOffset(test, i, j, 0); + } + } + } + ZoneOffset test1 = ZoneOffset.of("-1800"); + doTestOffset(test1, -18, 0, 0); + ZoneOffset test2 = ZoneOffset.of("+1800"); + doTestOffset(test2, 18, 0, 0); + } + + @Test(groups={"tck"}) + public void test_factory_string_hours_minutes_colon() { + for (int i = -17; i <= 17; i++) { + for (int j = -59; j <= 59; j++) { + if ((i < 0 && j <= 0) || (i > 0 && j >= 0) || i == 0) { + String str = (i < 0 || j < 0 ? "-" : "+") + + Integer.toString(Math.abs(i) + 100).substring(1) + ":" + + Integer.toString(Math.abs(j) + 100).substring(1); + ZoneOffset test = ZoneOffset.of(str); + doTestOffset(test, i, j, 0); + } + } + } + ZoneOffset test1 = ZoneOffset.of("-18:00"); + doTestOffset(test1, -18, 0, 0); + ZoneOffset test2 = ZoneOffset.of("+18:00"); + doTestOffset(test2, 18, 0, 0); + } + + @Test(groups={"tck"}) + public void test_factory_string_hours_minutes_seconds_noColon() { + for (int i = -17; i <= 17; i++) { + for (int j = -59; j <= 59; j++) { + for (int k = -59; k <= 59; k++) { + if ((i < 0 && j <= 0 && k <= 0) || (i > 0 && j >= 0 && k >= 0) || + (i == 0 && ((j < 0 && k <= 0) || (j > 0 && k >= 0) || j == 0))) { + String str = (i < 0 || j < 0 || k < 0 ? "-" : "+") + + Integer.toString(Math.abs(i) + 100).substring(1) + + Integer.toString(Math.abs(j) + 100).substring(1) + + Integer.toString(Math.abs(k) + 100).substring(1); + ZoneOffset test = ZoneOffset.of(str); + doTestOffset(test, i, j, k); + } + } + } + } + ZoneOffset test1 = ZoneOffset.of("-180000"); + doTestOffset(test1, -18, 0, 0); + ZoneOffset test2 = ZoneOffset.of("+180000"); + doTestOffset(test2, 18, 0, 0); + } + + @Test(groups={"tck"}) + public void test_factory_string_hours_minutes_seconds_colon() { + for (int i = -17; i <= 17; i++) { + for (int j = -59; j <= 59; j++) { + for (int k = -59; k <= 59; k++) { + if ((i < 0 && j <= 0 && k <= 0) || (i > 0 && j >= 0 && k >= 0) || + (i == 0 && ((j < 0 && k <= 0) || (j > 0 && k >= 0) || j == 0))) { + String str = (i < 0 || j < 0 || k < 0 ? "-" : "+") + + Integer.toString(Math.abs(i) + 100).substring(1) + ":" + + Integer.toString(Math.abs(j) + 100).substring(1) + ":" + + Integer.toString(Math.abs(k) + 100).substring(1); + ZoneOffset test = ZoneOffset.of(str); + doTestOffset(test, i, j, k); + } + } + } + } + ZoneOffset test1 = ZoneOffset.of("-18:00:00"); + doTestOffset(test1, -18, 0, 0); + ZoneOffset test2 = ZoneOffset.of("+18:00:00"); + doTestOffset(test2, 18, 0, 0); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_factory_int_hours() { + for (int i = -18; i <= 18; i++) { + ZoneOffset test = ZoneOffset.ofHours(i); + doTestOffset(test, i, 0, 0); + } + } + + @Test(groups={"tck"}, expectedExceptions=DateTimeException.class) + public void test_factory_int_hours_tooBig() { + ZoneOffset.ofHours(19); + } + + @Test(groups={"tck"}, expectedExceptions=DateTimeException.class) + public void test_factory_int_hours_tooSmall() { + ZoneOffset.ofHours(-19); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_factory_int_hours_minutes() { + for (int i = -17; i <= 17; i++) { + for (int j = -59; j <= 59; j++) { + if ((i < 0 && j <= 0) || (i > 0 && j >= 0) || i == 0) { + ZoneOffset test = ZoneOffset.ofHoursMinutes(i, j); + doTestOffset(test, i, j, 0); + } + } + } + ZoneOffset test1 = ZoneOffset.ofHoursMinutes(-18, 0); + doTestOffset(test1, -18, 0, 0); + ZoneOffset test2 = ZoneOffset.ofHoursMinutes(18, 0); + doTestOffset(test2, 18, 0, 0); + } + + @Test(groups={"tck"}, expectedExceptions=DateTimeException.class) + public void test_factory_int_hours_minutes_tooBig() { + ZoneOffset.ofHoursMinutes(19, 0); + } + + @Test(groups={"tck"}, expectedExceptions=DateTimeException.class) + public void test_factory_int_hours_minutes_tooSmall() { + ZoneOffset.ofHoursMinutes(-19, 0); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_factory_int_hours_minutes_seconds() { + for (int i = -17; i <= 17; i++) { + for (int j = -59; j <= 59; j++) { + for (int k = -59; k <= 59; k++) { + if ((i < 0 && j <= 0 && k <= 0) || (i > 0 && j >= 0 && k >= 0) || + (i == 0 && ((j < 0 && k <= 0) || (j > 0 && k >= 0) || j == 0))) { + ZoneOffset test = ZoneOffset.ofHoursMinutesSeconds(i, j, k); + doTestOffset(test, i, j, k); + } + } + } + } + ZoneOffset test1 = ZoneOffset.ofHoursMinutesSeconds(-18, 0, 0); + doTestOffset(test1, -18, 0, 0); + ZoneOffset test2 = ZoneOffset.ofHoursMinutesSeconds(18, 0, 0); + doTestOffset(test2, 18, 0, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_int_hours_minutes_seconds_plusHoursMinusMinutes() { + ZoneOffset.ofHoursMinutesSeconds(1, -1, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_int_hours_minutes_seconds_plusHoursMinusSeconds() { + ZoneOffset.ofHoursMinutesSeconds(1, 0, -1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_int_hours_minutes_seconds_minusHoursPlusMinutes() { + ZoneOffset.ofHoursMinutesSeconds(-1, 1, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_int_hours_minutes_seconds_minusHoursPlusSeconds() { + ZoneOffset.ofHoursMinutesSeconds(-1, 0, 1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_int_hours_minutes_seconds_zeroHoursMinusMinutesPlusSeconds() { + ZoneOffset.ofHoursMinutesSeconds(0, -1, 1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_int_hours_minutes_seconds_zeroHoursPlusMinutesMinusSeconds() { + ZoneOffset.ofHoursMinutesSeconds(0, 1, -1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_int_hours_minutes_seconds_minutesTooLarge() { + ZoneOffset.ofHoursMinutesSeconds(0, 60, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_int_hours_minutes_seconds_minutesTooSmall() { + ZoneOffset.ofHoursMinutesSeconds(0, -60, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_int_hours_minutes_seconds_secondsTooLarge() { + ZoneOffset.ofHoursMinutesSeconds(0, 0, 60); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_int_hours_minutes_seconds_secondsTooSmall() { + ZoneOffset.ofHoursMinutesSeconds(0, 0, 60); + } + + @Test(groups={"tck"}, expectedExceptions=DateTimeException.class) + public void test_factory_int_hours_minutes_seconds_hoursTooBig() { + ZoneOffset.ofHoursMinutesSeconds(19, 0, 0); + } + + @Test(groups={"tck"}, expectedExceptions=DateTimeException.class) + public void test_factory_int_hours_minutes_seconds_hoursTooSmall() { + ZoneOffset.ofHoursMinutesSeconds(-19, 0, 0); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_factory_ofTotalSeconds() { + assertEquals(ZoneOffset.ofTotalSeconds(60 * 60 + 1), ZoneOffset.ofHoursMinutesSeconds(1, 0, 1)); + assertEquals(ZoneOffset.ofTotalSeconds(18 * 60 * 60), ZoneOffset.ofHours(18)); + assertEquals(ZoneOffset.ofTotalSeconds(-18 * 60 * 60), ZoneOffset.ofHours(-18)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_ofTotalSeconds_tooLarge() { + ZoneOffset.ofTotalSeconds(18 * 60 * 60 + 1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_ofTotalSeconds_tooSmall() { + ZoneOffset.ofTotalSeconds(-18 * 60 * 60 - 1); + } + + //----------------------------------------------------------------------- + // from() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_factory_CalendricalObject() { + assertEquals(ZoneOffset.from(OffsetDate.of(LocalDate.of(2012, 5, 2), ZoneOffset.ofHours(6))), ZoneOffset.ofHours(6)); + assertEquals(ZoneOffset.from(ZonedDateTime.of(LocalDateTime.of(LocalDate.of(2007, 7, 15), + LocalTime.of(17, 30)), ZoneOffset.ofHours(2))), ZoneOffset.ofHours(2)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_CalendricalObject_invalid_noDerive() { + ZoneOffset.from(LocalTime.of(12, 30)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_factory_CalendricalObject_null() { + ZoneOffset.from((TemporalAccessor) null); + } + + //----------------------------------------------------------------------- + // getTotalSeconds() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_getTotalSeconds() { + ZoneOffset offset = ZoneOffset.ofTotalSeconds(60 * 60 + 1); + assertEquals(offset.getTotalSeconds(), 60 * 60 + 1); + } + + //----------------------------------------------------------------------- + // getId() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_getId() { + ZoneOffset offset = ZoneOffset.ofHoursMinutesSeconds(1, 0, 0); + assertEquals(offset.getId(), "+01:00"); + offset = ZoneOffset.ofHoursMinutesSeconds(1, 2, 3); + assertEquals(offset.getId(), "+01:02:03"); + offset = ZoneOffset.UTC; + assertEquals(offset.getId(), "Z"); + } + + //----------------------------------------------------------------------- + // getRules() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_getRules() { + ZoneOffset offset = ZoneOffset.ofHoursMinutesSeconds(1, 2, 3); + assertEquals(offset.getRules().isFixedOffset(), true); + assertEquals(offset.getRules().getOffset((Instant) null), offset); + assertEquals(offset.getRules().getDaylightSavings((Instant) null), Duration.ZERO); + assertEquals(offset.getRules().getStandardOffset((Instant) null), offset); + assertEquals(offset.getRules().nextTransition((Instant) null), null); + assertEquals(offset.getRules().previousTransition((Instant) null), null); + + assertEquals(offset.getRules().isValidOffset((LocalDateTime) null, offset), true); + assertEquals(offset.getRules().isValidOffset((LocalDateTime) null, ZoneOffset.UTC), false); + assertEquals(offset.getRules().isValidOffset((LocalDateTime) null, null), false); + assertEquals(offset.getRules().getOffset((LocalDateTime) null), offset); + assertEquals(offset.getRules().getValidOffsets((LocalDateTime) null), Arrays.asList(offset)); + assertEquals(offset.getRules().getTransition((LocalDateTime) null), null); + assertEquals(offset.getRules().getTransitions().size(), 0); + assertEquals(offset.getRules().getTransitionRules().size(), 0); + } + + //----------------------------------------------------------------------- + // get(TemporalField) + //----------------------------------------------------------------------- + @Test + public void test_get_TemporalField() { + assertEquals(ZoneOffset.UTC.get(OFFSET_SECONDS), 0); + assertEquals(ZoneOffset.ofHours(-2).get(OFFSET_SECONDS), -7200); + assertEquals(ZoneOffset.ofHoursMinutesSeconds(0, 1, 5).get(OFFSET_SECONDS), 65); + } + + @Test + public void test_getLong_TemporalField() { + assertEquals(ZoneOffset.UTC.getLong(OFFSET_SECONDS), 0); + assertEquals(ZoneOffset.ofHours(-2).getLong(OFFSET_SECONDS), -7200); + assertEquals(ZoneOffset.ofHoursMinutesSeconds(0, 1, 5).getLong(OFFSET_SECONDS), 65); + } + + //----------------------------------------------------------------------- + // query(TemporalQuery) + //----------------------------------------------------------------------- + @Test + public void test_query_chrono() { + ZoneOffset test = ZoneOffset.ofHoursMinutes(1, 30); + assertEquals(test.query(Queries.chrono()), null); + assertEquals(Queries.chrono().queryFrom(test), null); + } + + @Test + public void test_query_zoneId() { + ZoneOffset test = ZoneOffset.ofHoursMinutes(1, 30); + assertEquals(test.query(Queries.zoneId()), null); + assertEquals(Queries.zoneId().queryFrom(test), null); + } + + @Test + public void test_query_precision() { + ZoneOffset test = ZoneOffset.ofHoursMinutes(1, 30); + assertEquals(test.query(Queries.precision()), null); + assertEquals(Queries.precision().queryFrom(test), null); + } + + @Test + public void test_query_offset() { + ZoneOffset test = ZoneOffset.ofHoursMinutes(1, 30); + assertEquals(test.query(Queries.offset()), test); + assertEquals(Queries.offset().queryFrom(test), test); + } + + @Test + public void test_query_zone() { + ZoneOffset test = ZoneOffset.ofHoursMinutes(1, 30); + assertEquals(test.query(Queries.zone()), test); + assertEquals(Queries.zone().queryFrom(test), test); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_query_null() { + ZoneOffset.ofHoursMinutes(1, 30).query(null); + } + + //----------------------------------------------------------------------- + // compareTo() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_compareTo() { + ZoneOffset offset1 = ZoneOffset.ofHoursMinutesSeconds(1, 2, 3); + ZoneOffset offset2 = ZoneOffset.ofHoursMinutesSeconds(2, 3, 4); + assertTrue(offset1.compareTo(offset2) > 0); + assertTrue(offset2.compareTo(offset1) < 0); + assertTrue(offset1.compareTo(offset1) == 0); + assertTrue(offset2.compareTo(offset2) == 0); + } + + //----------------------------------------------------------------------- + // equals() / hashCode() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_equals() { + ZoneOffset offset1 = ZoneOffset.ofHoursMinutesSeconds(1, 2, 3); + ZoneOffset offset2 = ZoneOffset.ofHoursMinutesSeconds(2, 3, 4); + ZoneOffset offset2b = ZoneOffset.ofHoursMinutesSeconds(2, 3, 4); + assertEquals(offset1.equals(offset2), false); + assertEquals(offset2.equals(offset1), false); + + assertEquals(offset1.equals(offset1), true); + assertEquals(offset2.equals(offset2), true); + assertEquals(offset2.equals(offset2b), true); + + assertEquals(offset1.hashCode() == offset1.hashCode(), true); + assertEquals(offset2.hashCode() == offset2.hashCode(), true); + assertEquals(offset2.hashCode() == offset2b.hashCode(), true); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toString() { + ZoneOffset offset = ZoneOffset.ofHoursMinutesSeconds(1, 0, 0); + assertEquals(offset.toString(), "+01:00"); + offset = ZoneOffset.ofHoursMinutesSeconds(1, 2, 3); + assertEquals(offset.toString(), "+01:02:03"); + offset = ZoneOffset.UTC; + assertEquals(offset.toString(), "Z"); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + private void doTestOffset(ZoneOffset offset, int hours, int minutes, int seconds) { + assertEquals(offset.getTotalSeconds(), hours * 60 * 60 + minutes * 60 + seconds); + final String id; + if (hours == 0 && minutes == 0 && seconds == 0) { + id = "Z"; + } else { + String str = (hours < 0 || minutes < 0 || seconds < 0) ? "-" : "+"; + str += Integer.toString(Math.abs(hours) + 100).substring(1); + str += ":"; + str += Integer.toString(Math.abs(minutes) + 100).substring(1); + if (seconds != 0) { + str += ":"; + str += Integer.toString(Math.abs(seconds) + 100).substring(1); + } + id = str; + } + assertEquals(offset.getId(), id); + assertEquals(offset, ZoneOffset.ofHoursMinutesSeconds(hours, minutes, seconds)); + if (seconds == 0) { + assertEquals(offset, ZoneOffset.ofHoursMinutes(hours, minutes)); + if (minutes == 0) { + assertEquals(offset, ZoneOffset.ofHours(hours)); + } + } + assertEquals(ZoneOffset.of(id), offset); + assertEquals(offset.toString(), id); + } + +} diff --git a/jdk/test/java/time/tck/java/time/TCKZonedDateTime.java b/jdk/test/java/time/tck/java/time/TCKZonedDateTime.java new file mode 100644 index 00000000000..df18b4f246f --- /dev/null +++ b/jdk/test/java/time/tck/java/time/TCKZonedDateTime.java @@ -0,0 +1,2166 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time; + +import java.time.*; +import test.java.time.MockSimplePeriod; + +import static java.time.Month.JANUARY; +import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; +import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; +import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; +import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; +import static java.time.temporal.ChronoField.AMPM_OF_DAY; +import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_AMPM; +import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_DAY; +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.DAY_OF_WEEK; +import static java.time.temporal.ChronoField.DAY_OF_YEAR; +import static java.time.temporal.ChronoField.EPOCH_DAY; +import static java.time.temporal.ChronoField.EPOCH_MONTH; +import static java.time.temporal.ChronoField.ERA; +import static java.time.temporal.ChronoField.HOUR_OF_AMPM; +import static java.time.temporal.ChronoField.HOUR_OF_DAY; +import static java.time.temporal.ChronoField.INSTANT_SECONDS; +import static java.time.temporal.ChronoField.MICRO_OF_DAY; +import static java.time.temporal.ChronoField.MICRO_OF_SECOND; +import static java.time.temporal.ChronoField.MILLI_OF_DAY; +import static java.time.temporal.ChronoField.MILLI_OF_SECOND; +import static java.time.temporal.ChronoField.MINUTE_OF_DAY; +import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.NANO_OF_DAY; +import static java.time.temporal.ChronoField.NANO_OF_SECOND; +import static java.time.temporal.ChronoField.OFFSET_SECONDS; +import static java.time.temporal.ChronoField.SECOND_OF_DAY; +import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; +import static java.time.temporal.ChronoField.YEAR; +import static java.time.temporal.ChronoField.YEAR_OF_ERA; +import static java.time.temporal.ChronoUnit.DAYS; +import static java.time.temporal.ChronoUnit.HOURS; +import static java.time.temporal.ChronoUnit.MINUTES; +import static java.time.temporal.ChronoUnit.NANOS; +import static java.time.temporal.ChronoUnit.SECONDS; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoUnit; +import java.time.temporal.Queries; +import java.time.temporal.TemporalSubtractor; +import java.time.temporal.TemporalAdder; +import java.time.temporal.TemporalAdjuster; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalQuery; +import java.time.temporal.TemporalField; +import java.time.temporal.ISOChrono; +import java.time.temporal.JulianFields; +import test.java.time.temporal.MockFieldNoValue; + +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatters; +import java.time.format.DateTimeParseException; +import java.time.temporal.OffsetDateTime; +import java.time.temporal.Year; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test ZonedDateTime. + */ +@Test +public class TCKZonedDateTime extends AbstractDateTimeTest { + + private static final ZoneOffset OFFSET_0100 = ZoneOffset.ofHours(1); + private static final ZoneOffset OFFSET_0200 = ZoneOffset.ofHours(2); + private static final ZoneOffset OFFSET_0130 = ZoneOffset.of("+01:30"); + private static final ZoneOffset OFFSET_MAX = ZoneOffset.MAX; + private static final ZoneOffset OFFSET_MIN = ZoneOffset.MIN; + + private static final ZoneId ZONE_0100 = OFFSET_0100; + private static final ZoneId ZONE_0200 = OFFSET_0200; + private static final ZoneId ZONE_M0100 = ZoneOffset.ofHours(-1); + private static final ZoneId ZONE_LONDON = ZoneId.of("Europe/London"); + private static final ZoneId ZONE_PARIS = ZoneId.of("Europe/Paris"); + private LocalDateTime TEST_PARIS_GAP_2008_03_30_02_30; + private LocalDateTime TEST_PARIS_OVERLAP_2008_10_26_02_30; + private LocalDateTime TEST_LOCAL_2008_06_30_11_30_59_500; + private ZonedDateTime TEST_DATE_TIME; + private ZonedDateTime TEST_DATE_TIME_PARIS; + + @BeforeMethod(groups={"tck","implementation"}) + public void setUp() { + TEST_LOCAL_2008_06_30_11_30_59_500 = LocalDateTime.of(2008, 6, 30, 11, 30, 59, 500); + TEST_DATE_TIME = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_0100); + TEST_DATE_TIME_PARIS = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_PARIS); + TEST_PARIS_OVERLAP_2008_10_26_02_30 = LocalDateTime.of(2008, 10, 26, 2, 30); + TEST_PARIS_GAP_2008_03_30_02_30 = LocalDateTime.of(2008, 3, 30, 2, 30); + } + + //----------------------------------------------------------------------- + @Override + protected List samples() { + TemporalAccessor[] array = {TEST_DATE_TIME, }; + return Arrays.asList(array); + } + + @Override + protected List validFields() { + TemporalField[] array = { + NANO_OF_SECOND, + NANO_OF_DAY, + MICRO_OF_SECOND, + MICRO_OF_DAY, + MILLI_OF_SECOND, + MILLI_OF_DAY, + SECOND_OF_MINUTE, + SECOND_OF_DAY, + MINUTE_OF_HOUR, + MINUTE_OF_DAY, + CLOCK_HOUR_OF_AMPM, + HOUR_OF_AMPM, + CLOCK_HOUR_OF_DAY, + HOUR_OF_DAY, + AMPM_OF_DAY, + DAY_OF_WEEK, + ALIGNED_DAY_OF_WEEK_IN_MONTH, + ALIGNED_DAY_OF_WEEK_IN_YEAR, + DAY_OF_MONTH, + DAY_OF_YEAR, + EPOCH_DAY, + ALIGNED_WEEK_OF_MONTH, + ALIGNED_WEEK_OF_YEAR, + MONTH_OF_YEAR, + EPOCH_MONTH, + YEAR_OF_ERA, + YEAR, + ERA, + OFFSET_SECONDS, + INSTANT_SECONDS, + JulianFields.JULIAN_DAY, + JulianFields.MODIFIED_JULIAN_DAY, + JulianFields.RATA_DIE, + }; + return Arrays.asList(array); + } + + @Override + protected List invalidFields() { + List list = new ArrayList<>(Arrays.asList(ChronoField.values())); + list.removeAll(validFields()); + return list; + } + + //----------------------------------------------------------------------- + @Test + public void test_serialization() throws ClassNotFoundException, IOException { + assertSerializable(TEST_DATE_TIME); + } + + @Test + public void test_serialization_format_zoneId() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baos) ) { + dos.writeByte(6); + dos.writeInt(2012); // date + dos.writeByte(9); + dos.writeByte(16); + dos.writeByte(22); // time + dos.writeByte(17); + dos.writeByte(59); + dos.writeInt(470_000_000); + dos.writeByte(4); // offset + dos.writeByte(7); // zoneId + dos.writeUTF("Europe/London"); + } + byte[] bytes = baos.toByteArray(); + ZonedDateTime zdt = LocalDateTime.of(2012, 9, 16, 22, 17, 59, 470_000_000).atZone(ZoneId.of("Europe/London")); + assertSerializedBySer(zdt, bytes); + } + + @Test + public void test_serialization_format_zoneOffset() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baos) ) { + dos.writeByte(6); + dos.writeInt(2012); // date + dos.writeByte(9); + dos.writeByte(16); + dos.writeByte(22); // time + dos.writeByte(17); + dos.writeByte(59); + dos.writeInt(470_000_000); + dos.writeByte(4); // offset + dos.writeByte(8); // zoneId + dos.writeByte(4); + } + byte[] bytes = baos.toByteArray(); + ZonedDateTime zdt = LocalDateTime.of(2012, 9, 16, 22, 17, 59, 470_000_000).atZone(ZoneOffset.ofHours(1)); + assertSerializedBySer(zdt, bytes); + } + + //----------------------------------------------------------------------- + // now() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void now() { + ZonedDateTime expected = ZonedDateTime.now(Clock.systemDefaultZone()); + ZonedDateTime test = ZonedDateTime.now(); + long diff = Math.abs(test.getTime().toNanoOfDay() - expected.getTime().toNanoOfDay()); + if (diff >= 100000000) { + // may be date change + expected = ZonedDateTime.now(Clock.systemDefaultZone()); + test = ZonedDateTime.now(); + diff = Math.abs(test.getTime().toNanoOfDay() - expected.getTime().toNanoOfDay()); + } + assertTrue(diff < 100000000); // less than 0.1 secs + } + + //----------------------------------------------------------------------- + // now(ZoneId) + //----------------------------------------------------------------------- + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void now_ZoneId_nullZoneId() { + ZonedDateTime.now((ZoneId) null); + } + + @Test(groups={"tck"}) + public void now_ZoneId() { + ZoneId zone = ZoneId.of("UTC+01:02:03"); + ZonedDateTime expected = ZonedDateTime.now(Clock.system(zone)); + ZonedDateTime test = ZonedDateTime.now(zone); + for (int i = 0; i < 100; i++) { + if (expected.equals(test)) { + return; + } + expected = ZonedDateTime.now(Clock.system(zone)); + test = ZonedDateTime.now(zone); + } + assertEquals(test, expected); + } + + //----------------------------------------------------------------------- + // now(Clock) + //----------------------------------------------------------------------- + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void now_Clock_nullClock() { + ZonedDateTime.now((Clock)null); + } + + @Test(groups={"tck"}) + public void now_Clock_allSecsInDay_utc() { + for (int i = 0; i < (2 * 24 * 60 * 60); i++) { + Instant instant = Instant.ofEpochSecond(i).plusNanos(123456789L); + Clock clock = Clock.fixed(instant, ZoneOffset.UTC); + ZonedDateTime test = ZonedDateTime.now(clock); + assertEquals(test.getYear(), 1970); + assertEquals(test.getMonth(), Month.JANUARY); + assertEquals(test.getDayOfMonth(), (i < 24 * 60 * 60 ? 1 : 2)); + assertEquals(test.getHour(), (i / (60 * 60)) % 24); + assertEquals(test.getMinute(), (i / 60) % 60); + assertEquals(test.getSecond(), i % 60); + assertEquals(test.getNano(), 123456789); + assertEquals(test.getOffset(), ZoneOffset.UTC); + assertEquals(test.getZone(), ZoneOffset.UTC); + } + } + + @Test(groups={"tck"}) + public void now_Clock_allSecsInDay_zone() { + ZoneId zone = ZoneId.of("Europe/London"); + for (int i = 0; i < (2 * 24 * 60 * 60); i++) { + Instant instant = Instant.ofEpochSecond(i).plusNanos(123456789L); + ZonedDateTime expected = ZonedDateTime.ofInstant(instant, zone); + Clock clock = Clock.fixed(expected.toInstant(), zone); + ZonedDateTime test = ZonedDateTime.now(clock); + assertEquals(test, expected); + } + } + + @Test(groups={"tck"}) + public void now_Clock_allSecsInDay_beforeEpoch() { + LocalTime expected = LocalTime.MIDNIGHT.plusNanos(123456789L); + for (int i =-1; i >= -(24 * 60 * 60); i--) { + Instant instant = Instant.ofEpochSecond(i).plusNanos(123456789L); + Clock clock = Clock.fixed(instant, ZoneOffset.UTC); + ZonedDateTime test = ZonedDateTime.now(clock); + assertEquals(test.getYear(), 1969); + assertEquals(test.getMonth(), Month.DECEMBER); + assertEquals(test.getDayOfMonth(), 31); + expected = expected.minusSeconds(1); + assertEquals(test.getTime(), expected); + assertEquals(test.getOffset(), ZoneOffset.UTC); + assertEquals(test.getZone(), ZoneOffset.UTC); + } + } + + @Test(groups={"tck"}) + public void now_Clock_offsets() { + ZonedDateTime base = ZonedDateTime.of(LocalDateTime.of(1970, 1, 1, 12, 0), ZoneOffset.UTC); + for (int i = -9; i < 15; i++) { + ZoneOffset offset = ZoneOffset.ofHours(i); + Clock clock = Clock.fixed(base.toInstant(), offset); + ZonedDateTime test = ZonedDateTime.now(clock); + assertEquals(test.getHour(), (12 + i) % 24); + assertEquals(test.getMinute(), 0); + assertEquals(test.getSecond(), 0); + assertEquals(test.getNano(), 0); + assertEquals(test.getOffset(), offset); + assertEquals(test.getZone(), offset); + } + } + + //----------------------------------------------------------------------- + // dateTime factories + //----------------------------------------------------------------------- + void check(ZonedDateTime test, int y, int m, int d, int h, int min, int s, int n, ZoneOffset offset, ZoneId zone) { + assertEquals(test.getYear(), y); + assertEquals(test.getMonth().getValue(), m); + assertEquals(test.getDayOfMonth(), d); + assertEquals(test.getHour(), h); + assertEquals(test.getMinute(), min); + assertEquals(test.getSecond(), s); + assertEquals(test.getNano(), n); + assertEquals(test.getOffset(), offset); + assertEquals(test.getZone(), zone); + } + + //----------------------------------------------------------------------- + // of(LocalDateTime, ZoneId) + //----------------------------------------------------------------------- + // TODO: tests of overlap/gap + + @Test(groups={"tck"}) + public void factory_of_LocalDateTime() { + LocalDateTime base = LocalDateTime.of(2008, 6, 30, 11, 30, 10, 500); + ZonedDateTime test = ZonedDateTime.of(base, ZONE_PARIS); + check(test, 2008, 6, 30, 11, 30, 10, 500, OFFSET_0200, ZONE_PARIS); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_of_LocalDateTime_nullDateTime() { + ZonedDateTime.of((LocalDateTime) null, ZONE_PARIS); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_of_LocalDateTime_nullZone() { + LocalDateTime base = LocalDateTime.of(2008, 6, 30, 11, 30, 10, 500); + ZonedDateTime.of(base, null); + } + + //----------------------------------------------------------------------- + // ofInstant(Instant, ZoneId) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_ofInstant_Instant_ZR() { + Instant instant = LocalDateTime.of(2008, 6, 30, 11, 30, 10, 35).toInstant(OFFSET_0200); + ZonedDateTime test = ZonedDateTime.ofInstant(instant, ZONE_PARIS); + check(test, 2008, 6, 30, 11, 30, 10, 35, OFFSET_0200, ZONE_PARIS); + } + + @Test(groups={"tck"}) + public void factory_ofInstant_Instant_ZO() { + Instant instant = LocalDateTime.of(2008, 6, 30, 11, 30, 10, 45).toInstant(OFFSET_0200); + ZonedDateTime test = ZonedDateTime.ofInstant(instant, OFFSET_0200); + check(test, 2008, 6, 30, 11, 30, 10, 45, OFFSET_0200, OFFSET_0200); + } + + @Test(groups={"tck"}) + public void factory_ofInstant_Instant_inGap() { + Instant instant = TEST_PARIS_GAP_2008_03_30_02_30.toInstant(OFFSET_0100); + ZonedDateTime test = ZonedDateTime.ofInstant(instant, ZONE_PARIS); + check(test, 2008, 3, 30, 3, 30, 0, 0, OFFSET_0200, ZONE_PARIS); // one hour later in summer offset + } + + @Test(groups={"tck"}) + public void factory_ofInstant_Instant_inOverlap_earlier() { + Instant instant = TEST_PARIS_OVERLAP_2008_10_26_02_30.toInstant(OFFSET_0200); + ZonedDateTime test = ZonedDateTime.ofInstant(instant, ZONE_PARIS); + check(test, 2008, 10, 26, 2, 30, 0, 0, OFFSET_0200, ZONE_PARIS); // same time and offset + } + + @Test(groups={"tck"}) + public void factory_ofInstant_Instant_inOverlap_later() { + Instant instant = TEST_PARIS_OVERLAP_2008_10_26_02_30.toInstant(OFFSET_0100); + ZonedDateTime test = ZonedDateTime.ofInstant(instant, ZONE_PARIS); + check(test, 2008, 10, 26, 2, 30, 0, 0, OFFSET_0100, ZONE_PARIS); // same time and offset + } + + @Test(groups={"tck"}) + public void factory_ofInstant_Instant_invalidOffset() { + Instant instant = LocalDateTime.of(2008, 6, 30, 11, 30, 10, 500).toInstant(OFFSET_0130); + ZonedDateTime test = ZonedDateTime.ofInstant(instant, ZONE_PARIS); + check(test, 2008, 6, 30, 12, 0, 10, 500, OFFSET_0200, ZONE_PARIS); // corrected offset, thus altered time + } + + @Test(groups={"tck"}) + public void factory_ofInstant_allSecsInDay() { + for (int i = 0; i < (24 * 60 * 60); i++) { + Instant instant = Instant.ofEpochSecond(i); + ZonedDateTime test = ZonedDateTime.ofInstant(instant, OFFSET_0100); + assertEquals(test.getYear(), 1970); + assertEquals(test.getMonth(), Month.JANUARY); + assertEquals(test.getDayOfMonth(), 1 + (i >= 23 * 60 * 60 ? 1 : 0)); + assertEquals(test.getHour(), ((i / (60 * 60)) + 1) % 24); + assertEquals(test.getMinute(), (i / 60) % 60); + assertEquals(test.getSecond(), i % 60); + } + } + + @Test(groups={"tck"}) + public void factory_ofInstant_allDaysInCycle() { + // sanity check using different algorithm + ZonedDateTime expected = LocalDateTime.of(1970, 1, 1, 0, 0, 0, 0).atZone(ZoneOffset.UTC); + for (long i = 0; i < 146097; i++) { + Instant instant = Instant.ofEpochSecond(i * 24L * 60L * 60L); + ZonedDateTime test = ZonedDateTime.ofInstant(instant, ZoneOffset.UTC); + assertEquals(test, expected); + expected = expected.plusDays(1); + } + } + + @Test(groups={"tck"}) + public void factory_ofInstant_minWithMinOffset() { + long days_0000_to_1970 = (146097 * 5) - (30 * 365 + 7); + int year = Year.MIN_VALUE; + long days = (year * 365L + (year / 4 - year / 100 + year / 400)) - days_0000_to_1970; + Instant instant = Instant.ofEpochSecond(days * 24L * 60L * 60L - OFFSET_MIN.getTotalSeconds()); + ZonedDateTime test = ZonedDateTime.ofInstant(instant, OFFSET_MIN); + assertEquals(test.getYear(), Year.MIN_VALUE); + assertEquals(test.getMonth().getValue(), 1); + assertEquals(test.getDayOfMonth(), 1); + assertEquals(test.getOffset(), OFFSET_MIN); + assertEquals(test.getHour(), 0); + assertEquals(test.getMinute(), 0); + assertEquals(test.getSecond(), 0); + assertEquals(test.getNano(), 0); + } + + @Test(groups={"tck"}) + public void factory_ofInstant_minWithMaxOffset() { + long days_0000_to_1970 = (146097 * 5) - (30 * 365 + 7); + int year = Year.MIN_VALUE; + long days = (year * 365L + (year / 4 - year / 100 + year / 400)) - days_0000_to_1970; + Instant instant = Instant.ofEpochSecond(days * 24L * 60L * 60L - OFFSET_MAX.getTotalSeconds()); + ZonedDateTime test = ZonedDateTime.ofInstant(instant, OFFSET_MAX); + assertEquals(test.getYear(), Year.MIN_VALUE); + assertEquals(test.getMonth().getValue(), 1); + assertEquals(test.getDayOfMonth(), 1); + assertEquals(test.getOffset(), OFFSET_MAX); + assertEquals(test.getHour(), 0); + assertEquals(test.getMinute(), 0); + assertEquals(test.getSecond(), 0); + assertEquals(test.getNano(), 0); + } + + @Test(groups={"tck"}) + public void factory_ofInstant_maxWithMinOffset() { + long days_0000_to_1970 = (146097 * 5) - (30 * 365 + 7); + int year = Year.MAX_VALUE; + long days = (year * 365L + (year / 4 - year / 100 + year / 400)) + 365 - days_0000_to_1970; + Instant instant = Instant.ofEpochSecond((days + 1) * 24L * 60L * 60L - 1 - OFFSET_MIN.getTotalSeconds()); + ZonedDateTime test = ZonedDateTime.ofInstant(instant, OFFSET_MIN); + assertEquals(test.getYear(), Year.MAX_VALUE); + assertEquals(test.getMonth().getValue(), 12); + assertEquals(test.getDayOfMonth(), 31); + assertEquals(test.getOffset(), OFFSET_MIN); + assertEquals(test.getHour(), 23); + assertEquals(test.getMinute(), 59); + assertEquals(test.getSecond(), 59); + assertEquals(test.getNano(), 0); + } + + @Test(groups={"tck"}) + public void factory_ofInstant_maxWithMaxOffset() { + long days_0000_to_1970 = (146097 * 5) - (30 * 365 + 7); + int year = Year.MAX_VALUE; + long days = (year * 365L + (year / 4 - year / 100 + year / 400)) + 365 - days_0000_to_1970; + Instant instant = Instant.ofEpochSecond((days + 1) * 24L * 60L * 60L - 1 - OFFSET_MAX.getTotalSeconds()); + ZonedDateTime test = ZonedDateTime.ofInstant(instant, OFFSET_MAX); + assertEquals(test.getYear(), Year.MAX_VALUE); + assertEquals(test.getMonth().getValue(), 12); + assertEquals(test.getDayOfMonth(), 31); + assertEquals(test.getOffset(), OFFSET_MAX); + assertEquals(test.getHour(), 23); + assertEquals(test.getMinute(), 59); + assertEquals(test.getSecond(), 59); + assertEquals(test.getNano(), 0); + } + + //----------------------------------------------------------------------- + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofInstant_maxInstantWithMaxOffset() { + Instant instant = Instant.ofEpochSecond(Long.MAX_VALUE); + ZonedDateTime.ofInstant(instant, OFFSET_MAX); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofInstant_maxInstantWithMinOffset() { + Instant instant = Instant.ofEpochSecond(Long.MAX_VALUE); + ZonedDateTime.ofInstant(instant, OFFSET_MIN); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofInstant_tooBig() { + long days_0000_to_1970 = (146097 * 5) - (30 * 365 + 7); + long year = Year.MAX_VALUE + 1L; + long days = (year * 365L + (year / 4 - year / 100 + year / 400)) - days_0000_to_1970; + Instant instant = Instant.ofEpochSecond(days * 24L * 60L * 60L); + ZonedDateTime.ofInstant(instant, ZoneOffset.UTC); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofInstant_tooLow() { + long days_0000_to_1970 = (146097 * 5) - (30 * 365 + 7); + int year = Year.MIN_VALUE - 1; + long days = (year * 365L + (year / 4 - year / 100 + year / 400)) - days_0000_to_1970; + Instant instant = Instant.ofEpochSecond(days * 24L * 60L * 60L); + ZonedDateTime.ofInstant(instant, ZoneOffset.UTC); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_ofInstant_Instant_nullInstant() { + ZonedDateTime.ofInstant((Instant) null, ZONE_0100); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_ofInstant_Instant_nullZone() { + ZonedDateTime.ofInstant(Instant.EPOCH, null); + } + + //----------------------------------------------------------------------- + // ofStrict(LocalDateTime, ZoneId, ZoneOffset) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_ofStrict_LDT_ZI_ZO() { + LocalDateTime normal = LocalDateTime.of(2008, 6, 30, 11, 30, 10, 500); + ZonedDateTime test = ZonedDateTime.ofStrict(normal, OFFSET_0200, ZONE_PARIS); + check(test, 2008, 6, 30, 11, 30, 10, 500, OFFSET_0200, ZONE_PARIS); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofStrict_LDT_ZI_ZO_inGap() { + try { + ZonedDateTime.ofStrict(TEST_PARIS_GAP_2008_03_30_02_30, OFFSET_0100, ZONE_PARIS); + } catch (DateTimeException ex) { + assertEquals(ex.getMessage().contains(" gap"), true); + throw ex; + } + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofStrict_LDT_ZI_ZO_inOverlap_invalidOfset() { + try { + ZonedDateTime.ofStrict(TEST_PARIS_OVERLAP_2008_10_26_02_30, OFFSET_0130, ZONE_PARIS); + } catch (DateTimeException ex) { + assertEquals(ex.getMessage().contains(" is not valid for "), true); + throw ex; + } + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_ofStrict_LDT_ZI_ZO_invalidOffset() { + try { + ZonedDateTime.ofStrict(TEST_LOCAL_2008_06_30_11_30_59_500, OFFSET_0130, ZONE_PARIS); + } catch (DateTimeException ex) { + assertEquals(ex.getMessage().contains(" is not valid for "), true); + throw ex; + } + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_ofStrict_LDT_ZI_ZO_nullLDT() { + ZonedDateTime.ofStrict((LocalDateTime) null, OFFSET_0100, ZONE_PARIS); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_ofStrict_LDT_ZI_ZO_nullZO() { + ZonedDateTime.ofStrict(TEST_LOCAL_2008_06_30_11_30_59_500, null, ZONE_PARIS); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_ofStrict_LDT_ZI_ZO_nullZI() { + ZonedDateTime.ofStrict(TEST_LOCAL_2008_06_30_11_30_59_500, OFFSET_0100, null); + } + + //----------------------------------------------------------------------- + // from(TemporalAccessor) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_from_TemporalAccessor_ZDT() { + assertEquals(ZonedDateTime.from(TEST_DATE_TIME_PARIS), TEST_DATE_TIME_PARIS); + } + + @Test(groups={"tck"}) + public void factory_from_TemporalAccessor_LDT_ZoneId() { + assertEquals(ZonedDateTime.from(new TemporalAccessor() { + @Override + public boolean isSupported(TemporalField field) { + return TEST_DATE_TIME_PARIS.getDateTime().isSupported(field); + } + @Override + public long getLong(TemporalField field) { + return TEST_DATE_TIME_PARIS.getDateTime().getLong(field); + } + @SuppressWarnings("unchecked") + @Override + public R query(TemporalQuery query) { + if (query == Queries.zoneId()) { + return (R) TEST_DATE_TIME_PARIS.getZone(); + } + return TemporalAccessor.super.query(query); + } + }), TEST_DATE_TIME_PARIS); + } + + @Test(groups={"tck"}) + public void factory_from_TemporalAccessor_Instant_ZoneId() { + assertEquals(ZonedDateTime.from(new TemporalAccessor() { + @Override + public boolean isSupported(TemporalField field) { + return field == INSTANT_SECONDS || field == NANO_OF_SECOND; + } + + @Override + public long getLong(TemporalField field) { + return TEST_DATE_TIME_PARIS.toInstant().getLong(field); + } + + @SuppressWarnings("unchecked") + @Override + public R query(TemporalQuery query) { + if (query == Queries.zoneId()) { + return (R) TEST_DATE_TIME_PARIS.getZone(); + } + return TemporalAccessor.super.query(query); + } + }), TEST_DATE_TIME_PARIS); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_from_TemporalAccessor_invalid_noDerive() { + ZonedDateTime.from(LocalTime.of(12, 30)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_from_TemporalAccessor_null() { + ZonedDateTime.from((TemporalAccessor) null); + } + + //----------------------------------------------------------------------- + // parse() + //----------------------------------------------------------------------- + @Test(dataProvider="sampleToString", groups={"tck"}) + public void test_parse(int y, int month, int d, int h, int m, int s, int n, String zoneId, String text) { + ZonedDateTime t = ZonedDateTime.parse(text); + assertEquals(t.getYear(), y); + assertEquals(t.getMonth().getValue(), month); + assertEquals(t.getDayOfMonth(), d); + assertEquals(t.getHour(), h); + assertEquals(t.getMinute(), m); + assertEquals(t.getSecond(), s); + assertEquals(t.getNano(), n); + assertEquals(t.getZone().getId(), zoneId); + } + + @Test(expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_illegalValue() { + ZonedDateTime.parse("2008-06-32T11:15+01:00[Europe/Paris]"); + } + + @Test(expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_invalidValue() { + ZonedDateTime.parse("2008-06-31T11:15+01:00[Europe/Paris]"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_nullText() { + ZonedDateTime.parse((String) null); + } + + //----------------------------------------------------------------------- + // parse(DateTimeFormatter) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_parse_formatter() { + DateTimeFormatter f = DateTimeFormatters.pattern("y M d H m s I"); + ZonedDateTime test = ZonedDateTime.parse("2010 12 3 11 30 0 Europe/London", f); + assertEquals(test, ZonedDateTime.of(LocalDateTime.of(2010, 12, 3, 11, 30), ZoneId.of("Europe/London"))); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_formatter_nullText() { + DateTimeFormatter f = DateTimeFormatters.pattern("y M d H m s"); + ZonedDateTime.parse((String) null, f); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_formatter_nullFormatter() { + ZonedDateTime.parse("ANY", null); + } + + //----------------------------------------------------------------------- + // basics + //----------------------------------------------------------------------- + @DataProvider(name="sampleTimes") + Object[][] provider_sampleTimes() { + return new Object[][] { + {2008, 6, 30, 11, 30, 20, 500, ZONE_0100}, + {2008, 6, 30, 11, 0, 0, 0, ZONE_0100}, + {2008, 6, 30, 11, 30, 20, 500, ZONE_PARIS}, + {2008, 6, 30, 11, 0, 0, 0, ZONE_PARIS}, + {2008, 6, 30, 23, 59, 59, 999999999, ZONE_0100}, + {-1, 1, 1, 0, 0, 0, 0, ZONE_0100}, + }; + } + + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_get(int y, int o, int d, int h, int m, int s, int n, ZoneId zone) { + LocalDate localDate = LocalDate.of(y, o, d); + LocalTime localTime = LocalTime.of(h, m, s, n); + LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime); + ZoneOffset offset = zone.getRules().getOffset(localDateTime); + ZonedDateTime a = ZonedDateTime.of(localDateTime, zone); + + assertEquals(a.getYear(), localDate.getYear()); + assertEquals(a.getMonth(), localDate.getMonth()); + assertEquals(a.getDayOfMonth(), localDate.getDayOfMonth()); + assertEquals(a.getDayOfYear(), localDate.getDayOfYear()); + assertEquals(a.getDayOfWeek(), localDate.getDayOfWeek()); + + assertEquals(a.getHour(), localTime.getHour()); + assertEquals(a.getMinute(), localTime.getMinute()); + assertEquals(a.getSecond(), localTime.getSecond()); + assertEquals(a.getNano(), localTime.getNano()); + + assertEquals(a.getDate(), localDate); + assertEquals(a.getTime(), localTime); + assertEquals(a.getDateTime(), localDateTime); + if (zone instanceof ZoneOffset) { + assertEquals(a.toString(), localDateTime.toString() + offset.toString()); + } else { + assertEquals(a.toString(), localDateTime.toString() + offset.toString() + "[" + zone.toString() + "]"); + } + } + + //----------------------------------------------------------------------- + // get(TemporalField) + //----------------------------------------------------------------------- + @Test + public void test_get_TemporalField() { + ZonedDateTime test = ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 12, 30, 40, 987654321), ZONE_0100); + assertEquals(test.get(ChronoField.YEAR), 2008); + assertEquals(test.get(ChronoField.MONTH_OF_YEAR), 6); + assertEquals(test.get(ChronoField.DAY_OF_MONTH), 30); + assertEquals(test.get(ChronoField.DAY_OF_WEEK), 1); + assertEquals(test.get(ChronoField.DAY_OF_YEAR), 182); + + assertEquals(test.get(ChronoField.HOUR_OF_DAY), 12); + assertEquals(test.get(ChronoField.MINUTE_OF_HOUR), 30); + assertEquals(test.get(ChronoField.SECOND_OF_MINUTE), 40); + assertEquals(test.get(ChronoField.NANO_OF_SECOND), 987654321); + assertEquals(test.get(ChronoField.HOUR_OF_AMPM), 0); + assertEquals(test.get(ChronoField.AMPM_OF_DAY), 1); + + assertEquals(test.get(ChronoField.OFFSET_SECONDS), 3600); + } + + @Test + public void test_getLong_TemporalField() { + ZonedDateTime test = ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 12, 30, 40, 987654321), ZONE_0100); + assertEquals(test.getLong(ChronoField.YEAR), 2008); + assertEquals(test.getLong(ChronoField.MONTH_OF_YEAR), 6); + assertEquals(test.getLong(ChronoField.DAY_OF_MONTH), 30); + assertEquals(test.getLong(ChronoField.DAY_OF_WEEK), 1); + assertEquals(test.getLong(ChronoField.DAY_OF_YEAR), 182); + + assertEquals(test.getLong(ChronoField.HOUR_OF_DAY), 12); + assertEquals(test.getLong(ChronoField.MINUTE_OF_HOUR), 30); + assertEquals(test.getLong(ChronoField.SECOND_OF_MINUTE), 40); + assertEquals(test.getLong(ChronoField.NANO_OF_SECOND), 987654321); + assertEquals(test.getLong(ChronoField.HOUR_OF_AMPM), 0); + assertEquals(test.getLong(ChronoField.AMPM_OF_DAY), 1); + + assertEquals(test.getLong(ChronoField.OFFSET_SECONDS), 3600); + assertEquals(test.getLong(ChronoField.INSTANT_SECONDS), test.toEpochSecond()); + } + + //----------------------------------------------------------------------- + // query(TemporalQuery) + //----------------------------------------------------------------------- + @Test + public void test_query_chrono() { + assertEquals(TEST_DATE_TIME.query(Queries.chrono()), ISOChrono.INSTANCE); + assertEquals(Queries.chrono().queryFrom(TEST_DATE_TIME), ISOChrono.INSTANCE); + } + + @Test + public void test_query_zoneId() { + assertEquals(TEST_DATE_TIME.query(Queries.zoneId()), TEST_DATE_TIME.getZone()); + assertEquals(Queries.zoneId().queryFrom(TEST_DATE_TIME), TEST_DATE_TIME.getZone()); + } + + @Test + public void test_query_precision() { + assertEquals(TEST_DATE_TIME.query(Queries.precision()), NANOS); + assertEquals(Queries.precision().queryFrom(TEST_DATE_TIME), NANOS); + } + + @Test + public void test_query_offset() { + assertEquals(TEST_DATE_TIME.query(Queries.offset()), TEST_DATE_TIME.getOffset()); + assertEquals(Queries.offset().queryFrom(TEST_DATE_TIME), TEST_DATE_TIME.getOffset()); + } + + @Test + public void test_query_zone() { + assertEquals(TEST_DATE_TIME.query(Queries.zone()), TEST_DATE_TIME.getZone()); + assertEquals(Queries.zone().queryFrom(TEST_DATE_TIME), TEST_DATE_TIME.getZone()); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_query_null() { + TEST_DATE_TIME.query(null); + } + + //----------------------------------------------------------------------- + // withEarlierOffsetAtOverlap() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withEarlierOffsetAtOverlap_notAtOverlap() { + ZonedDateTime base = ZonedDateTime.ofStrict(TEST_LOCAL_2008_06_30_11_30_59_500, OFFSET_0200, ZONE_PARIS); + ZonedDateTime test = base.withEarlierOffsetAtOverlap(); + assertEquals(test, base); // not changed + } + + @Test(groups={"tck"}) + public void test_withEarlierOffsetAtOverlap_atOverlap() { + ZonedDateTime base = ZonedDateTime.ofStrict(TEST_PARIS_OVERLAP_2008_10_26_02_30, OFFSET_0100, ZONE_PARIS); + ZonedDateTime test = base.withEarlierOffsetAtOverlap(); + assertEquals(test.getOffset(), OFFSET_0200); // offset changed to earlier + assertEquals(test.getDateTime(), base.getDateTime()); // date-time not changed + } + + @Test(groups={"tck"}) + public void test_withEarlierOffsetAtOverlap_atOverlap_noChange() { + ZonedDateTime base = ZonedDateTime.ofStrict(TEST_PARIS_OVERLAP_2008_10_26_02_30, OFFSET_0200, ZONE_PARIS); + ZonedDateTime test = base.withEarlierOffsetAtOverlap(); + assertEquals(test, base); // not changed + } + + //----------------------------------------------------------------------- + // withLaterOffsetAtOverlap() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withLaterOffsetAtOverlap_notAtOverlap() { + ZonedDateTime base = ZonedDateTime.ofStrict(TEST_LOCAL_2008_06_30_11_30_59_500, OFFSET_0200, ZONE_PARIS); + ZonedDateTime test = base.withLaterOffsetAtOverlap(); + assertEquals(test, base); // not changed + } + + @Test(groups={"tck"}) + public void test_withLaterOffsetAtOverlap_atOverlap() { + ZonedDateTime base = ZonedDateTime.ofStrict(TEST_PARIS_OVERLAP_2008_10_26_02_30, OFFSET_0200, ZONE_PARIS); + ZonedDateTime test = base.withLaterOffsetAtOverlap(); + assertEquals(test.getOffset(), OFFSET_0100); // offset changed to later + assertEquals(test.getDateTime(), base.getDateTime()); // date-time not changed + } + + @Test(groups={"tck"}) + public void test_withLaterOffsetAtOverlap_atOverlap_noChange() { + ZonedDateTime base = ZonedDateTime.ofStrict(TEST_PARIS_OVERLAP_2008_10_26_02_30, OFFSET_0100, ZONE_PARIS); + ZonedDateTime test = base.withLaterOffsetAtOverlap(); + assertEquals(test, base); // not changed + } + + //----------------------------------------------------------------------- + // withZoneSameLocal(ZoneId) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withZoneSameLocal() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime base = ZonedDateTime.of(ldt, ZONE_0100); + ZonedDateTime test = base.withZoneSameLocal(ZONE_0200); + assertEquals(test.getDateTime(), base.getDateTime()); + } + + @Test(groups={"implementation"}) + public void test_withZoneSameLocal_noChange() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime base = ZonedDateTime.of(ldt, ZONE_0100); + ZonedDateTime test = base.withZoneSameLocal(ZONE_0100); + assertEquals(test, base); + } + + @Test(groups={"tck"}) + public void test_withZoneSameLocal_retainOffset1() { + LocalDateTime ldt = LocalDateTime.of(2008, 11, 2, 1, 30, 59, 0); // overlap + ZonedDateTime base = ZonedDateTime.of(ldt, ZoneId.of("UTC-04:00") ); + ZonedDateTime test = base.withZoneSameLocal(ZoneId.of("America/New_York")); + assertEquals(base.getOffset(), ZoneOffset.ofHours(-4)); + assertEquals(test.getOffset(), ZoneOffset.ofHours(-4)); + } + + @Test(groups={"tck"}) + public void test_withZoneSameLocal_retainOffset2() { + LocalDateTime ldt = LocalDateTime.of(2008, 11, 2, 1, 30, 59, 0); // overlap + ZonedDateTime base = ZonedDateTime.of(ldt, ZoneId.of("UTC-05:00") ); + ZonedDateTime test = base.withZoneSameLocal(ZoneId.of("America/New_York")); + assertEquals(base.getOffset(), ZoneOffset.ofHours(-5)); + assertEquals(test.getOffset(), ZoneOffset.ofHours(-5)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_withZoneSameLocal_null() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime base = ZonedDateTime.of(ldt, ZONE_0100); + base.withZoneSameLocal(null); + } + + //----------------------------------------------------------------------- + // withZoneSameInstant() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withZoneSameInstant() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_0100); + ZonedDateTime test = base.withZoneSameInstant(ZONE_0200); + ZonedDateTime expected = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500.plusHours(1), ZONE_0200); + assertEquals(test, expected); + } + + @Test(groups={"tck"}) + public void test_withZoneSameInstant_noChange() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_0100); + ZonedDateTime test = base.withZoneSameInstant(ZONE_0100); + assertEquals(test, base); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_withZoneSameInstant_null() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_0100); + base.withZoneSameInstant(null); + } + + //----------------------------------------------------------------------- + // withFixedOffsetZone() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withZoneLocked() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_PARIS); + ZonedDateTime test = base.withFixedOffsetZone(); + ZonedDateTime expected = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_0200); + assertEquals(test, expected); + } + + //----------------------------------------------------------------------- + // with(WithAdjuster) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_with_WithAdjuster_LocalDateTime_sameOffset() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_PARIS); + ZonedDateTime test = base.with(LocalDateTime.of(2012, 7, 15, 14, 30)); + check(test, 2012, 7, 15, 14, 30, 0, 0, OFFSET_0200, ZONE_PARIS); + } + + @Test(groups={"tck"}) + public void test_with_WithAdjuster_LocalDateTime_adjustedOffset() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_PARIS); + ZonedDateTime test = base.with(LocalDateTime.of(2012, 1, 15, 14, 30)); + check(test, 2012, 1, 15, 14, 30, 0, 0, OFFSET_0100, ZONE_PARIS); + } + + @Test(groups={"tck"}) + public void test_with_WithAdjuster_LocalDate() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_PARIS); + ZonedDateTime test = base.with(LocalDate.of(2012, 7, 28)); + check(test, 2012, 7, 28, 11, 30, 59, 500, OFFSET_0200, ZONE_PARIS); + } + + @Test(groups={"tck"}) + public void test_with_WithAdjuster_LocalTime() { + ZonedDateTime base = ZonedDateTime.of(TEST_PARIS_OVERLAP_2008_10_26_02_30, ZONE_PARIS); + ZonedDateTime test = base.with(LocalTime.of(2, 29)); + check(test, 2008, 10, 26, 2, 29, 0, 0, OFFSET_0200, ZONE_PARIS); + } + + @Test(groups={"tck"}) + public void test_with_WithAdjuster_Year() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime base = ZonedDateTime.of(ldt, ZONE_0100); + ZonedDateTime test = base.with(Year.of(2007)); + assertEquals(test, ZonedDateTime.of(ldt.withYear(2007), ZONE_0100)); + } + + @Test(groups={"tck"}) + public void test_with_WithAdjuster_Month_adjustedDayOfMonth() { + ZonedDateTime base = ZonedDateTime.of(LocalDateTime.of(2012, 7, 31, 0, 0), ZONE_PARIS); + ZonedDateTime test = base.with(Month.JUNE); + check(test, 2012, 6, 30, 0, 0, 0, 0, OFFSET_0200, ZONE_PARIS); + } + + @Test(groups={"tck"}) + public void test_with_WithAdjuster_Offset_same() { + ZonedDateTime base = ZonedDateTime.of(LocalDateTime.of(2012, 7, 31, 0, 0), ZONE_PARIS); + ZonedDateTime test = base.with(ZoneOffset.ofHours(2)); + check(test, 2012, 7, 31, 0, 0, 0, 0, OFFSET_0200, ZONE_PARIS); + } + + @Test(groups={"tck"}) + public void test_with_WithAdjuster_Offset_timeAdjust() { + ZonedDateTime base = ZonedDateTime.of(LocalDateTime.of(2012, 7, 31, 0, 0), ZONE_PARIS); + ZonedDateTime test = base.with(ZoneOffset.ofHours(1)); + check(test, 2012, 7, 31, 1, 0, 0, 0, OFFSET_0200, ZONE_PARIS); // time adjusted + } + + @Test(groups={"tck"}) + public void test_with_WithAdjuster_LocalDate_retainOffset1() { + ZoneId newYork = ZoneId.of("America/New_York"); + LocalDateTime ldt = LocalDateTime.of(2008, 11, 1, 1, 30); + ZonedDateTime base = ZonedDateTime.of(ldt, newYork); + assertEquals(base.getOffset(), ZoneOffset.ofHours(-4)); + ZonedDateTime test = base.with(LocalDate.of(2008, 11, 2)); + assertEquals(test.getOffset(), ZoneOffset.ofHours(-4)); + } + + @Test(groups={"tck"}) + public void test_with_WithAdjuster_LocalDate_retainOffset2() { + ZoneId newYork = ZoneId.of("America/New_York"); + LocalDateTime ldt = LocalDateTime.of(2008, 11, 3, 1, 30); + ZonedDateTime base = ZonedDateTime.of(ldt, newYork); + assertEquals(base.getOffset(), ZoneOffset.ofHours(-5)); + ZonedDateTime test = base.with(LocalDate.of(2008, 11, 2)); + assertEquals(test.getOffset(), ZoneOffset.ofHours(-5)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_with_WithAdjuster_null() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_0100); + base.with((TemporalAdjuster) null); + } + + //----------------------------------------------------------------------- + // withYear() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withYear_normal() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_0100); + ZonedDateTime test = base.withYear(2007); + assertEquals(test, ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500.withYear(2007), ZONE_0100)); + } + + @Test(groups={"tck"}) + public void test_withYear_noChange() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_0100); + ZonedDateTime test = base.withYear(2008); + assertEquals(test, base); + } + + //----------------------------------------------------------------------- + // with(Month) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withMonth_Month_normal() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_0100); + ZonedDateTime test = base.with(JANUARY); + assertEquals(test, ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500.withMonth(1), ZONE_0100)); + } + + @Test(expectedExceptions = NullPointerException.class, groups={"tck"}) + public void test_withMonth_Month_null() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_0100); + base.with((Month) null); + } + + //----------------------------------------------------------------------- + // withMonth() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withMonth_normal() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_0100); + ZonedDateTime test = base.withMonth(1); + assertEquals(test, ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500.withMonth(1), ZONE_0100)); + } + + @Test(groups={"tck"}) + public void test_withMonth_noChange() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_0100); + ZonedDateTime test = base.withMonth(6); + assertEquals(test, base); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withMonth_tooBig() { + TEST_DATE_TIME.withMonth(13); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withMonth_tooSmall() { + TEST_DATE_TIME.withMonth(0); + } + + //----------------------------------------------------------------------- + // withDayOfMonth() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withDayOfMonth_normal() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_0100); + ZonedDateTime test = base.withDayOfMonth(15); + assertEquals(test, ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500.withDayOfMonth(15), ZONE_0100)); + } + + @Test(groups={"tck"}) + public void test_withDayOfMonth_noChange() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_0100); + ZonedDateTime test = base.withDayOfMonth(30); + assertEquals(test, base); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withDayOfMonth_tooBig() { + LocalDateTime.of(2007, 7, 2, 11, 30).atZone(ZONE_PARIS).withDayOfMonth(32); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withDayOfMonth_tooSmall() { + TEST_DATE_TIME.withDayOfMonth(0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withDayOfMonth_invalid31() { + LocalDateTime.of(2007, 6, 2, 11, 30).atZone(ZONE_PARIS).withDayOfMonth(31); + } + + //----------------------------------------------------------------------- + // withDayOfYear() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withDayOfYear_normal() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_0100); + ZonedDateTime test = base.withDayOfYear(33); + assertEquals(test, ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500.withDayOfYear(33), ZONE_0100)); + } + + @Test(groups={"tck"}) + public void test_withDayOfYear_noChange() { + LocalDateTime ldt = LocalDateTime.of(2008, 2, 5, 23, 30, 59, 0); + ZonedDateTime base = ZonedDateTime.of(ldt, ZONE_0100); + ZonedDateTime test = base.withDayOfYear(36); + assertEquals(test, base); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withDayOfYear_tooBig() { + TEST_DATE_TIME.withDayOfYear(367); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withDayOfYear_tooSmall() { + TEST_DATE_TIME.withDayOfYear(0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withDayOfYear_invalid366() { + LocalDateTime.of(2007, 2, 2, 11, 30).atZone(ZONE_PARIS).withDayOfYear(366); + } + + //----------------------------------------------------------------------- + // withHour() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withHour_normal() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_0100); + ZonedDateTime test = base.withHour(15); + assertEquals(test, ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500.withHour(15), ZONE_0100)); + } + + @Test(groups={"tck"}) + public void test_withHour_noChange() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_0100); + ZonedDateTime test = base.withHour(11); + assertEquals(test, base); + } + + //----------------------------------------------------------------------- + // withMinute() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withMinute_normal() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_0100); + ZonedDateTime test = base.withMinute(15); + assertEquals(test, ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500.withMinute(15), ZONE_0100)); + } + + @Test(groups={"tck"}) + public void test_withMinute_noChange() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_0100); + ZonedDateTime test = base.withMinute(30); + assertEquals(test, base); + } + + //----------------------------------------------------------------------- + // withSecond() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withSecond_normal() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_0100); + ZonedDateTime test = base.withSecond(12); + assertEquals(test, ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500.withSecond(12), ZONE_0100)); + } + + @Test(groups={"tck"}) + public void test_withSecond_noChange() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_0100); + ZonedDateTime test = base.withSecond(59); + assertEquals(test, base); + } + + //----------------------------------------------------------------------- + // withNano() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withNanoOfSecond_normal() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_0100); + ZonedDateTime test = base.withNano(15); + assertEquals(test, ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500.withNano(15), ZONE_0100)); + } + + @Test(groups={"tck"}) + public void test_withNanoOfSecond_noChange() { + ZonedDateTime base = ZonedDateTime.of(TEST_LOCAL_2008_06_30_11_30_59_500, ZONE_0100); + ZonedDateTime test = base.withNano(500); + assertEquals(test, base); + } + + //----------------------------------------------------------------------- + // truncatedTo(TemporalUnit) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_truncatedTo_normal() { + assertEquals(TEST_DATE_TIME.truncatedTo(NANOS), TEST_DATE_TIME); + assertEquals(TEST_DATE_TIME.truncatedTo(SECONDS), TEST_DATE_TIME.withNano(0)); + assertEquals(TEST_DATE_TIME.truncatedTo(DAYS), TEST_DATE_TIME.with(LocalTime.MIDNIGHT)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_truncatedTo_null() { + TEST_DATE_TIME.truncatedTo(null); + } + + //----------------------------------------------------------------------- + // plus/minus + //----------------------------------------------------------------------- + @DataProvider(name="plusDays") + Object[][] data_plusDays() { + return new Object[][] { + // normal + {dateTime(2008, 6, 30, 23, 30, 59, 0, OFFSET_0100, ZONE_0100), 0, dateTime(2008, 6, 30, 23, 30, 59, 0, OFFSET_0100, ZONE_0100)}, + {dateTime(2008, 6, 30, 23, 30, 59, 0, OFFSET_0100, ZONE_0100), 1, dateTime(2008, 7, 1, 23, 30, 59, 0, OFFSET_0100, ZONE_0100)}, + {dateTime(2008, 6, 30, 23, 30, 59, 0, OFFSET_0100, ZONE_0100), -1, dateTime(2008, 6, 29, 23, 30, 59, 0, OFFSET_0100, ZONE_0100)}, + // skip over gap + {dateTime(2008, 3, 30, 1, 30, 0, 0, OFFSET_0100, ZONE_PARIS), 1, dateTime(2008, 3, 31, 1, 30, 0, 0, OFFSET_0200, ZONE_PARIS)}, + {dateTime(2008, 3, 30, 3, 30, 0, 0, OFFSET_0200, ZONE_PARIS), -1, dateTime(2008, 3, 29, 3, 30, 0, 0, OFFSET_0100, ZONE_PARIS)}, + // land in gap + {dateTime(2008, 3, 29, 2, 30, 0, 0, OFFSET_0100, ZONE_PARIS), 1, dateTime(2008, 3, 30, 3, 30, 0, 0, OFFSET_0200, ZONE_PARIS)}, + {dateTime(2008, 3, 31, 2, 30, 0, 0, OFFSET_0200, ZONE_PARIS), -1, dateTime(2008, 3, 30, 3, 30, 0, 0, OFFSET_0200, ZONE_PARIS)}, + // skip over overlap + {dateTime(2008, 10, 26, 1, 30, 0, 0, OFFSET_0200, ZONE_PARIS), 1, dateTime(2008, 10, 27, 1, 30, 0, 0, OFFSET_0100, ZONE_PARIS)}, + {dateTime(2008, 10, 25, 3, 30, 0, 0, OFFSET_0200, ZONE_PARIS), 1, dateTime(2008, 10, 26, 3, 30, 0, 0, OFFSET_0100, ZONE_PARIS)}, + // land in overlap + {dateTime(2008, 10, 25, 2, 30, 0, 0, OFFSET_0200, ZONE_PARIS), 1, dateTime(2008, 10, 26, 2, 30, 0, 0, OFFSET_0200, ZONE_PARIS)}, + {dateTime(2008, 10, 27, 2, 30, 0, 0, OFFSET_0100, ZONE_PARIS), -1, dateTime(2008, 10, 26, 2, 30, 0, 0, OFFSET_0100, ZONE_PARIS)}, + }; + } + + @DataProvider(name="plusTime") + Object[][] data_plusTime() { + return new Object[][] { + // normal + {dateTime(2008, 6, 30, 23, 30, 59, 0, OFFSET_0100, ZONE_0100), 0, dateTime(2008, 6, 30, 23, 30, 59, 0, OFFSET_0100, ZONE_0100)}, + {dateTime(2008, 6, 30, 23, 30, 59, 0, OFFSET_0100, ZONE_0100), 1, dateTime(2008, 7, 1, 0, 30, 59, 0, OFFSET_0100, ZONE_0100)}, + {dateTime(2008, 6, 30, 23, 30, 59, 0, OFFSET_0100, ZONE_0100), -1, dateTime(2008, 6, 30, 22, 30, 59, 0, OFFSET_0100, ZONE_0100)}, + // gap + {dateTime(2008, 3, 30, 1, 30, 0, 0, OFFSET_0100, ZONE_PARIS), 1, dateTime(2008, 3, 30, 3, 30, 0, 0, OFFSET_0200, ZONE_PARIS)}, + {dateTime(2008, 3, 30, 3, 30, 0, 0, OFFSET_0200, ZONE_PARIS), -1, dateTime(2008, 3, 30, 1, 30, 0, 0, OFFSET_0100, ZONE_PARIS)}, + // overlap + {dateTime(2008, 10, 26, 1, 30, 0, 0, OFFSET_0200, ZONE_PARIS), 1, dateTime(2008, 10, 26, 2, 30, 0, 0, OFFSET_0200, ZONE_PARIS)}, + {dateTime(2008, 10, 26, 1, 30, 0, 0, OFFSET_0200, ZONE_PARIS), 2, dateTime(2008, 10, 26, 2, 30, 0, 0, OFFSET_0100, ZONE_PARIS)}, + {dateTime(2008, 10, 26, 1, 30, 0, 0, OFFSET_0200, ZONE_PARIS), 3, dateTime(2008, 10, 26, 3, 30, 0, 0, OFFSET_0100, ZONE_PARIS)}, + {dateTime(2008, 10, 26, 2, 30, 0, 0, OFFSET_0200, ZONE_PARIS), 1, dateTime(2008, 10, 26, 2, 30, 0, 0, OFFSET_0100, ZONE_PARIS)}, + {dateTime(2008, 10, 26, 2, 30, 0, 0, OFFSET_0200, ZONE_PARIS), 2, dateTime(2008, 10, 26, 3, 30, 0, 0, OFFSET_0100, ZONE_PARIS)}, + }; + } + + //----------------------------------------------------------------------- + // plus(adjuster) + //----------------------------------------------------------------------- + @Test(groups={"tck"}, dataProvider="plusDays") + public void test_plus_adjuster_Period_days(ZonedDateTime base, long amount, ZonedDateTime expected) { + assertEquals(base.plus(Period.of(amount, DAYS)), expected); + } + + @Test(groups={"tck"}, dataProvider="plusTime") + public void test_plus_adjuster_Period_hours(ZonedDateTime base, long amount, ZonedDateTime expected) { + assertEquals(base.plus(Period.of(amount, HOURS)), expected); + } + + @Test(groups={"tck"}, dataProvider="plusTime") + public void test_plus_adjuster_Duration_hours(ZonedDateTime base, long amount, ZonedDateTime expected) { + assertEquals(base.plus(Duration.ofHours(amount)), expected); + } + + @Test(groups={"tck"}) + public void test_plus_adjuster() { + MockSimplePeriod period = MockSimplePeriod.of(7, ChronoUnit.MONTHS); + ZonedDateTime t = ZonedDateTime.of(LocalDateTime.of(2008, 6, 1, 12, 30, 59, 500), ZONE_0100); + ZonedDateTime expected = ZonedDateTime.of(LocalDateTime.of(2009, 1, 1, 12, 30, 59, 500), ZONE_0100); + assertEquals(t.plus(period), expected); + } + + @Test(groups={"tck"}) + public void test_plus_adjuster_Duration() { + Duration duration = Duration.ofSeconds(4L * 60 * 60 + 5L * 60 + 6L); + ZonedDateTime t = ZonedDateTime.of(LocalDateTime.of(2008, 6, 1, 12, 30, 59, 500), ZONE_0100); + ZonedDateTime expected = ZonedDateTime.of(LocalDateTime.of(2008, 6, 1, 16, 36, 5, 500), ZONE_0100); + assertEquals(t.plus(duration), expected); + } + + @Test(groups={"tck"}) + public void test_plus_adjuster_Period_zero() { + ZonedDateTime t = TEST_DATE_TIME.plus(MockSimplePeriod.ZERO_DAYS); + assertEquals(t, TEST_DATE_TIME); + } + + @Test(groups={"tck"}) + public void test_plus_adjuster_Duration_zero() { + ZonedDateTime t = TEST_DATE_TIME.plus(Duration.ZERO); + assertEquals(t, TEST_DATE_TIME); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_plus_adjuster_null() { + TEST_DATE_TIME.plus((TemporalAdder) null); + } + + //----------------------------------------------------------------------- + // plus(long,TemporalUnit) + //----------------------------------------------------------------------- + @Test(groups={"tck"}, dataProvider="plusDays") + public void test_plus_longUnit_days(ZonedDateTime base, long amount, ZonedDateTime expected) { + assertEquals(base.plus(amount, DAYS), expected); + } + + @Test(groups={"tck"}, dataProvider="plusTime") + public void test_plus_longUnit_hours(ZonedDateTime base, long amount, ZonedDateTime expected) { + assertEquals(base.plus(amount, HOURS), expected); + } + + @Test(groups={"tck"}, dataProvider="plusTime") + public void test_plus_longUnit_minutes(ZonedDateTime base, long amount, ZonedDateTime expected) { + assertEquals(base.plus(amount * 60, MINUTES), expected); + } + + @Test(groups={"tck"}, dataProvider="plusTime") + public void test_plus_longUnit_seconds(ZonedDateTime base, long amount, ZonedDateTime expected) { + assertEquals(base.plus(amount * 3600, SECONDS), expected); + } + + @Test(groups={"tck"}, dataProvider="plusTime") + public void test_plus_longUnit_nanos(ZonedDateTime base, long amount, ZonedDateTime expected) { + assertEquals(base.plus(amount * 3600_000_000_000L, NANOS), expected); + } + + @Test(groups={"tck"}, expectedExceptions=NullPointerException.class) + public void test_plus_longUnit_null() { + TEST_DATE_TIME_PARIS.plus(0, null); + } + + //----------------------------------------------------------------------- + // plusYears() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusYears() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime base = ZonedDateTime.of(ldt, ZONE_0100); + ZonedDateTime test = base.plusYears(1); + assertEquals(test, ZonedDateTime.of(ldt.plusYears(1), ZONE_0100)); + } + + @Test(groups={"tck"}) + public void test_plusYears_zero() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime base = ZonedDateTime.of(ldt, ZONE_0100); + ZonedDateTime test = base.plusYears(0); + assertEquals(test, base); + } + + //----------------------------------------------------------------------- + // plusMonths() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusMonths() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime base = ZonedDateTime.of(ldt, ZONE_0100); + ZonedDateTime test = base.plusMonths(1); + assertEquals(test, ZonedDateTime.of(ldt.plusMonths(1), ZONE_0100)); + } + + @Test(groups={"tck"}) + public void test_plusMonths_zero() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime base = ZonedDateTime.of(ldt, ZONE_0100); + ZonedDateTime test = base.plusMonths(0); + assertEquals(test, base); + } + + //----------------------------------------------------------------------- + // plusWeeks() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusWeeks() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime base = ZonedDateTime.of(ldt, ZONE_0100); + ZonedDateTime test = base.plusWeeks(1); + assertEquals(test, ZonedDateTime.of(ldt.plusWeeks(1), ZONE_0100)); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_zero() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime base = ZonedDateTime.of(ldt, ZONE_0100); + ZonedDateTime test = base.plusWeeks(0); + assertEquals(test, base); + } + + //----------------------------------------------------------------------- + // plusDays() + //----------------------------------------------------------------------- + @Test(groups={"tck"}, dataProvider="plusDays") + public void test_plusDays(ZonedDateTime base, long amount, ZonedDateTime expected) { + assertEquals(base.plusDays(amount), expected); + } + + //----------------------------------------------------------------------- + // plusHours() + //----------------------------------------------------------------------- + @Test(groups={"tck"}, dataProvider="plusTime") + public void test_plusHours(ZonedDateTime base, long amount, ZonedDateTime expected) { + assertEquals(base.plusHours(amount), expected); + } + + //----------------------------------------------------------------------- + // plusMinutes() + //----------------------------------------------------------------------- + @Test(groups={"tck"}, dataProvider="plusTime") + public void test_plusMinutes(ZonedDateTime base, long amount, ZonedDateTime expected) { + assertEquals(base.plusMinutes(amount * 60), expected); + } + + @Test(groups={"tck"}) + public void test_plusMinutes_minutes() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime base = ZonedDateTime.of(ldt, ZONE_0100); + ZonedDateTime test = base.plusMinutes(30); + assertEquals(test, ZonedDateTime.of(ldt.plusMinutes(30), ZONE_0100)); + } + + //----------------------------------------------------------------------- + // plusSeconds() + //----------------------------------------------------------------------- + @Test(groups={"tck"}, dataProvider="plusTime") + public void test_plusSeconds(ZonedDateTime base, long amount, ZonedDateTime expected) { + assertEquals(base.plusSeconds(amount * 3600), expected); + } + + @Test(groups={"tck"}) + public void test_plusSeconds_seconds() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime base = ZonedDateTime.of(ldt, ZONE_0100); + ZonedDateTime test = base.plusSeconds(1); + assertEquals(test, ZonedDateTime.of(ldt.plusSeconds(1), ZONE_0100)); + } + + //----------------------------------------------------------------------- + // plusNanos() + //----------------------------------------------------------------------- + @Test(groups={"tck"}, dataProvider="plusTime") + public void test_plusNanos(ZonedDateTime base, long amount, ZonedDateTime expected) { + assertEquals(base.plusNanos(amount * 3600_000_000_000L), expected); + } + + @Test(groups={"tck"}) + public void test_plusNanos_nanos() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime base = ZonedDateTime.of(ldt, ZONE_0100); + ZonedDateTime test = base.plusNanos(1); + assertEquals(test, ZonedDateTime.of(ldt.plusNanos(1), ZONE_0100)); + } + + //----------------------------------------------------------------------- + // minus(adjuster) + //----------------------------------------------------------------------- + @Test(groups={"tck"}, dataProvider="plusDays") + public void test_minus_adjuster_Period_days(ZonedDateTime base, long amount, ZonedDateTime expected) { + assertEquals(base.minus(Period.of(-amount, DAYS)), expected); + } + + @Test(groups={"tck"}, dataProvider="plusTime") + public void test_minus_adjuster_Period_hours(ZonedDateTime base, long amount, ZonedDateTime expected) { + assertEquals(base.minus(Period.of(-amount, HOURS)), expected); + } + + @Test(groups={"tck"}, dataProvider="plusTime") + public void test_minus_adjuster_Duration_hours(ZonedDateTime base, long amount, ZonedDateTime expected) { + assertEquals(base.minus(Duration.ofHours(-amount)), expected); + } + + @Test(groups={"tck"}) + public void test_minus_adjuster() { + MockSimplePeriod period = MockSimplePeriod.of(7, ChronoUnit.MONTHS); + ZonedDateTime t = ZonedDateTime.of(LocalDateTime.of(2008, 6, 1, 12, 30, 59, 500), ZONE_0100); + ZonedDateTime expected = ZonedDateTime.of(LocalDateTime.of(2007, 11, 1, 12, 30, 59, 500), ZONE_0100); + assertEquals(t.minus(period), expected); + } + + @Test(groups={"tck"}) + public void test_minus_adjuster_Duration() { + Duration duration = Duration.ofSeconds(4L * 60 * 60 + 5L * 60 + 6L); + ZonedDateTime t = ZonedDateTime.of(LocalDateTime.of(2008, 6, 1, 12, 30, 59, 500), ZONE_0100); + ZonedDateTime expected = ZonedDateTime.of(LocalDateTime.of(2008, 6, 1, 8, 25, 53, 500), ZONE_0100); + assertEquals(t.minus(duration), expected); + } + + @Test(groups={"tck"}) + public void test_minus_adjuster_Period_zero() { + ZonedDateTime t = TEST_DATE_TIME.minus(MockSimplePeriod.ZERO_DAYS); + assertEquals(t, TEST_DATE_TIME); + } + + @Test(groups={"tck"}) + public void test_minus_adjuster_Duration_zero() { + ZonedDateTime t = TEST_DATE_TIME.minus(Duration.ZERO); + assertEquals(t, TEST_DATE_TIME); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_minus_adjuster_null() { + TEST_DATE_TIME.minus((TemporalSubtractor) null); + } + + //----------------------------------------------------------------------- + // minusYears() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusYears() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime base = ZonedDateTime.of(ldt, ZONE_0100); + ZonedDateTime test = base.minusYears(1); + assertEquals(test, ZonedDateTime.of(ldt.minusYears(1), ZONE_0100)); + } + + @Test(groups={"tck"}) + public void test_minusYears_zero() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime base = ZonedDateTime.of(ldt, ZONE_0100); + ZonedDateTime test = base.minusYears(0); + assertEquals(test, base); + } + + //----------------------------------------------------------------------- + // minusMonths() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusMonths() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime base = ZonedDateTime.of(ldt, ZONE_0100); + ZonedDateTime test = base.minusMonths(1); + assertEquals(test, ZonedDateTime.of(ldt.minusMonths(1), ZONE_0100)); + } + + @Test(groups={"tck"}) + public void test_minusMonths_zero() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime base = ZonedDateTime.of(ldt, ZONE_0100); + ZonedDateTime test = base.minusMonths(0); + assertEquals(test, base); + } + + //----------------------------------------------------------------------- + // minusWeeks() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusWeeks() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime base = ZonedDateTime.of(ldt, ZONE_0100); + ZonedDateTime test = base.minusWeeks(1); + assertEquals(test, ZonedDateTime.of(ldt.minusWeeks(1), ZONE_0100)); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_zero() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime base = ZonedDateTime.of(ldt, ZONE_0100); + ZonedDateTime test = base.minusWeeks(0); + assertEquals(test, base); + } + + //----------------------------------------------------------------------- + // minusDays() + //----------------------------------------------------------------------- + @Test(groups={"tck"}, dataProvider="plusDays") + public void test_minusDays(ZonedDateTime base, long amount, ZonedDateTime expected) { + assertEquals(base.minusDays(-amount), expected); + } + + //----------------------------------------------------------------------- + // minusHours() + //----------------------------------------------------------------------- + @Test(groups={"tck"}, dataProvider="plusTime") + public void test_minusHours(ZonedDateTime base, long amount, ZonedDateTime expected) { + assertEquals(base.minusHours(-amount), expected); + } + + //----------------------------------------------------------------------- + // minusMinutes() + //----------------------------------------------------------------------- + @Test(groups={"tck"}, dataProvider="plusTime") + public void test_minusMinutes(ZonedDateTime base, long amount, ZonedDateTime expected) { + assertEquals(base.minusMinutes(-amount * 60), expected); + } + + @Test(groups={"tck"}) + public void test_minusMinutes_minutes() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime base = ZonedDateTime.of(ldt, ZONE_0100); + ZonedDateTime test = base.minusMinutes(30); + assertEquals(test, ZonedDateTime.of(ldt.minusMinutes(30), ZONE_0100)); + } + + //----------------------------------------------------------------------- + // minusSeconds() + //----------------------------------------------------------------------- + @Test(groups={"tck"}, dataProvider="plusTime") + public void test_minusSeconds(ZonedDateTime base, long amount, ZonedDateTime expected) { + assertEquals(base.minusSeconds(-amount * 3600), expected); + } + + @Test(groups={"tck"}) + public void test_minusSeconds_seconds() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime base = ZonedDateTime.of(ldt, ZONE_0100); + ZonedDateTime test = base.minusSeconds(1); + assertEquals(test, ZonedDateTime.of(ldt.minusSeconds(1), ZONE_0100)); + } + + //----------------------------------------------------------------------- + // minusNanos() + //----------------------------------------------------------------------- + @Test(groups={"tck"}, dataProvider="plusTime") + public void test_minusNanos(ZonedDateTime base, long amount, ZonedDateTime expected) { + assertEquals(base.minusNanos(-amount * 3600_000_000_000L), expected); + } + + @Test(groups={"tck"}) + public void test_minusNanos_nanos() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime base = ZonedDateTime.of(ldt, ZONE_0100); + ZonedDateTime test = base.minusNanos(1); + assertEquals(test, ZonedDateTime.of(ldt.minusNanos(1), ZONE_0100)); + } + + //----------------------------------------------------------------------- + // periodUntil(Temporal,TemporalUnit) + //----------------------------------------------------------------------- + // TODO: more tests for period between two different zones + // compare results to OffsetDateTime.periodUntil, especially wrt dates + + @Test(groups={"tck"}, dataProvider="plusDays") + public void test_periodUntil_days(ZonedDateTime base, long expected, ZonedDateTime end) { + assertEquals(base.periodUntil(end, DAYS), expected); + } + + @Test(groups={"tck"}, dataProvider="plusTime") + public void test_periodUntil_hours(ZonedDateTime base, long expected, ZonedDateTime end) { + assertEquals(base.periodUntil(end, HOURS), expected); + } + + @Test(groups={"tck"}, dataProvider="plusTime") + public void test_periodUntil_minutes(ZonedDateTime base, long expected, ZonedDateTime end) { + assertEquals(base.periodUntil(end, MINUTES), expected * 60); + } + + @Test(groups={"tck"}, dataProvider="plusTime") + public void test_periodUntil_seconds(ZonedDateTime base, long expected, ZonedDateTime end) { + assertEquals(base.periodUntil(end, SECONDS), expected * 3600); + } + + @Test(groups={"tck"}, dataProvider="plusTime") + public void test_periodUntil_nanos(ZonedDateTime base, long expected, ZonedDateTime end) { + assertEquals(base.periodUntil(end, NANOS), expected * 3600_000_000_000L); + } + + @Test(groups={"tck"}) + public void test_periodUntil_parisLondon() { + ZonedDateTime midnightLondon = LocalDate.of(2012, 6, 28).atStartOfDay(ZONE_LONDON); + ZonedDateTime midnightParis1 = LocalDate.of(2012, 6, 29).atStartOfDay(ZONE_PARIS); + ZonedDateTime oneAm1 = LocalDateTime.of(2012, 6, 29, 1, 0).atZone(ZONE_PARIS); + ZonedDateTime midnightParis2 = LocalDate.of(2012, 6, 30).atStartOfDay(ZONE_PARIS); + + assertEquals(midnightLondon.periodUntil(midnightParis1, HOURS), 23); + assertEquals(midnightLondon.periodUntil(oneAm1, HOURS), 24); + assertEquals(midnightLondon.periodUntil(midnightParis2, HOURS), 23 + 24); + + assertEquals(midnightLondon.periodUntil(midnightParis1, DAYS), 0); + assertEquals(midnightLondon.periodUntil(oneAm1, DAYS), 1); + assertEquals(midnightLondon.periodUntil(midnightParis2, DAYS), 1); + } + + @Test(groups={"tck"}) + public void test_periodUntil_gap() { + ZonedDateTime before = TEST_PARIS_GAP_2008_03_30_02_30.withHour(0).withMinute(0).atZone(ZONE_PARIS); + ZonedDateTime after = TEST_PARIS_GAP_2008_03_30_02_30.withHour(0).withMinute(0).plusDays(1).atZone(ZONE_PARIS); + + assertEquals(before.periodUntil(after, HOURS), 23); + assertEquals(before.periodUntil(after, DAYS), 1); + } + + @Test(groups={"tck"}) + public void test_periodUntil_overlap() { + ZonedDateTime before = TEST_PARIS_OVERLAP_2008_10_26_02_30.withHour(0).withMinute(0).atZone(ZONE_PARIS); + ZonedDateTime after = TEST_PARIS_OVERLAP_2008_10_26_02_30.withHour(0).withMinute(0).plusDays(1).atZone(ZONE_PARIS); + + assertEquals(before.periodUntil(after, HOURS), 25); + assertEquals(before.periodUntil(after, DAYS), 1); + } + + @Test(groups={"tck"}, expectedExceptions=DateTimeException.class) + public void test_periodUntil_differentType() { + TEST_DATE_TIME_PARIS.periodUntil(TEST_LOCAL_2008_06_30_11_30_59_500, DAYS); + } + + @Test(groups={"tck"}, expectedExceptions=NullPointerException.class) + public void test_periodUntil_nullTemporal() { + TEST_DATE_TIME_PARIS.periodUntil(null, DAYS); + } + + @Test(groups={"tck"}, expectedExceptions=NullPointerException.class) + public void test_periodUntil_nullUnit() { + TEST_DATE_TIME_PARIS.periodUntil(TEST_DATE_TIME_PARIS, null); + } + + //----------------------------------------------------------------------- + // toOffsetDateTime() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toOffsetDateTime() { + assertEquals(TEST_DATE_TIME.toOffsetDateTime(), OffsetDateTime.of(TEST_DATE_TIME.getDateTime(), TEST_DATE_TIME.getOffset())); + } + + //----------------------------------------------------------------------- + // toInstant() + //----------------------------------------------------------------------- + @DataProvider(name="toInstant") + Object[][] data_toInstant() { + return new Object[][] { + {LocalDateTime.of(1970, 1, 1, 0, 0, 0, 0), 0L, 0}, + {LocalDateTime.of(1970, 1, 1, 0, 0, 0, 1), 0L, 1}, + {LocalDateTime.of(1970, 1, 1, 0, 0, 0, 999_999_999), 0L, 999_999_999}, + {LocalDateTime.of(1970, 1, 1, 0, 0, 1, 0), 1L, 0}, + {LocalDateTime.of(1970, 1, 1, 0, 0, 1, 1), 1L, 1}, + {LocalDateTime.of(1969, 12, 31, 23, 59, 59, 999999999), -1L, 999_999_999}, + {LocalDateTime.of(1970, 1, 2, 0, 0), 24L * 60L * 60L, 0}, + {LocalDateTime.of(1969, 12, 31, 0, 0), -24L * 60L * 60L, 0}, + }; + } + + @Test(groups={"tck"}, dataProvider="toInstant") + public void test_toInstant_UTC(LocalDateTime ldt, long expectedEpSec, int expectedNos) { + ZonedDateTime dt = ldt.atZone(ZoneOffset.UTC); + Instant test = dt.toInstant(); + assertEquals(test.getEpochSecond(), expectedEpSec); + assertEquals(test.getNano(), expectedNos); + } + + @Test(groups={"tck"}, dataProvider="toInstant") + public void test_toInstant_P0100(LocalDateTime ldt, long expectedEpSec, int expectedNos) { + ZonedDateTime dt = ldt.atZone(ZONE_0100); + Instant test = dt.toInstant(); + assertEquals(test.getEpochSecond(), expectedEpSec - 3600); + assertEquals(test.getNano(), expectedNos); + } + + @Test(groups={"tck"}, dataProvider="toInstant") + public void test_toInstant_M0100(LocalDateTime ldt, long expectedEpSec, int expectedNos) { + ZonedDateTime dt = ldt.atZone(ZONE_M0100); + Instant test = dt.toInstant(); + assertEquals(test.getEpochSecond(), expectedEpSec + 3600); + assertEquals(test.getNano(), expectedNos); + } + + //----------------------------------------------------------------------- + // toEpochSecond() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toEpochSecond_afterEpoch() { + LocalDateTime ldt = LocalDateTime.of(1970, 1, 1, 0, 0).plusHours(1); + for (int i = 0; i < 100000; i++) { + ZonedDateTime a = ZonedDateTime.of(ldt, ZONE_PARIS); + assertEquals(a.toEpochSecond(), i); + ldt = ldt.plusSeconds(1); + } + } + + @Test(groups={"tck"}) + public void test_toEpochSecond_beforeEpoch() { + LocalDateTime ldt = LocalDateTime.of(1970, 1, 1, 0, 0).plusHours(1); + for (int i = 0; i < 100000; i++) { + ZonedDateTime a = ZonedDateTime.of(ldt, ZONE_PARIS); + assertEquals(a.toEpochSecond(), -i); + ldt = ldt.minusSeconds(1); + } + } + + @Test(groups={"tck"}, dataProvider="toInstant") + public void test_toEpochSecond_UTC(LocalDateTime ldt, long expectedEpSec, int expectedNos) { + ZonedDateTime dt = ldt.atZone(ZoneOffset.UTC); + assertEquals(dt.toEpochSecond(), expectedEpSec); + } + + @Test(groups={"tck"}, dataProvider="toInstant") + public void test_toEpochSecond_P0100(LocalDateTime ldt, long expectedEpSec, int expectedNos) { + ZonedDateTime dt = ldt.atZone(ZONE_0100); + assertEquals(dt.toEpochSecond(), expectedEpSec - 3600); + } + + @Test(groups={"tck"}, dataProvider="toInstant") + public void test_toEpochSecond_M0100(LocalDateTime ldt, long expectedEpSec, int expectedNos) { + ZonedDateTime dt = ldt.atZone(ZONE_M0100); + assertEquals(dt.toEpochSecond(), expectedEpSec + 3600); + } + + //----------------------------------------------------------------------- + // compareTo() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_compareTo_time1() { + ZonedDateTime a = ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 11, 30, 39), ZONE_0100); + ZonedDateTime b = ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 11, 30, 41), ZONE_0100); // a is before b due to time + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + } + + @Test(groups={"tck"}) + public void test_compareTo_time2() { + ZonedDateTime a = ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 11, 30, 40, 4), ZONE_0100); + ZonedDateTime b = ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 11, 30, 40, 5), ZONE_0100); // a is before b due to time + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + } + + @Test(groups={"tck"}) + public void test_compareTo_offset1() { + ZonedDateTime a = ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 11, 30, 41), ZONE_0200); + ZonedDateTime b = ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 11, 30, 39), ZONE_0100); // a is before b due to offset + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + } + + @Test(groups={"tck"}) + public void test_compareTo_offset2() { + ZonedDateTime a = ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 11, 30, 40, 5), ZoneId.of("UTC+01:01")); + ZonedDateTime b = ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 11, 30, 40, 4), ZONE_0100); // a is before b due to offset + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + } + + @Test(groups={"tck"}) + public void test_compareTo_both() { + ZonedDateTime a = ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 11, 50), ZONE_0200); + ZonedDateTime b = ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 11, 20), ZONE_0100); // a is before b on instant scale + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + } + + @Test(groups={"tck"}) + public void test_compareTo_bothNanos() { + ZonedDateTime a = ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 11, 20, 40, 5), ZONE_0200); + ZonedDateTime b = ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 10, 20, 40, 6), ZONE_0100); // a is before b on instant scale + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + } + + @Test(groups={"tck"}) + public void test_compareTo_hourDifference() { + ZonedDateTime a = ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 10, 0), ZONE_0100); + ZonedDateTime b = ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 11, 0), ZONE_0200); // a is before b despite being same time-line time + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_compareTo_null() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime a = ZonedDateTime.of(ldt, ZONE_0100); + a.compareTo(null); + } + + //----------------------------------------------------------------------- + // isBefore() + //----------------------------------------------------------------------- + @DataProvider(name="IsBefore") + Object[][] data_isBefore() { + return new Object[][] { + {11, 30, ZONE_0100, 11, 31, ZONE_0100, true}, // a is before b due to time + {11, 30, ZONE_0200, 11, 30, ZONE_0100, true}, // a is before b due to offset + {11, 30, ZONE_0200, 10, 30, ZONE_0100, false}, // a is equal b due to same instant + }; + } + + @Test(dataProvider="IsBefore", groups={"tck"}) + public void test_isBefore(int hour1, int minute1, ZoneId zone1, int hour2, int minute2, ZoneId zone2, boolean expected) { + ZonedDateTime a = ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, hour1, minute1), zone1); + ZonedDateTime b = ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, hour2, minute2), zone2); + assertEquals(a.isBefore(b), expected); + assertEquals(b.isBefore(a), false); + assertEquals(a.isBefore(a), false); + assertEquals(b.isBefore(b), false); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_isBefore_null() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime a = ZonedDateTime.of(ldt, ZONE_0100); + a.isBefore(null); + } + + //----------------------------------------------------------------------- + // isAfter() + //----------------------------------------------------------------------- + @DataProvider(name="IsAfter") + Object[][] data_isAfter() { + return new Object[][] { + {11, 31, ZONE_0100, 11, 30, ZONE_0100, true}, // a is after b due to time + {11, 30, ZONE_0100, 11, 30, ZONE_0200, true}, // a is after b due to offset + {11, 30, ZONE_0200, 10, 30, ZONE_0100, false}, // a is equal b due to same instant + }; + } + + @Test(dataProvider="IsAfter", groups={"tck"}) + public void test_isAfter(int hour1, int minute1, ZoneId zone1, int hour2, int minute2, ZoneId zone2, boolean expected) { + ZonedDateTime a = ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, hour1, minute1), zone1); + ZonedDateTime b = ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, hour2, minute2), zone2); + assertEquals(a.isAfter(b), expected); + assertEquals(b.isAfter(a), false); + assertEquals(a.isAfter(a), false); + assertEquals(b.isAfter(b), false); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_isAfter_null() { + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 23, 30, 59, 0); + ZonedDateTime a = ZonedDateTime.of(ldt, ZONE_0100); + a.isAfter(null); + } + + //----------------------------------------------------------------------- + // equals() / hashCode() + //----------------------------------------------------------------------- + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_true(int y, int o, int d, int h, int m, int s, int n, ZoneId ignored) { + ZonedDateTime a = ZonedDateTime.of(dateTime(y, o, d, h, m, s, n), ZONE_0100); + ZonedDateTime b = ZonedDateTime.of(dateTime(y, o, d, h, m, s, n), ZONE_0100); + assertEquals(a.equals(b), true); + assertEquals(a.hashCode() == b.hashCode(), true); + } + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_false_year_differs(int y, int o, int d, int h, int m, int s, int n, ZoneId ignored) { + ZonedDateTime a = ZonedDateTime.of(dateTime(y, o, d, h, m, s, n), ZONE_0100); + ZonedDateTime b = ZonedDateTime.of(dateTime(y + 1, o, d, h, m, s, n), ZONE_0100); + assertEquals(a.equals(b), false); + } + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_false_hour_differs(int y, int o, int d, int h, int m, int s, int n, ZoneId ignored) { + h = (h == 23 ? 22 : h); + ZonedDateTime a = ZonedDateTime.of(dateTime(y, o, d, h, m, s, n), ZONE_0100); + ZonedDateTime b = ZonedDateTime.of(dateTime(y, o, d, h + 1, m, s, n), ZONE_0100); + assertEquals(a.equals(b), false); + } + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_false_minute_differs(int y, int o, int d, int h, int m, int s, int n, ZoneId ignored) { + m = (m == 59 ? 58 : m); + ZonedDateTime a = ZonedDateTime.of(dateTime(y, o, d, h, m, s, n), ZONE_0100); + ZonedDateTime b = ZonedDateTime.of(dateTime(y, o, d, h, m + 1, s, n), ZONE_0100); + assertEquals(a.equals(b), false); + } + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_false_second_differs(int y, int o, int d, int h, int m, int s, int n, ZoneId ignored) { + s = (s == 59 ? 58 : s); + ZonedDateTime a = ZonedDateTime.of(dateTime(y, o, d, h, m, s, n), ZONE_0100); + ZonedDateTime b = ZonedDateTime.of(dateTime(y, o, d, h, m, s + 1, n), ZONE_0100); + assertEquals(a.equals(b), false); + } + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_false_nano_differs(int y, int o, int d, int h, int m, int s, int n, ZoneId ignored) { + n = (n == 999999999 ? 999999998 : n); + ZonedDateTime a = ZonedDateTime.of(dateTime(y, o, d, h, m, s, n), ZONE_0100); + ZonedDateTime b = ZonedDateTime.of(dateTime(y, o, d, h, m, s, n + 1), ZONE_0100); + assertEquals(a.equals(b), false); + } + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_false_offset_differs(int y, int o, int d, int h, int m, int s, int n, ZoneId ignored) { + ZonedDateTime a = ZonedDateTime.of(dateTime(y, o, d, h, m, s, n), ZONE_0100); + ZonedDateTime b = ZonedDateTime.of(dateTime(y, o, d, h, m, s, n), ZONE_0200); + assertEquals(a.equals(b), false); + } + + @Test(groups={"tck"}) + public void test_equals_itself_true() { + assertEquals(TEST_DATE_TIME.equals(TEST_DATE_TIME), true); + } + + @Test(groups={"tck"}) + public void test_equals_string_false() { + assertEquals(TEST_DATE_TIME.equals("2007-07-15"), false); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @DataProvider(name="sampleToString") + Object[][] provider_sampleToString() { + return new Object[][] { + {2008, 6, 30, 11, 30, 59, 0, "Z", "2008-06-30T11:30:59Z"}, + {2008, 6, 30, 11, 30, 59, 0, "+01:00", "2008-06-30T11:30:59+01:00"}, + {2008, 6, 30, 11, 30, 59, 999000000, "Z", "2008-06-30T11:30:59.999Z"}, + {2008, 6, 30, 11, 30, 59, 999000000, "+01:00", "2008-06-30T11:30:59.999+01:00"}, + {2008, 6, 30, 11, 30, 59, 999000, "Z", "2008-06-30T11:30:59.000999Z"}, + {2008, 6, 30, 11, 30, 59, 999000, "+01:00", "2008-06-30T11:30:59.000999+01:00"}, + {2008, 6, 30, 11, 30, 59, 999, "Z", "2008-06-30T11:30:59.000000999Z"}, + {2008, 6, 30, 11, 30, 59, 999, "+01:00", "2008-06-30T11:30:59.000000999+01:00"}, + + {2008, 6, 30, 11, 30, 59, 999, "Europe/London", "2008-06-30T11:30:59.000000999+01:00[Europe/London]"}, + {2008, 6, 30, 11, 30, 59, 999, "Europe/Paris", "2008-06-30T11:30:59.000000999+02:00[Europe/Paris]"}, + }; + } + + @Test(dataProvider="sampleToString", groups={"tck"}) + public void test_toString(int y, int o, int d, int h, int m, int s, int n, String zoneId, String expected) { + ZonedDateTime t = ZonedDateTime.of(dateTime(y, o, d, h, m, s, n), ZoneId.of(zoneId)); + String str = t.toString(); + assertEquals(str, expected); + } + + //----------------------------------------------------------------------- + // toString(DateTimeFormatter) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toString_formatter() { + DateTimeFormatter f = DateTimeFormatters.pattern("y M d H m s"); + String t = ZonedDateTime.of(dateTime(2010, 12, 3, 11, 30), ZONE_PARIS).toString(f); + assertEquals(t, "2010 12 3 11 30 0"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_toString_formatter_null() { + ZonedDateTime.of(dateTime(2010, 12, 3, 11, 30), ZONE_PARIS).toString(null); + } + + //------------------------------------------------------------------------- + private static LocalDateTime dateTime( + int year, int month, int dayOfMonth, + int hour, int minute) { + return LocalDateTime.of(year, month, dayOfMonth, hour, minute); + } + + private static LocalDateTime dateTime( + int year, int month, int dayOfMonth, + int hour, int minute, int second, int nanoOfSecond) { + return LocalDateTime.of(year, month, dayOfMonth, hour, minute, second, nanoOfSecond); + } + + private static ZonedDateTime dateTime( + int year, int month, int dayOfMonth, + int hour, int minute, int second, int nanoOfSecond, ZoneOffset offset, ZoneId zoneId) { + return ZonedDateTime.ofStrict(LocalDateTime.of(year, month, dayOfMonth, hour, minute, second, nanoOfSecond), offset, zoneId); + } + +} diff --git a/jdk/test/java/time/tck/java/time/calendar/CopticChrono.java b/jdk/test/java/time/tck/java/time/calendar/CopticChrono.java new file mode 100644 index 00000000000..c3dc941e491 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/calendar/CopticChrono.java @@ -0,0 +1,251 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package tck.java.time.calendar; + +import static java.time.temporal.ChronoField.EPOCH_DAY; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +import java.time.DateTimeException; +import java.time.temporal.ChronoField; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.ValueRange; +import java.time.temporal.Chrono; +import java.time.temporal.ChronoLocalDate; +import java.time.temporal.Era; + +/** + * The Coptic calendar system. + *

    + * This chronology defines the rules of the Coptic calendar system. + * This calendar system is primarily used in Christian Egypt. + * Dates are aligned such that {@code 0001AM-01-01 (Coptic)} is {@code 0284-08-29 (ISO)}. + *

    + * The fields are defined as follows: + *

      + *
    • era - There are two eras, the current 'Era of the Martyrs' (AM) and the previous era (ERA_ERA_BEFORE_AM). + *
    • year-of-era - The year-of-era for the current era increases uniformly from the epoch at year one. + * For the previous era the year increases from one as time goes backwards. + *
    • proleptic-year - The proleptic year is the same as the year-of-era for the + * current era. For the previous era, years have zero, then negative values. + *
    • month-of-year - There are 13 months in a Coptic year, numbered from 1 to 13. + *
    • day-of-month - There are 30 days in each of the first 12 Coptic months, numbered 1 to 30. + * The 13th month has 5 days, or 6 in a leap year, numbered 1 to 5 or 1 to 6. + *
    • day-of-year - There are 365 days in a standard Coptic year and 366 in a leap year. + * The days are numbered from 1 to 365 or 1 to 366. + *
    • leap-year - Leap years occur every 4 years. + *

    + * + *

    Implementation notes

    + * This class is immutable and thread-safe. + */ +public final class CopticChrono extends Chrono implements Serializable { + + /** + * Singleton instance of the Coptic chronology. + */ + public static final CopticChrono INSTANCE = new CopticChrono(); + /** + * The singleton instance for the era BEFORE_AM. + * This has the numeric value of {@code 0}. + */ + public static final Era ERA_BEFORE_AM = CopticEra.BEFORE_AM; + /** + * The singleton instance for the era AM - 'Era of the Martyrs'. + * This has the numeric value of {@code 1}. + */ + public static final Era ERA_AM = CopticEra.AM; + + /** + * Serialization version. + */ + private static final long serialVersionUID = 7291205177830286973L; + /** + * Range of months. + */ + static final ValueRange MOY_RANGE = ValueRange.of(1, 13); + /** + * Range of days. + */ + static final ValueRange DOM_RANGE = ValueRange.of(1, 5, 30); + /** + * Range of days. + */ + static final ValueRange DOM_RANGE_NONLEAP = ValueRange.of(1, 5); + /** + * Range of days. + */ + static final ValueRange DOM_RANGE_LEAP = ValueRange.of(1, 6); + + /** + * Public Constructor to be instantiated by the ServiceLoader + */ + public CopticChrono() { + } + + /** + * Resolve singleton. + * + * @return the singleton instance, not null + */ + private Object readResolve() { + return INSTANCE; + } + + //----------------------------------------------------------------------- + /** + * Gets the ID of the chronology - 'Coptic'. + *

    + * The ID uniquely identifies the {@code Chrono}. + * It can be used to lookup the {@code Chrono} using {@link #of(String)}. + * + * @return the chronology ID - 'Coptic' + * @see #getCalendarType() + */ + @Override + public String getId() { + return "Coptic"; + } + + /** + * Gets the calendar type of the underlying calendar system - 'coptic'. + *

    + * The calendar type is an identifier defined by the + * Unicode Locale Data Markup Language (LDML) specification. + * It can be used to lookup the {@code Chrono} using {@link #of(String)}. + * It can also be used as part of a locale, accessible via + * {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'. + * + * @return the calendar system type - 'coptic' + * @see #getId() + */ + @Override + public String getCalendarType() { + return "coptic"; + } + + //----------------------------------------------------------------------- + @Override + public ChronoLocalDate date(int prolepticYear, int month, int dayOfMonth) { + return new CopticDate(prolepticYear, month, dayOfMonth); + } + + @Override + public ChronoLocalDate dateYearDay(int prolepticYear, int dayOfYear) { + return new CopticDate(prolepticYear, (dayOfYear - 1) / 30 + 1, (dayOfYear - 1) % 30 + 1); + } + + @Override + public ChronoLocalDate date(TemporalAccessor dateTime) { + if (dateTime instanceof CopticDate) { + return (CopticDate) dateTime; + } + return CopticDate.ofEpochDay(dateTime.getLong(EPOCH_DAY)); + } + + //----------------------------------------------------------------------- + /** + * Checks if the specified year is a leap year. + *

    + * A Coptic proleptic-year is leap if the remainder after division by four equals three. + * This method does not validate the year passed in, and only has a + * well-defined result for years in the supported range. + * + * @param prolepticYear the proleptic-year to check, not validated for range + * @return true if the year is a leap year + */ + @Override + public boolean isLeapYear(long prolepticYear) { + return Math.floorMod(prolepticYear, 4) == 3; + } + + @Override + public int prolepticYear(Era era, int yearOfEra) { + if (era instanceof CopticEra == false) { + throw new DateTimeException("Era must be CopticEra"); + } + return (era == CopticEra.AM ? yearOfEra : 1 - yearOfEra); + } + + @Override + public Era eraOf(int eraValue) { + return CopticEra.of(eraValue); + } + + @Override + public List> eras() { + return Arrays.>asList(CopticEra.values()); + } + + //----------------------------------------------------------------------- + @Override + public ValueRange range(ChronoField field) { + switch (field) { + case DAY_OF_MONTH: return ValueRange.of(1, 5, 30); + case ALIGNED_WEEK_OF_MONTH: return ValueRange.of(1, 1, 5); + case MONTH_OF_YEAR: return ValueRange.of(1, 13); + case EPOCH_MONTH: return ValueRange.of(-1000, 1000); // TODO + case YEAR_OF_ERA: return ValueRange.of(1, 999, 1000); // TODO + case YEAR: return ValueRange.of(-1000, 1000); // TODO + } + return field.range(); + } + +} diff --git a/jdk/test/java/time/tck/java/time/calendar/CopticDate.java b/jdk/test/java/time/tck/java/time/calendar/CopticDate.java new file mode 100644 index 00000000000..30f1664dd0e --- /dev/null +++ b/jdk/test/java/time/tck/java/time/calendar/CopticDate.java @@ -0,0 +1,340 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.calendar; + +import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; +import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; +import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; +import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.YEAR_OF_ERA; + +import java.io.Serializable; + +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoLocalDate; +import java.time.temporal.ChronoUnit; +import java.time.temporal.Era; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAdjuster; +import java.time.temporal.TemporalAdder; +import java.time.temporal.TemporalSubtractor; +import java.time.temporal.TemporalField; +import java.time.temporal.TemporalUnit; +import java.time.temporal.ValueRange; +import java.time.temporal.Year; + +/** + * A date in the Coptic calendar system. + *

    + * This implements {@code ChronoLocalDate} for the {@link CopticChrono Coptic calendar}. + * + *

    Implementation notes

    + * This class is immutable and thread-safe. + */ +final class CopticDate + implements ChronoLocalDate, Serializable { + + /** + * Serialization version. + */ + private static final long serialVersionUID = -7920528871688876868L; + /** + * The difference between the Coptic and Coptic epoch day count. + */ + private static final int EPOCH_DAY_DIFFERENCE = 574971 + 40587; + + /** + * The proleptic year. + */ + private final int prolepticYear; + /** + * The month. + */ + private final short month; + /** + * The day. + */ + private final short day; + + //----------------------------------------------------------------------- + /** + * Creates an instance. + * + * @param epochDay the epoch day to convert based on 1970-01-01 (ISO) + * @return the Coptic date, not null + * @throws DateTimeException if the date is invalid + */ + static CopticDate ofEpochDay(long epochDay) { + epochDay += EPOCH_DAY_DIFFERENCE; + int prolepticYear = (int) (((epochDay * 4) + 1463) / 1461); + int startYearEpochDay = (prolepticYear - 1) * 365 + (prolepticYear / 4); + int doy0 = (int) (epochDay - startYearEpochDay); + int month = doy0 / 30 + 1; + int dom = doy0 % 30 + 1; + return new CopticDate(prolepticYear, month, dom); + } + + private static CopticDate resolvePreviousValid(int prolepticYear, int month, int day) { + if (month == 13 && day > 5) { + day = CopticChrono.INSTANCE.isLeapYear(prolepticYear) ? 6 : 5; + } + return new CopticDate(prolepticYear, month, day); + } + + //----------------------------------------------------------------------- + /** + * Creates an instance. + * + * @param prolepticYear the Coptic proleptic-year + * @param month the Coptic month, from 1 to 13 + * @param dayOfMonth the Coptic day-of-month, from 1 to 30 + * @throws DateTimeException if the date is invalid + */ + CopticDate(int prolepticYear, int month, int dayOfMonth) { + CopticChrono.MOY_RANGE.checkValidValue(month, MONTH_OF_YEAR); + ValueRange range; + if (month == 13) { + range = CopticChrono.INSTANCE.isLeapYear(prolepticYear) ? CopticChrono.DOM_RANGE_LEAP : CopticChrono.DOM_RANGE_NONLEAP; + } else { + range = CopticChrono.DOM_RANGE; + } + range.checkValidValue(dayOfMonth, DAY_OF_MONTH); + + this.prolepticYear = prolepticYear; + this.month = (short) month; + this.day = (short) dayOfMonth; + } + + /** + * Validates the object. + * + * @return the resolved date, not null + */ + private Object readResolve() { + // TODO: validate + return this; + } + + //----------------------------------------------------------------------- + @Override + public CopticChrono getChrono() { + return CopticChrono.INSTANCE; + } + + //----------------------------------------------------------------------- + @Override + public int lengthOfMonth() { + switch (month) { + case 13: + return (isLeapYear() ? 6 : 5); + default: + return 30; + } + } + + @Override + public ValueRange range(TemporalField field) { + if (field instanceof ChronoField) { + if (isSupported(field)) { + ChronoField f = (ChronoField) field; + switch (f) { + case DAY_OF_MONTH: return ValueRange.of(1, lengthOfMonth()); + case DAY_OF_YEAR: return ValueRange.of(1, lengthOfYear()); + case ALIGNED_WEEK_OF_MONTH: return ValueRange.of(1, month == 13 ? 1 : 5); + case YEAR: + case YEAR_OF_ERA: return (prolepticYear <= 0 ? + ValueRange.of(1, Year.MAX_VALUE + 1) : ValueRange.of(1, Year.MAX_VALUE)); // TODO + } + return getChrono().range(f); + } + throw new DateTimeException("Unsupported field: " + field.getName()); + } + return field.doRange(this); + } + + @Override + public long getLong(TemporalField field) { + if (field instanceof ChronoField) { + switch ((ChronoField) field) { + case DAY_OF_WEEK: return Math.floorMod(toEpochDay() + 3, 7) + 1; + case ALIGNED_DAY_OF_WEEK_IN_MONTH: return ((day - 1) % 7) + 1; + case ALIGNED_DAY_OF_WEEK_IN_YEAR: return ((get(ChronoField.DAY_OF_YEAR) - 1) % 7) + 1; + case DAY_OF_MONTH: return day; + case DAY_OF_YEAR: return (month - 1) * 30 + day; + case EPOCH_DAY: return toEpochDay(); + case ALIGNED_WEEK_OF_MONTH: return ((day - 1) / 7) + 1; + case ALIGNED_WEEK_OF_YEAR: return ((get(ChronoField.DAY_OF_YEAR) - 1) / 7) + 1; + case MONTH_OF_YEAR: return month; + case YEAR_OF_ERA: return (prolepticYear >= 1 ? prolepticYear : 1 - prolepticYear); + case YEAR: return prolepticYear; + case ERA: return (prolepticYear >= 1 ? 1 : 0); + } + throw new DateTimeException("Unsupported field: " + field.getName()); + } + return field.doGet(this); + } + + @Override + public CopticDate with(TemporalField field, long newValue) { + if (field instanceof ChronoField) { + ChronoField f = (ChronoField) field; + f.checkValidValue(newValue); // TODO: validate value + int nvalue = (int) newValue; + switch (f) { + case DAY_OF_WEEK: return plusDays(newValue - get(ChronoField.DAY_OF_WEEK)); + case ALIGNED_DAY_OF_WEEK_IN_MONTH: return plusDays(newValue - getLong(ALIGNED_DAY_OF_WEEK_IN_MONTH)); + case ALIGNED_DAY_OF_WEEK_IN_YEAR: return plusDays(newValue - getLong(ALIGNED_DAY_OF_WEEK_IN_YEAR)); + case DAY_OF_MONTH: return resolvePreviousValid(prolepticYear, month, nvalue); + case DAY_OF_YEAR: return resolvePreviousValid(prolepticYear, ((nvalue - 1) / 30) + 1, ((nvalue - 1) % 30) + 1); + case EPOCH_DAY: return ofEpochDay(nvalue); + case ALIGNED_WEEK_OF_MONTH: return plusDays((newValue - getLong(ALIGNED_WEEK_OF_MONTH)) * 7); + case ALIGNED_WEEK_OF_YEAR: return plusDays((newValue - getLong(ALIGNED_WEEK_OF_YEAR)) * 7); + case MONTH_OF_YEAR: return resolvePreviousValid(prolepticYear, nvalue, day); + case YEAR_OF_ERA: return resolvePreviousValid(prolepticYear >= 1 ? nvalue : 1 - nvalue, month, day); + case YEAR: return resolvePreviousValid(nvalue, month, day); + case ERA: return resolvePreviousValid(1 - prolepticYear, month, day); + } + throw new DateTimeException("Unsupported field: " + field.getName()); + } + return field.doWith(this, newValue); + } + + //----------------------------------------------------------------------- + @Override + public CopticDate plus(long amountToAdd, TemporalUnit unit) { + if (unit instanceof ChronoUnit) { + ChronoUnit f = (ChronoUnit) unit; + switch (f) { + case DAYS: return plusDays(amountToAdd); + case WEEKS: return plusDays(Math.multiplyExact(amountToAdd, 7)); + case MONTHS: return plusMonths(amountToAdd); + case YEARS: return plusYears(amountToAdd); + case DECADES: return plusYears(Math.multiplyExact(amountToAdd, 10)); + case CENTURIES: return plusYears(Math.multiplyExact(amountToAdd, 100)); + case MILLENNIA: return plusYears(Math.multiplyExact(amountToAdd, 1000)); + } + throw new DateTimeException(unit.getName() + " not valid for CopticDate"); + } + return unit.doPlus(this, amountToAdd); + } + + //----------------------------------------------------------------------- + private CopticDate plusYears(long years) { + return plusMonths(Math.multiplyExact(years, 13)); + } + + private CopticDate plusMonths(long months) { + if (months == 0) { + return this; + } + long curEm = prolepticYear * 13L + (month - 1); + long calcEm = Math.addExact(curEm, months); + int newYear = Math.toIntExact(Math.floorDiv(calcEm, 13)); + int newMonth = (int)Math.floorMod(calcEm, 13) + 1; + return resolvePreviousValid(newYear, newMonth, day); + } + + private CopticDate plusDays(long days) { + if (days == 0) { + return this; + } + return CopticDate.ofEpochDay(Math.addExact(toEpochDay(), days)); + } + + @Override + public long periodUntil(Temporal endDateTime, TemporalUnit unit) { + if (endDateTime instanceof ChronoLocalDate == false) { + throw new DateTimeException("Unable to calculate period between objects of two different types"); + } + ChronoLocalDate end = (ChronoLocalDate) endDateTime; + if (getChrono().equals(end.getChrono()) == false) { + throw new DateTimeException("Unable to calculate period between two different chronologies"); + } + if (unit instanceof ChronoUnit) { + return LocalDate.from(this).periodUntil(end, unit); // TODO: this is wrong + } + return unit.between(this, endDateTime).getAmount(); + } + + //----------------------------------------------------------------------- + @Override + public long toEpochDay() { + long year = (long) prolepticYear; + long copticEpochDay = ((year - 1) * 365) + Math.floorDiv(year, 4) + (get(ChronoField.DAY_OF_YEAR) - 1); + return copticEpochDay - EPOCH_DAY_DIFFERENCE; + } + + @Override + public String toString() { + // getLong() reduces chances of exceptions in toString() + long yoe = getLong(YEAR_OF_ERA); + long moy = getLong(MONTH_OF_YEAR); + long dom = getLong(DAY_OF_MONTH); + StringBuilder buf = new StringBuilder(30); + buf.append(getChrono().toString()) + .append(" ") + .append(getEra()) + .append(" ") + .append(yoe) + .append(moy < 10 ? "-0" : "-").append(moy) + .append(dom < 10 ? "-0" : "-").append(dom); + return buf.toString(); + } +} diff --git a/jdk/test/java/time/tck/java/time/calendar/CopticEra.java b/jdk/test/java/time/tck/java/time/calendar/CopticEra.java new file mode 100644 index 00000000000..2526530ed3b --- /dev/null +++ b/jdk/test/java/time/tck/java/time/calendar/CopticEra.java @@ -0,0 +1,210 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.calendar; + +import static java.time.temporal.ChronoField.ERA; + +import java.util.Locale; + +import java.time.DateTimeException; +import java.time.temporal.ChronoField; +import java.time.temporal.Queries; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalField; +import java.time.temporal.TemporalQuery; +import java.time.temporal.ValueRange; +import java.time.temporal.ChronoLocalDate; +import java.time.temporal.Era; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.TextStyle; + +/** + * An era in the Coptic calendar system. + *

    + * The Coptic calendar system uses the 'Era of the Martyrs'. + * The start of the Coptic epoch {@code 0001-01-01 (Coptic)} is {@code 0284-08-29 (ISO)}. + *

    + * Do not use {@code ordinal()} to obtain the numeric representation of {@code CopticEra}. + * Use {@code getValue()} instead. + * + *

    Implementation notes

    + * This is an immutable and thread-safe enum. + */ +enum CopticEra implements Era { + + /** + * The singleton instance for the era BEFORE_AM, 'Before Era of the Martyrs'. + * This has the numeric value of {@code 0}. + */ + BEFORE_AM, + /** + * The singleton instance for the era AM, 'Era of the Martyrs'. + * This has the numeric value of {@code 1}. + */ + AM; + + //----------------------------------------------------------------------- + /** + * Obtains an instance of {@code CopticEra} from an {@code int} value. + *

    + * {@code CopticEra} is an enum representing the Coptic eras of BEFORE_AM/AM. + * This factory allows the enum to be obtained from the {@code int} value. + * + * @param era the BEFORE_AM/AM value to represent, from 0 (BEFORE_AM) to 1 (AM) + * @return the era singleton, not null + * @throws DateTimeException if the value is invalid + */ + public static CopticEra of(int era) { + switch (era) { + case 0: + return BEFORE_AM; + case 1: + return AM; + default: + throw new DateTimeException("Invalid era: " + era); + } + } + + //----------------------------------------------------------------------- + /** + * Gets the numeric era {@code int} value. + *

    + * The era BEFORE_AM has the value 0, while the era AM has the value 1. + * + * @return the era value, from 0 (BEFORE_AM) to 1 (AM) + */ + public int getValue() { + return ordinal(); + } + + @Override + public CopticChrono getChrono() { + return CopticChrono.INSTANCE; + } + + // JDK8 default methods: + //----------------------------------------------------------------------- + @Override + public ChronoLocalDate date(int year, int month, int day) { + return getChrono().date(this, year, month, day); + } + + @Override + public ChronoLocalDate dateYearDay(int year, int dayOfYear) { + return getChrono().dateYearDay(this, year, dayOfYear); + } + + //----------------------------------------------------------------------- + @Override + public boolean isSupported(TemporalField field) { + if (field instanceof ChronoField) { + return field == ERA; + } + return field != null && field.doIsSupported(this); + } + + @Override + public ValueRange range(TemporalField field) { + if (field == ERA) { + return field.range(); + } else if (field instanceof ChronoField) { + throw new DateTimeException("Unsupported field: " + field.getName()); + } + return field.doRange(this); + } + + @Override + public int get(TemporalField field) { + if (field == ERA) { + return getValue(); + } + return range(field).checkValidIntValue(getLong(field), field); + } + + @Override + public long getLong(TemporalField field) { + if (field == ERA) { + return getValue(); + } else if (field instanceof ChronoField) { + throw new DateTimeException("Unsupported field: " + field.getName()); + } + return field.doGet(this); + } + + //------------------------------------------------------------------------- + @Override + public Temporal adjustInto(Temporal dateTime) { + return dateTime.with(ERA, getValue()); + } + + @SuppressWarnings("unchecked") + @Override + public R query(TemporalQuery query) { + if (query == Queries.zoneId()) { + return null; + } else if (query == Queries.chrono()) { + return (R) getChrono(); + } + return query.queryFrom(this); + } + + //----------------------------------------------------------------------- + @Override + public String getText(TextStyle style, Locale locale) { + return new DateTimeFormatterBuilder().appendText(ERA, style).toFormatter(locale).print(this); + } + +} diff --git a/jdk/test/java/time/tck/java/time/calendar/TestChronoLocalDate.java b/jdk/test/java/time/tck/java/time/calendar/TestChronoLocalDate.java new file mode 100644 index 00000000000..67175c0b06c --- /dev/null +++ b/jdk/test/java/time/tck/java/time/calendar/TestChronoLocalDate.java @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.calendar; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.List; + +import java.time.Duration; +import java.time.LocalDate; +import java.time.calendar.HijrahChrono; +import java.time.calendar.JapaneseChrono; +import java.time.calendar.MinguoChrono; +import java.time.calendar.ThaiBuddhistChrono; +import java.time.temporal.Chrono; +import java.time.temporal.ChronoLocalDate; +import java.time.temporal.ChronoUnit; +import java.time.temporal.SimplePeriod; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAccessor; +import java.time.format.DateTimeBuilder; +import java.time.temporal.TemporalAdder; +import java.time.temporal.TemporalAdjuster; +import java.time.temporal.TemporalField; +import java.time.temporal.TemporalSubtractor; +import java.time.temporal.ValueRange; +import java.time.temporal.TemporalUnit; +import java.time.temporal.ISOChrono; + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test assertions that must be true for all built-in chronologies. + */ +@Test +public class TestChronoLocalDate { + + //----------------------------------------------------------------------- + // regular data factory for names and descriptions of available calendars + //----------------------------------------------------------------------- + @DataProvider(name = "calendars") + Chrono[][] data_of_calendars() { + return new Chrono[][]{ + {HijrahChrono.INSTANCE}, + {ISOChrono.INSTANCE}, + {JapaneseChrono.INSTANCE}, + {MinguoChrono.INSTANCE}, + {ThaiBuddhistChrono.INSTANCE}}; + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badWithAdjusterChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoLocalDate date = chrono.date(refDate); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoLocalDate date2 = chrono2.date(refDate); + TemporalAdjuster adjuster = new FixedAdjuster(date2); + if (chrono != chrono2) { + try { + date.with(adjuster); + Assert.fail("WithAdjuster should have thrown a ClassCastException"); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoLocalDate result = date.with(adjuster); + assertEquals(result, date2, "WithAdjuster failed to replace date"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badPlusAdjusterChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoLocalDate date = chrono.date(refDate); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoLocalDate date2 = chrono2.date(refDate); + TemporalAdder adjuster = new FixedAdjuster(date2); + if (chrono != chrono2) { + try { + date.plus(adjuster); + Assert.fail("WithAdjuster should have thrown a ClassCastException"); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoLocalDate result = date.plus(adjuster); + assertEquals(result, date2, "WithAdjuster failed to replace date"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badMinusAdjusterChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoLocalDate date = chrono.date(refDate); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoLocalDate date2 = chrono2.date(refDate); + TemporalSubtractor adjuster = new FixedAdjuster(date2); + if (chrono != chrono2) { + try { + date.minus(adjuster); + Assert.fail("WithAdjuster should have thrown a ClassCastException"); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoLocalDate result = date.minus(adjuster); + assertEquals(result, date2, "WithAdjuster failed to replace date"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badPlusTemporalUnitChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoLocalDate date = chrono.date(refDate); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoLocalDate date2 = chrono2.date(refDate); + TemporalUnit adjuster = new FixedTemporalUnit(date2); + if (chrono != chrono2) { + try { + date.plus(1, adjuster); + Assert.fail("TemporalUnit.doAdd plus should have thrown a ClassCastException" + date.getClass() + + ", can not be cast to " + date2.getClass()); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoLocalDate result = date.plus(1, adjuster); + assertEquals(result, date2, "WithAdjuster failed to replace date"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badMinusTemporalUnitChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoLocalDate date = chrono.date(refDate); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoLocalDate date2 = chrono2.date(refDate); + TemporalUnit adjuster = new FixedTemporalUnit(date2); + if (chrono != chrono2) { + try { + date.minus(1, adjuster); + Assert.fail("TemporalUnit.doAdd minus should have thrown a ClassCastException" + date.getClass() + + ", can not be cast to " + date2.getClass()); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoLocalDate result = date.minus(1, adjuster); + assertEquals(result, date2, "WithAdjuster failed to replace date"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badTemporalFieldChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoLocalDate date = chrono.date(refDate); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoLocalDate date2 = chrono2.date(refDate); + TemporalField adjuster = new FixedTemporalField(date2); + if (chrono != chrono2) { + try { + date.with(adjuster, 1); + Assert.fail("TemporalField doSet should have thrown a ClassCastException" + date.getClass() + + ", can not be cast to " + date2.getClass()); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoLocalDate result = date.with(adjuster, 1); + assertEquals(result, date2, "TemporalField doSet failed to replace date"); + } + } + } + + //----------------------------------------------------------------------- + // isBefore, isAfter, isEqual, DATE_COMPARATOR + //----------------------------------------------------------------------- + @Test(groups={"tck"}, dataProvider="calendars") + public void test_date_comparisons(Chrono chrono) { + List> dates = new ArrayList<>(); + + ChronoLocalDate date = chrono.date(LocalDate.of(1900, 1, 1)); + + // Insert dates in order, no duplicates + dates.add(date.minus(1000, ChronoUnit.YEARS)); + dates.add(date.minus(100, ChronoUnit.YEARS)); + dates.add(date.minus(10, ChronoUnit.YEARS)); + dates.add(date.minus(1, ChronoUnit.YEARS)); + dates.add(date.minus(1, ChronoUnit.MONTHS)); + dates.add(date.minus(1, ChronoUnit.WEEKS)); + dates.add(date.minus(1, ChronoUnit.DAYS)); + dates.add(date); + dates.add(date.plus(1, ChronoUnit.DAYS)); + dates.add(date.plus(1, ChronoUnit.WEEKS)); + dates.add(date.plus(1, ChronoUnit.MONTHS)); + dates.add(date.plus(1, ChronoUnit.YEARS)); + dates.add(date.plus(10, ChronoUnit.YEARS)); + dates.add(date.plus(100, ChronoUnit.YEARS)); + dates.add(date.plus(1000, ChronoUnit.YEARS)); + + // Check these dates against the corresponding dates for every calendar + for (Chrono[] clist : data_of_calendars()) { + List> otherDates = new ArrayList<>(); + Chrono chrono2 = clist[0]; + for (ChronoLocalDate d : dates) { + otherDates.add(chrono2.date(d)); + } + + // Now compare the sequence of original dates with the sequence of converted dates + for (int i = 0; i < dates.size(); i++) { + ChronoLocalDate a = dates.get(i); + for (int j = 0; j < otherDates.size(); j++) { + ChronoLocalDate b = otherDates.get(j); + int cmp = ChronoLocalDate.DATE_COMPARATOR.compare(a, b); + if (i < j) { + assertTrue(cmp < 0, a + " compare " + b); + assertEquals(a.isBefore(b), true, a + " isBefore " + b); + assertEquals(a.isAfter(b), false, a + " isAfter " + b); + assertEquals(a.isEqual(b), false, a + " isEqual " + b); + } else if (i > j) { + assertTrue(cmp > 0, a + " compare " + b); + assertEquals(a.isBefore(b), false, a + " isBefore " + b); + assertEquals(a.isAfter(b), true, a + " isAfter " + b); + assertEquals(a.isEqual(b), false, a + " isEqual " + b); + } else { + assertTrue(cmp == 0, a + " compare " + b); + assertEquals(a.isBefore(b), false, a + " isBefore " + b); + assertEquals(a.isAfter(b), false, a + " isAfter " + b); + assertEquals(a.isEqual(b), true, a + " isEqual " + b); + } + } + } + } + } + + //----------------------------------------------------------------------- + // Test Serialization of Calendars + //----------------------------------------------------------------------- + @Test( groups={"tck"}, dataProvider="calendars") + public > void test_ChronoSerialization(C chrono) throws Exception { + LocalDate ref = LocalDate.of(1900, 1, 5); + ChronoLocalDate orginal = chrono.date(ref); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(baos); + out.writeObject(orginal); + out.close(); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream in = new ObjectInputStream(bais); + @SuppressWarnings("unchecked") + ChronoLocalDate ser = (ChronoLocalDate) in.readObject(); + assertEquals(ser, orginal, "deserialized date is wrong"); + } + + /** + * FixedAdjusted returns a fixed Temporal in all adjustments. + * Construct an adjuster with the Temporal that should be returned from adjust. + */ + static class FixedAdjuster implements TemporalAdjuster, TemporalAdder, TemporalSubtractor { + private Temporal datetime; + + FixedAdjuster(Temporal datetime) { + this.datetime = datetime; + } + + @Override + public Temporal adjustInto(Temporal ignore) { + return datetime; + } + + @Override + public Temporal addTo(Temporal ignore) { + return datetime; + } + + @Override + public Temporal subtractFrom(Temporal ignore) { + return datetime; + } + + } + + /** + * FixedTemporalUnit returns a fixed Temporal in all adjustments. + * Construct an FixedTemporalUnit with the Temporal that should be returned from doAdd. + */ + static class FixedTemporalUnit implements TemporalUnit { + private Temporal temporal; + + FixedTemporalUnit(Temporal temporal) { + this.temporal = temporal; + } + + @Override + public String getName() { + return "FixedTemporalUnit"; + } + + @Override + public Duration getDuration() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean isDurationEstimated() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean isSupported(Temporal temporal) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @SuppressWarnings("unchecked") + @Override + public R doPlus(R dateTime, long periodToAdd) { + return (R) this.temporal; + } + + @Override + public SimplePeriod between(R dateTime1, R dateTime2) { + throw new UnsupportedOperationException("Not supported yet."); + } + } + + /** + * FixedTemporalField returns a fixed Temporal in all adjustments. + * Construct an FixedTemporalField with the Temporal that should be returned from doSet. + */ + static class FixedTemporalField implements TemporalField { + private Temporal temporal; + FixedTemporalField(Temporal temporal) { + this.temporal = temporal; + } + + @Override + public String getName() { + return "FixedTemporalField"; + } + + @Override + public TemporalUnit getBaseUnit() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public TemporalUnit getRangeUnit() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public ValueRange range() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean doIsSupported(TemporalAccessor temporal) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public ValueRange doRange(TemporalAccessor temporal) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public long doGet(TemporalAccessor temporal) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @SuppressWarnings("unchecked") + @Override + public R doWith(R temporal, long newValue) { + return (R) this.temporal; + } + + @Override + public boolean resolve(DateTimeBuilder builder, long value) { + throw new UnsupportedOperationException("Not supported yet."); + } + + } +} diff --git a/jdk/test/java/time/tck/java/time/calendar/TestChronoLocalDateTime.java b/jdk/test/java/time/tck/java/time/calendar/TestChronoLocalDateTime.java new file mode 100644 index 00000000000..e514fb82e02 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/calendar/TestChronoLocalDateTime.java @@ -0,0 +1,469 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.calendar; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.List; + +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.calendar.HijrahChrono; +import java.time.calendar.JapaneseChrono; +import java.time.calendar.MinguoChrono; +import java.time.calendar.ThaiBuddhistChrono; +import java.time.temporal.Chrono; +import java.time.temporal.ChronoLocalDateTime; +import java.time.temporal.ChronoUnit; +import java.time.temporal.SimplePeriod; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAccessor; +import java.time.format.DateTimeBuilder; +import java.time.temporal.TemporalAdder; +import java.time.temporal.TemporalAdjuster; +import java.time.temporal.TemporalField; +import java.time.temporal.TemporalSubtractor; +import java.time.temporal.ValueRange; +import java.time.temporal.ISOChrono; +import java.time.temporal.TemporalUnit; + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test assertions that must be true for all built-in chronologies. + */ +@Test +public class TestChronoLocalDateTime { + //----------------------------------------------------------------------- + // regular data factory for names and descriptions of available calendars + //----------------------------------------------------------------------- + @DataProvider(name = "calendars") + Chrono[][] data_of_calendars() { + return new Chrono[][]{ + {HijrahChrono.INSTANCE}, + {ISOChrono.INSTANCE}, + {JapaneseChrono.INSTANCE}, + {MinguoChrono.INSTANCE}, + {ThaiBuddhistChrono.INSTANCE}}; + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badWithAdjusterChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoLocalDateTime cdt = chrono.date(refDate).atTime(LocalTime.NOON); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoLocalDateTime cdt2 = chrono2.date(refDate).atTime(LocalTime.NOON); + TemporalAdjuster adjuster = new FixedAdjuster(cdt2); + if (chrono != chrono2) { + try { + ChronoLocalDateTime notreached = cdt.with(adjuster); + Assert.fail("WithAdjuster should have thrown a ClassCastException, " + + "required: " + cdt + ", supplied: " + cdt2); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoLocalDateTime result = cdt.with(adjuster); + assertEquals(result, cdt2, "WithAdjuster failed to replace date"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badPlusAdjusterChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoLocalDateTime cdt = chrono.date(refDate).atTime(LocalTime.NOON); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoLocalDateTime cdt2 = chrono2.date(refDate).atTime(LocalTime.NOON); + TemporalAdder adjuster = new FixedAdjuster(cdt2); + if (chrono != chrono2) { + try { + ChronoLocalDateTime notreached = cdt.plus(adjuster); + Assert.fail("WithAdjuster should have thrown a ClassCastException, " + + "required: " + cdt + ", supplied: " + cdt2); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoLocalDateTime result = cdt.plus(adjuster); + assertEquals(result, cdt2, "WithAdjuster failed to replace date time"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badMinusAdjusterChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoLocalDateTime cdt = chrono.date(refDate).atTime(LocalTime.NOON); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoLocalDateTime cdt2 = chrono2.date(refDate).atTime(LocalTime.NOON); + TemporalSubtractor adjuster = new FixedAdjuster(cdt2); + if (chrono != chrono2) { + try { + ChronoLocalDateTime notreached = cdt.minus(adjuster); + Assert.fail("WithAdjuster should have thrown a ClassCastException, " + + "required: " + cdt + ", supplied: " + cdt2); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoLocalDateTime result = cdt.minus(adjuster); + assertEquals(result, cdt2, "WithAdjuster failed to replace date"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badPlusTemporalUnitChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoLocalDateTime cdt = chrono.date(refDate).atTime(LocalTime.NOON); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoLocalDateTime cdt2 = chrono2.date(refDate).atTime(LocalTime.NOON); + TemporalUnit adjuster = new FixedTemporalUnit(cdt2); + if (chrono != chrono2) { + try { + ChronoLocalDateTime notreached = cdt.plus(1, adjuster); + Assert.fail("TemporalUnit.doAdd plus should have thrown a ClassCastException" + cdt + + ", can not be cast to " + cdt2); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoLocalDateTime result = cdt.plus(1, adjuster); + assertEquals(result, cdt2, "WithAdjuster failed to replace date"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badMinusTemporalUnitChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoLocalDateTime cdt = chrono.date(refDate).atTime(LocalTime.NOON); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoLocalDateTime cdt2 = chrono2.date(refDate).atTime(LocalTime.NOON); + TemporalUnit adjuster = new FixedTemporalUnit(cdt2); + if (chrono != chrono2) { + try { + ChronoLocalDateTime notreached = cdt.minus(1, adjuster); + Assert.fail("TemporalUnit.doAdd minus should have thrown a ClassCastException" + cdt.getClass() + + ", can not be cast to " + cdt2.getClass()); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoLocalDateTime result = cdt.minus(1, adjuster); + assertEquals(result, cdt2, "WithAdjuster failed to replace date"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badTemporalFieldChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoLocalDateTime cdt = chrono.date(refDate).atTime(LocalTime.NOON); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoLocalDateTime cdt2 = chrono2.date(refDate).atTime(LocalTime.NOON); + TemporalField adjuster = new FixedTemporalField(cdt2); + if (chrono != chrono2) { + try { + ChronoLocalDateTime notreached = cdt.with(adjuster, 1); + Assert.fail("TemporalField doSet should have thrown a ClassCastException" + cdt.getClass() + + ", can not be cast to " + cdt2.getClass()); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoLocalDateTime result = cdt.with(adjuster, 1); + assertEquals(result, cdt2, "TemporalField doSet failed to replace date"); + } + } + } + + //----------------------------------------------------------------------- + // isBefore, isAfter, isEqual + //----------------------------------------------------------------------- + @Test(groups={"tck"}, dataProvider="calendars") + public void test_datetime_comparisons(Chrono chrono) { + List> dates = new ArrayList<>(); + + ChronoLocalDateTime date = chrono.date(LocalDate.of(1900, 1, 1)).atTime(LocalTime.MIN); + + // Insert dates in order, no duplicates + dates.add(date.minus(100, ChronoUnit.YEARS)); + dates.add(date.minus(1, ChronoUnit.YEARS)); + dates.add(date.minus(1, ChronoUnit.MONTHS)); + dates.add(date.minus(1, ChronoUnit.WEEKS)); + dates.add(date.minus(1, ChronoUnit.DAYS)); + dates.add(date.minus(1, ChronoUnit.HOURS)); + dates.add(date.minus(1, ChronoUnit.MINUTES)); + dates.add(date.minus(1, ChronoUnit.SECONDS)); + dates.add(date.minus(1, ChronoUnit.NANOS)); + dates.add(date); + dates.add(date.plus(1, ChronoUnit.NANOS)); + dates.add(date.plus(1, ChronoUnit.SECONDS)); + dates.add(date.plus(1, ChronoUnit.MINUTES)); + dates.add(date.plus(1, ChronoUnit.HOURS)); + dates.add(date.plus(1, ChronoUnit.DAYS)); + dates.add(date.plus(1, ChronoUnit.WEEKS)); + dates.add(date.plus(1, ChronoUnit.MONTHS)); + dates.add(date.plus(1, ChronoUnit.YEARS)); + dates.add(date.plus(100, ChronoUnit.YEARS)); + + // Check these dates against the corresponding dates for every calendar + for (Chrono[] clist : data_of_calendars()) { + List> otherDates = new ArrayList<>(); + Chrono chrono2 = clist[0]; + for (ChronoLocalDateTime d : dates) { + otherDates.add(chrono2.date(d).atTime(d.getTime())); + } + + // Now compare the sequence of original dates with the sequence of converted dates + for (int i = 0; i < dates.size(); i++) { + ChronoLocalDateTime a = dates.get(i); + for (int j = 0; j < otherDates.size(); j++) { + ChronoLocalDateTime b = otherDates.get(j); + int cmp = ChronoLocalDateTime.DATE_TIME_COMPARATOR.compare(a, b); + if (i < j) { + assertTrue(cmp < 0, a + " compare " + b); + assertEquals(a.isBefore(b), true, a + " isBefore " + b); + assertEquals(a.isAfter(b), false, a + " isAfter " + b); + assertEquals(a.isEqual(b), false, a + " isEqual " + b); + } else if (i > j) { + assertTrue(cmp > 0, a + " compare " + b); + assertEquals(a.isBefore(b), false, a + " isBefore " + b); + assertEquals(a.isAfter(b), true, a + " isAfter " + b); + assertEquals(a.isEqual(b), false, a + " isEqual " + b); + } else { + assertTrue(cmp == 0, a + " compare " + b); + assertEquals(a.isBefore(b), false, a + " isBefore " + b); + assertEquals(a.isAfter(b), false, a + " isAfter " + b); + assertEquals(a.isEqual(b), true, a + " isEqual " + b); + } + } + } + } + } + + //----------------------------------------------------------------------- + // Test Serialization of ISO via chrono API + //----------------------------------------------------------------------- + @Test( groups={"tck"}, dataProvider="calendars") + public > void test_ChronoLocalDateTimeSerialization(C chrono) throws Exception { + LocalDateTime ref = LocalDate.of(2000, 1, 5).atTime(12, 1, 2, 3); + ChronoLocalDateTime orginal = chrono.date(ref).atTime(ref.getTime()); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(baos); + out.writeObject(orginal); + out.close(); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream in = new ObjectInputStream(bais); + ChronoLocalDateTime ser = (ChronoLocalDateTime) in.readObject(); + assertEquals(ser, orginal, "deserialized date is wrong"); + } + + + /** + * FixedAdjusted returns a fixed Temporal in all adjustments. + * Construct an adjuster with the Temporal that should be returned from adjust. + */ + static class FixedAdjuster implements TemporalAdjuster, TemporalAdder, TemporalSubtractor { + private Temporal datetime; + + FixedAdjuster(Temporal datetime) { + this.datetime = datetime; + } + + @Override + public Temporal adjustInto(Temporal ignore) { + return datetime; + } + + @Override + public Temporal addTo(Temporal ignore) { + return datetime; + } + + @Override + public Temporal subtractFrom(Temporal ignore) { + return datetime; + } + + } + + /** + * FixedTemporalUnit returns a fixed Temporal in all adjustments. + * Construct an FixedTemporalUnit with the Temporal that should be returned from doAdd. + */ + static class FixedTemporalUnit implements TemporalUnit { + private Temporal temporal; + + FixedTemporalUnit(Temporal temporal) { + this.temporal = temporal; + } + + @Override + public String getName() { + return "FixedTemporalUnit"; + } + + @Override + public Duration getDuration() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean isDurationEstimated() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean isSupported(Temporal temporal) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @SuppressWarnings("unchecked") + @Override + public R doPlus(R dateTime, long periodToAdd) { + return (R) this.temporal; + } + + @Override + public SimplePeriod between(R dateTime1, R dateTime2) { + throw new UnsupportedOperationException("Not supported yet."); + } + } + + /** + * FixedTemporalField returns a fixed Temporal in all adjustments. + * Construct an FixedTemporalField with the Temporal that should be returned from doSet. + */ + static class FixedTemporalField implements TemporalField { + private Temporal temporal; + FixedTemporalField(Temporal temporal) { + this.temporal = temporal; + } + + @Override + public String getName() { + return "FixedTemporalField"; + } + + @Override + public TemporalUnit getBaseUnit() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public TemporalUnit getRangeUnit() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public ValueRange range() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean doIsSupported(TemporalAccessor temporal) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public ValueRange doRange(TemporalAccessor temporal) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public long doGet(TemporalAccessor temporal) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @SuppressWarnings("unchecked") + @Override + public R doWith(R temporal, long newValue) { + return (R) this.temporal; + } + + @Override + public boolean resolve(DateTimeBuilder builder, long value) { + throw new UnsupportedOperationException("Not supported yet."); + } + + } +} diff --git a/jdk/test/java/time/tck/java/time/calendar/TestHijrahChrono.java b/jdk/test/java/time/tck/java/time/calendar/TestHijrahChrono.java new file mode 100644 index 00000000000..3157e822f09 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/calendar/TestHijrahChrono.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.calendar; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.calendar.HijrahChrono; +import java.time.temporal.ChronoLocalDate; +import java.time.temporal.Adjusters; +import java.time.temporal.Chrono; +import java.time.temporal.ISOChrono; + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test. + */ +@Test +public class TestHijrahChrono { + + //----------------------------------------------------------------------- + // Chrono.ofName("Hijrah") Lookup by name + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_chrono_byName() { + Chrono c = HijrahChrono.INSTANCE; + Chrono test = Chrono.of("Hijrah"); + Assert.assertNotNull(test, "The Hijrah calendar could not be found byName"); + Assert.assertEquals(test.getId(), "Hijrah", "ID mismatch"); + Assert.assertEquals(test.getCalendarType(), "islamicc", "Type mismatch"); + Assert.assertEquals(test, c); + } + + //----------------------------------------------------------------------- + // creation, toLocalDate() + //----------------------------------------------------------------------- + @DataProvider(name="samples") + Object[][] data_samples() { + return new Object[][] { + {HijrahChrono.INSTANCE.date(1, 1, 1), LocalDate.of(622, 7, 19)}, + {HijrahChrono.INSTANCE.date(1, 1, 2), LocalDate.of(622, 7, 20)}, + {HijrahChrono.INSTANCE.date(1, 1, 3), LocalDate.of(622, 7, 21)}, + + {HijrahChrono.INSTANCE.date(2, 1, 1), LocalDate.of(623, 7, 8)}, + {HijrahChrono.INSTANCE.date(3, 1, 1), LocalDate.of(624, 6, 27)}, + {HijrahChrono.INSTANCE.date(3, 12, 6), LocalDate.of(625, 5, 23)}, + {HijrahChrono.INSTANCE.date(4, 1, 1), LocalDate.of(625, 6, 16)}, + {HijrahChrono.INSTANCE.date(4, 7, 3), LocalDate.of(625, 12, 12)}, + {HijrahChrono.INSTANCE.date(4, 7, 4), LocalDate.of(625, 12, 13)}, + {HijrahChrono.INSTANCE.date(5, 1, 1), LocalDate.of(626, 6, 5)}, + {HijrahChrono.INSTANCE.date(1662, 3, 3), LocalDate.of(2234, 4, 3)}, + {HijrahChrono.INSTANCE.date(1728, 10, 28), LocalDate.of(2298, 12, 03)}, + {HijrahChrono.INSTANCE.date(1728, 10, 29), LocalDate.of(2298, 12, 04)}, + }; + } + + @Test(dataProvider="samples", groups={"tck"}) + public void test_toLocalDate(ChronoLocalDate hijrahDate, LocalDate iso) { + assertEquals(LocalDate.from(hijrahDate), iso); + } + + @Test(dataProvider="samples", groups={"tck"}) + public void test_fromCalendrical(ChronoLocalDate hijrahDate, LocalDate iso) { + assertEquals(HijrahChrono.INSTANCE.date(iso), hijrahDate); + } + + @DataProvider(name="badDates") + Object[][] data_badDates() { + return new Object[][] { + {1728, 0, 0}, + + {1728, -1, 1}, + {1728, 0, 1}, + {1728, 14, 1}, + {1728, 15, 1}, + + {1728, 1, -1}, + {1728, 1, 0}, + {1728, 1, 32}, + + {1728, 12, -1}, + {1728, 12, 0}, + {1728, 12, 32}, + }; + } + + @Test(dataProvider="badDates", groups={"tck"}, expectedExceptions=DateTimeException.class) + public void test_badDates(int year, int month, int dom) { + HijrahChrono.INSTANCE.date(year, month, dom); + } + + //----------------------------------------------------------------------- + // with(WithAdjuster) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_adjust1() { + ChronoLocalDate base = HijrahChrono.INSTANCE.date(1728, 10, 28); + ChronoLocalDate test = base.with(Adjusters.lastDayOfMonth()); + assertEquals(test, HijrahChrono.INSTANCE.date(1728, 10, 29)); + } + + @Test(groups={"tck"}) + public void test_adjust2() { + ChronoLocalDate base = HijrahChrono.INSTANCE.date(1728, 12, 2); + ChronoLocalDate test = base.with(Adjusters.lastDayOfMonth()); + assertEquals(test, HijrahChrono.INSTANCE.date(1728, 12, 30)); + } + + //----------------------------------------------------------------------- + // HijrahDate.with(Local*) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_adjust_toLocalDate() { + ChronoLocalDate hijrahDate = HijrahChrono.INSTANCE.date(1726, 1, 4); + ChronoLocalDate test = hijrahDate.with(LocalDate.of(2012, 7, 6)); + assertEquals(test, HijrahChrono.INSTANCE.date(1433, 8, 16)); + } + + @Test(groups={"tck"}, expectedExceptions=DateTimeException.class) + public void test_adjust_toMonth() { + ChronoLocalDate hijrahDate = HijrahChrono.INSTANCE.date(1726, 1, 4); + hijrahDate.with(Month.APRIL); + } + + //----------------------------------------------------------------------- + // LocalDate.with(HijrahDate) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_LocalDate_adjustToHijrahDate() { + ChronoLocalDate hijrahDate = HijrahChrono.INSTANCE.date(1728, 10, 29); + LocalDate test = LocalDate.MIN.with(hijrahDate); + assertEquals(test, LocalDate.of(2298, 12, 4)); + } + + @Test(groups={"tck"}) + public void test_LocalDateTime_adjustToHijrahDate() { + ChronoLocalDate hijrahDate = HijrahChrono.INSTANCE.date(1728, 10, 29); + LocalDateTime test = LocalDateTime.MIN.with(hijrahDate); + assertEquals(test, LocalDateTime.of(2298, 12, 4, 0, 0)); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @DataProvider(name="toString") + Object[][] data_toString() { + return new Object[][] { + {HijrahChrono.INSTANCE.date(1, 1, 1), "Hijrah AH 1-01-01"}, + {HijrahChrono.INSTANCE.date(1728, 10, 28), "Hijrah AH 1728-10-28"}, + {HijrahChrono.INSTANCE.date(1728, 10, 29), "Hijrah AH 1728-10-29"}, + {HijrahChrono.INSTANCE.date(1727, 12, 5), "Hijrah AH 1727-12-05"}, + {HijrahChrono.INSTANCE.date(1727, 12, 6), "Hijrah AH 1727-12-06"}, + }; + } + + @Test(dataProvider="toString", groups={"tck"}) + public void test_toString(ChronoLocalDate hijrahDate, String expected) { + assertEquals(hijrahDate.toString(), expected); + } + + //----------------------------------------------------------------------- + // equals() + //----------------------------------------------------------------------- + @Test(groups="tck") + public void test_equals_true() { + assertTrue(HijrahChrono.INSTANCE.equals(HijrahChrono.INSTANCE)); + } + + @Test(groups="tck") + public void test_equals_false() { + assertFalse(HijrahChrono.INSTANCE.equals(ISOChrono.INSTANCE)); + } + +} diff --git a/jdk/test/java/time/tck/java/time/calendar/TestJapaneseChrono.java b/jdk/test/java/time/tck/java/time/calendar/TestJapaneseChrono.java new file mode 100644 index 00000000000..614a3ff0b0a --- /dev/null +++ b/jdk/test/java/time/tck/java/time/calendar/TestJapaneseChrono.java @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.calendar; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +import java.util.List; + +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.calendar.JapaneseChrono; +import java.time.temporal.Adjusters; +import java.time.temporal.Chrono; +import java.time.temporal.ChronoLocalDate; +import java.time.temporal.Era; +import java.time.temporal.ISOChrono; + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test. + */ +@Test +public class TestJapaneseChrono { + + //----------------------------------------------------------------------- + // Chrono.ofName("Japanese") Lookup by name + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_chrono_byName() { + Chrono c = JapaneseChrono.INSTANCE; + Chrono test = Chrono.of("Japanese"); + Assert.assertNotNull(test, "The Japanese calendar could not be found byName"); + Assert.assertEquals(test.getId(), "Japanese", "ID mismatch"); + Assert.assertEquals(test.getCalendarType(), "japanese", "Type mismatch"); + Assert.assertEquals(test, c); + } + + //----------------------------------------------------------------------- + // creation, toLocalDate() + //----------------------------------------------------------------------- + @DataProvider(name="samples") + Object[][] data_samples() { + return new Object[][] { + {JapaneseChrono.INSTANCE.date(1, 1, 1), LocalDate.of(1, 1, 1)}, + {JapaneseChrono.INSTANCE.date(1, 1, 2), LocalDate.of(1, 1, 2)}, + {JapaneseChrono.INSTANCE.date(1, 1, 3), LocalDate.of(1, 1, 3)}, + + {JapaneseChrono.INSTANCE.date(2, 1, 1), LocalDate.of(2, 1, 1)}, + {JapaneseChrono.INSTANCE.date(3, 1, 1), LocalDate.of(3, 1, 1)}, + {JapaneseChrono.INSTANCE.date(3, 12, 6), LocalDate.of(3, 12, 6)}, + {JapaneseChrono.INSTANCE.date(4, 1, 1), LocalDate.of(4, 1, 1)}, + {JapaneseChrono.INSTANCE.date(4, 7, 3), LocalDate.of(4, 7, 3)}, + {JapaneseChrono.INSTANCE.date(4, 7, 4), LocalDate.of(4, 7, 4)}, + {JapaneseChrono.INSTANCE.date(5, 1, 1), LocalDate.of(5, 1, 1)}, + {JapaneseChrono.INSTANCE.date(1662, 3, 3), LocalDate.of(1662, 3, 3)}, + {JapaneseChrono.INSTANCE.date(1728, 10, 28), LocalDate.of(1728, 10, 28)}, + {JapaneseChrono.INSTANCE.date(1728, 10, 29), LocalDate.of(1728, 10, 29)}, + }; + } + + @Test(dataProvider="samples", groups={"tck"}) + public void test_toLocalDate(ChronoLocalDate jdate, LocalDate iso) { + assertEquals(LocalDate.from(jdate), iso); + } + + @Test(dataProvider="samples", groups={"tck"}) + public void test_fromCalendrical(ChronoLocalDate jdate, LocalDate iso) { + assertEquals(JapaneseChrono.INSTANCE.date(iso), jdate); + } + + @DataProvider(name="badDates") + Object[][] data_badDates() { + return new Object[][] { + {1728, 0, 0}, + + {1728, -1, 1}, + {1728, 0, 1}, + {1728, 14, 1}, + {1728, 15, 1}, + + {1728, 1, -1}, + {1728, 1, 0}, + {1728, 1, 32}, + + {1728, 12, -1}, + {1728, 12, 0}, + {1728, 12, 32}, + }; + } + + @Test(dataProvider="badDates", groups={"tck"}, expectedExceptions=DateTimeException.class) + public void test_badDates(int year, int month, int dom) { + JapaneseChrono.INSTANCE.date(year, month, dom); + } + + //----------------------------------------------------------------------- + // with(WithAdjuster) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_adjust1() { + ChronoLocalDate base = JapaneseChrono.INSTANCE.date(1728, 10, 29); + ChronoLocalDate test = base.with(Adjusters.lastDayOfMonth()); + assertEquals(test, JapaneseChrono.INSTANCE.date(1728, 10, 31)); + } + + @Test(groups={"tck"}) + public void test_adjust2() { + ChronoLocalDate base = JapaneseChrono.INSTANCE.date(1728, 12, 2); + ChronoLocalDate test = base.with(Adjusters.lastDayOfMonth()); + assertEquals(test, JapaneseChrono.INSTANCE.date(1728, 12, 31)); + } + + //----------------------------------------------------------------------- + // JapaneseDate.with(Local*) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_adjust_toLocalDate() { + ChronoLocalDate jdate = JapaneseChrono.INSTANCE.date(1726, 1, 4); + ChronoLocalDate test = jdate.with(LocalDate.of(2012, 7, 6)); + assertEquals(test, JapaneseChrono.INSTANCE.date(2012, 7, 6)); + } + + @Test(groups={"tck"}, expectedExceptions=DateTimeException.class) + public void test_adjust_toMonth() { + ChronoLocalDate jdate = JapaneseChrono.INSTANCE.date(1726, 1, 4); + jdate.with(Month.APRIL); + } + + //----------------------------------------------------------------------- + // LocalDate.with(JapaneseDate) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_LocalDate_adjustToJapaneseDate() { + ChronoLocalDate jdate = JapaneseChrono.INSTANCE.date(1728, 10, 29); + LocalDate test = LocalDate.MIN.with(jdate); + assertEquals(test, LocalDate.of(1728, 10, 29)); + } + + @Test(groups={"tck"}) + public void test_LocalDateTime_adjustToJapaneseDate() { + ChronoLocalDate jdate = JapaneseChrono.INSTANCE.date(1728, 10, 29); + LocalDateTime test = LocalDateTime.MIN.with(jdate); + assertEquals(test, LocalDateTime.of(1728, 10, 29, 0, 0)); + } + + //----------------------------------------------------------------------- + // Check Japanese Eras + //----------------------------------------------------------------------- + @DataProvider(name="japaneseEras") + Object[][] data_japanseseEras() { + return new Object[][] { + { JapaneseChrono.ERA_SEIREKI, -999, "Seireki"}, + { JapaneseChrono.ERA_MEIJI, -1, "Meiji"}, + { JapaneseChrono.ERA_TAISHO, 0, "Taisho"}, + { JapaneseChrono.ERA_SHOWA, 1, "Showa"}, + { JapaneseChrono.ERA_HEISEI, 2, "Heisei"}, + }; + } + + @Test(groups={"tck"}, dataProvider="japaneseEras") + public void test_Japanese_Eras(Era era, int eraValue, String name) { + assertEquals(era.getValue(), eraValue, "EraValue"); + assertEquals(era.toString(), name, "Era Name"); + assertEquals(era, JapaneseChrono.INSTANCE.eraOf(eraValue), "JapaneseChrono.eraOf()"); + List> eras = JapaneseChrono.INSTANCE.eras(); + assertTrue(eras.contains(era), "Era is not present in JapaneseChrono.INSTANCE.eras()"); + } + + @Test(groups="tck") + public void test_Japanese_badEras() { + int badEras[] = {-1000, -998, -997, -2, 3, 4, 1000}; + for (int badEra : badEras) { + try { + Era era = JapaneseChrono.INSTANCE.eraOf(badEra); + fail("JapaneseChrono.eraOf returned " + era + " + for invalid eraValue " + badEra); + } catch (DateTimeException ex) { + // ignore expected exception + } + } + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @DataProvider(name="toString") + Object[][] data_toString() { + return new Object[][] { + {JapaneseChrono.INSTANCE.date(0001, 1, 1), "Japanese 0001-01-01"}, + {JapaneseChrono.INSTANCE.date(1728, 10, 28), "Japanese 1728-10-28"}, + {JapaneseChrono.INSTANCE.date(1728, 10, 29), "Japanese 1728-10-29"}, + {JapaneseChrono.INSTANCE.date(1727, 12, 5), "Japanese 1727-12-05"}, + {JapaneseChrono.INSTANCE.date(1727, 12, 6), "Japanese 1727-12-06"}, + {JapaneseChrono.INSTANCE.date(1868, 9, 8), "Japanese Meiji 1-09-08"}, + {JapaneseChrono.INSTANCE.date(1912, 7, 29), "Japanese Meiji 45-07-29"}, + {JapaneseChrono.INSTANCE.date(1912, 7, 30), "Japanese Taisho 1-07-30"}, + {JapaneseChrono.INSTANCE.date(1926, 12, 24), "Japanese Taisho 15-12-24"}, + {JapaneseChrono.INSTANCE.date(1926, 12, 25), "Japanese Showa 1-12-25"}, + {JapaneseChrono.INSTANCE.date(1989, 1, 7), "Japanese Showa 64-01-07"}, + {JapaneseChrono.INSTANCE.date(1989, 1, 8), "Japanese Heisei 1-01-08"}, + {JapaneseChrono.INSTANCE.date(2012, 12, 6), "Japanese Heisei 24-12-06"}, + }; + } + + @Test(dataProvider="toString", groups={"tck"}) + public void test_toString(ChronoLocalDate jdate, String expected) { + assertEquals(jdate.toString(), expected); + } + + //----------------------------------------------------------------------- + // equals() + //----------------------------------------------------------------------- + @Test(groups="tck") + public void test_equals_true() { + assertTrue(JapaneseChrono.INSTANCE.equals(JapaneseChrono.INSTANCE)); + } + + @Test(groups="tck") + public void test_equals_false() { + assertFalse(JapaneseChrono.INSTANCE.equals(ISOChrono.INSTANCE)); + } + +} diff --git a/jdk/test/java/time/tck/java/time/calendar/TestMinguoChrono.java b/jdk/test/java/time/tck/java/time/calendar/TestMinguoChrono.java new file mode 100644 index 00000000000..118f7d89b70 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/calendar/TestMinguoChrono.java @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.calendar; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Month; +import java.time.ZoneOffset; +import java.time.calendar.MinguoChrono; +import java.time.temporal.Adjusters; +import java.time.temporal.ChronoUnit; +import java.time.temporal.ChronoZonedDateTime; +import java.time.temporal.Chrono; +import java.time.temporal.ChronoLocalDate; +import java.time.temporal.ChronoLocalDateTime; +import java.time.temporal.ISOChrono; + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test. + */ +@Test +public class TestMinguoChrono { + + //----------------------------------------------------------------------- + // Chrono.ofName("Minguo") Lookup by name + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_chrono_byName() { + Chrono c = MinguoChrono.INSTANCE; + Chrono test = Chrono.of("Minguo"); + Assert.assertNotNull(test, "The Minguo calendar could not be found byName"); + Assert.assertEquals(test.getId(), "Minguo", "ID mismatch"); + Assert.assertEquals(test.getCalendarType(), "roc", "Type mismatch"); + Assert.assertEquals(test, c); + } + + //----------------------------------------------------------------------- + // creation, toLocalDate() + //----------------------------------------------------------------------- + @DataProvider(name="samples") + Object[][] data_samples() { + return new Object[][] { + {MinguoChrono.INSTANCE.date(1, 1, 1), LocalDate.of(1912, 1, 1)}, + {MinguoChrono.INSTANCE.date(1, 1, 2), LocalDate.of(1912, 1, 2)}, + {MinguoChrono.INSTANCE.date(1, 1, 3), LocalDate.of(1912, 1, 3)}, + + {MinguoChrono.INSTANCE.date(2, 1, 1), LocalDate.of(1913, 1, 1)}, + {MinguoChrono.INSTANCE.date(3, 1, 1), LocalDate.of(1914, 1, 1)}, + {MinguoChrono.INSTANCE.date(3, 12, 6), LocalDate.of(1914, 12, 6)}, + {MinguoChrono.INSTANCE.date(4, 1, 1), LocalDate.of(1915, 1, 1)}, + {MinguoChrono.INSTANCE.date(4, 7, 3), LocalDate.of(1915, 7, 3)}, + {MinguoChrono.INSTANCE.date(4, 7, 4), LocalDate.of(1915, 7, 4)}, + {MinguoChrono.INSTANCE.date(5, 1, 1), LocalDate.of(1916, 1, 1)}, + {MinguoChrono.INSTANCE.date(100, 3, 3), LocalDate.of(2011, 3, 3)}, + {MinguoChrono.INSTANCE.date(101, 10, 28), LocalDate.of(2012, 10, 28)}, + {MinguoChrono.INSTANCE.date(101, 10, 29), LocalDate.of(2012, 10, 29)}, + }; + } + + @Test(dataProvider="samples", groups={"tck"}) + public void test_toLocalDate(ChronoLocalDate minguo, LocalDate iso) { + assertEquals(LocalDate.from(minguo), iso); + } + + @Test(dataProvider="samples", groups={"tck"}) + public void test_fromCalendrical(ChronoLocalDate minguo, LocalDate iso) { + assertEquals(MinguoChrono.INSTANCE.date(iso), minguo); + } + + @SuppressWarnings("unused") + @Test(dataProvider="samples", groups={"implementation"}) + public void test_MinguoDate(ChronoLocalDate minguoDate, LocalDate iso) { + ChronoLocalDate hd = minguoDate; + ChronoLocalDateTime hdt = hd.atTime(LocalTime.NOON); + ZoneOffset zo = ZoneOffset.ofHours(1); + ChronoZonedDateTime hzdt = hdt.atZone(zo); + hdt = hdt.plus(1, ChronoUnit.YEARS); + hdt = hdt.plus(1, ChronoUnit.MONTHS); + hdt = hdt.plus(1, ChronoUnit.DAYS); + hdt = hdt.plus(1, ChronoUnit.HOURS); + hdt = hdt.plus(1, ChronoUnit.MINUTES); + hdt = hdt.plus(1, ChronoUnit.SECONDS); + hdt = hdt.plus(1, ChronoUnit.NANOS); + ChronoLocalDateTime a2 = hzdt.getDateTime(); + ChronoLocalDate a3 = a2.getDate(); + ChronoLocalDate a5 = hzdt.getDate(); + //System.out.printf(" d: %s, dt: %s; odt: %s; zodt: %s; a4: %s%n", date, hdt, hodt, hzdt, a5); + } + + @Test() + public void test_MinguoChrono() { + ChronoLocalDate h1 = MinguoChrono.ERA_ROC.date(1, 2, 3); + ChronoLocalDate h2 = h1; + ChronoLocalDateTime h3 = h2.atTime(LocalTime.NOON); + @SuppressWarnings("unused") + ChronoZonedDateTime h4 = h3.atZone(ZoneOffset.UTC); + } + + @DataProvider(name="badDates") + Object[][] data_badDates() { + return new Object[][] { + {1912, 0, 0}, + + {1912, -1, 1}, + {1912, 0, 1}, + {1912, 14, 1}, + {1912, 15, 1}, + + {1912, 1, -1}, + {1912, 1, 0}, + {1912, 1, 32}, + {1912, 2, 29}, + {1912, 2, 30}, + + {1912, 12, -1}, + {1912, 12, 0}, + {1912, 12, 32}, + }; + } + + @Test(dataProvider="badDates", groups={"tck"}, expectedExceptions=DateTimeException.class) + public void test_badDates(int year, int month, int dom) { + MinguoChrono.INSTANCE.date(year, month, dom); + } + + //----------------------------------------------------------------------- + // with(DateTimeAdjuster) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_adjust1() { + ChronoLocalDate base = MinguoChrono.INSTANCE.date(2012, 10, 29); + ChronoLocalDate test = base.with(Adjusters.lastDayOfMonth()); + assertEquals(test, MinguoChrono.INSTANCE.date(2012, 10, 31)); + } + + @Test(groups={"tck"}) + public void test_adjust2() { + ChronoLocalDate base = MinguoChrono.INSTANCE.date(1728, 12, 2); + ChronoLocalDate test = base.with(Adjusters.lastDayOfMonth()); + assertEquals(test, MinguoChrono.INSTANCE.date(1728, 12, 31)); + } + + //----------------------------------------------------------------------- + // MinguoDate.with(Local*) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_adjust_toLocalDate() { + ChronoLocalDate minguo = MinguoChrono.INSTANCE.date(99, 1, 4); + ChronoLocalDate test = minguo.with(LocalDate.of(2012, 7, 6)); + assertEquals(test, MinguoChrono.INSTANCE.date(101, 7, 6)); + } + + @Test(groups={"tck"}, expectedExceptions=DateTimeException.class) + public void test_adjust_toMonth() { + ChronoLocalDate minguo = MinguoChrono.INSTANCE.date(1726, 1, 4); + minguo.with(Month.APRIL); + } + + //----------------------------------------------------------------------- + // LocalDate.with(MinguoDate) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_LocalDate_adjustToMinguoDate() { + ChronoLocalDate minguo = MinguoChrono.INSTANCE.date(101, 10, 29); + LocalDate test = LocalDate.MIN.with(minguo); + assertEquals(test, LocalDate.of(2012, 10, 29)); + } + + @Test(groups={"tck"}) + public void test_LocalDateTime_adjustToMinguoDate() { + ChronoLocalDate minguo = MinguoChrono.INSTANCE.date(101, 10, 29); + LocalDateTime test = LocalDateTime.MIN.with(minguo); + assertEquals(test, LocalDateTime.of(2012, 10, 29, 0, 0)); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @DataProvider(name="toString") + Object[][] data_toString() { + return new Object[][] { + {MinguoChrono.INSTANCE.date(1, 1, 1), "Minguo ROC 1-01-01"}, + {MinguoChrono.INSTANCE.date(1728, 10, 28), "Minguo ROC 1728-10-28"}, + {MinguoChrono.INSTANCE.date(1728, 10, 29), "Minguo ROC 1728-10-29"}, + {MinguoChrono.INSTANCE.date(1727, 12, 5), "Minguo ROC 1727-12-05"}, + {MinguoChrono.INSTANCE.date(1727, 12, 6), "Minguo ROC 1727-12-06"}, + }; + } + + @Test(dataProvider="toString", groups={"tck"}) + public void test_toString(ChronoLocalDate minguo, String expected) { + assertEquals(minguo.toString(), expected); + } + + //----------------------------------------------------------------------- + // equals() + //----------------------------------------------------------------------- + @Test(groups="tck") + public void test_equals_true() { + assertTrue(MinguoChrono.INSTANCE.equals(MinguoChrono.INSTANCE)); + } + + @Test(groups="tck") + public void test_equals_false() { + assertFalse(MinguoChrono.INSTANCE.equals(ISOChrono.INSTANCE)); + } + +} diff --git a/jdk/test/java/time/tck/java/time/calendar/TestServiceLoader.java b/jdk/test/java/time/tck/java/time/calendar/TestServiceLoader.java new file mode 100644 index 00000000000..b7381b77c84 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/calendar/TestServiceLoader.java @@ -0,0 +1,106 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2011-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.calendar; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.fail; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.ServiceLoader; +import java.time.LocalDate; +import java.time.temporal.Chrono; +import java.time.temporal.ChronoLocalDate; +import java.time.temporal.ISOChrono; +import java.time.DateTimeException; + +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +/** + * Tests that a custom Chronology is available via the ServiceLoader. + * The CopticChrono is configured via META-INF/services/java.time.temporal.Chrono. + */ +@Test +public class TestServiceLoader { + + @Test(groups={"tck"}) + public void test_CopticServiceLoader() { + Chrono chrono = Chrono.of("Coptic"); + ChronoLocalDate copticDate = chrono.date(1729, 4, 27); + LocalDate ld = LocalDate.from(copticDate); + assertEquals(ld, LocalDate.of(2013, 1, 5), "CopticDate does not match LocalDate"); + } + + @Test(groups="implementation") + public void test_copticServiceLoader() { + Map chronos = new HashMap<>(); + ServiceLoader loader = ServiceLoader.load(Chrono.class, null); + for (Chrono chrono : loader) { + chronos.put(chrono.getId(), chrono); + } + assertNotNull(chronos.get("Coptic"), "CopticChrono not found"); + } + +} diff --git a/jdk/test/java/time/tck/java/time/calendar/TestThaiBuddhistChrono.java b/jdk/test/java/time/tck/java/time/calendar/TestThaiBuddhistChrono.java new file mode 100644 index 00000000000..4d20f570be8 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/calendar/TestThaiBuddhistChrono.java @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.calendar; + +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.DAY_OF_YEAR; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.YEAR; +import static java.time.temporal.ChronoField.YEAR_OF_ERA; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.calendar.ThaiBuddhistChrono; +import java.time.temporal.Chrono; +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoLocalDate; +import java.time.temporal.Adjusters; +import java.time.temporal.ValueRange; +import java.time.temporal.ISOChrono; + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test. + */ +@Test +public class TestThaiBuddhistChrono { + + private static final int YDIFF = 543; + + //----------------------------------------------------------------------- + // Chrono.ofName("ThaiBuddhist") Lookup by name + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_chrono_byName() { + Chrono c = ThaiBuddhistChrono.INSTANCE; + Chrono test = Chrono.of("ThaiBuddhist"); + Assert.assertNotNull(test, "The ThaiBuddhist calendar could not be found byName"); + Assert.assertEquals(test.getId(), "ThaiBuddhist", "ID mismatch"); + Assert.assertEquals(test.getCalendarType(), "buddhist", "Type mismatch"); + Assert.assertEquals(test, c); + } + + //----------------------------------------------------------------------- + // creation, toLocalDate() + //----------------------------------------------------------------------- + @DataProvider(name="samples") + Object[][] data_samples() { + return new Object[][] { + {ThaiBuddhistChrono.INSTANCE.date(1 + YDIFF, 1, 1), LocalDate.of(1, 1, 1)}, + {ThaiBuddhistChrono.INSTANCE.date(1 + YDIFF, 1, 2), LocalDate.of(1, 1, 2)}, + {ThaiBuddhistChrono.INSTANCE.date(1 + YDIFF, 1, 3), LocalDate.of(1, 1, 3)}, + + {ThaiBuddhistChrono.INSTANCE.date(2 + YDIFF, 1, 1), LocalDate.of(2, 1, 1)}, + {ThaiBuddhistChrono.INSTANCE.date(3 + YDIFF, 1, 1), LocalDate.of(3, 1, 1)}, + {ThaiBuddhistChrono.INSTANCE.date(3 + YDIFF, 12, 6), LocalDate.of(3, 12, 6)}, + {ThaiBuddhistChrono.INSTANCE.date(4 + YDIFF, 1, 1), LocalDate.of(4, 1, 1)}, + {ThaiBuddhistChrono.INSTANCE.date(4 + YDIFF, 7, 3), LocalDate.of(4, 7, 3)}, + {ThaiBuddhistChrono.INSTANCE.date(4 + YDIFF, 7, 4), LocalDate.of(4, 7, 4)}, + {ThaiBuddhistChrono.INSTANCE.date(5 + YDIFF, 1, 1), LocalDate.of(5, 1, 1)}, + {ThaiBuddhistChrono.INSTANCE.date(1662 + YDIFF, 3, 3), LocalDate.of(1662, 3, 3)}, + {ThaiBuddhistChrono.INSTANCE.date(1728 + YDIFF, 10, 28), LocalDate.of(1728, 10, 28)}, + {ThaiBuddhistChrono.INSTANCE.date(1728 + YDIFF, 10, 29), LocalDate.of(1728, 10, 29)}, + {ThaiBuddhistChrono.INSTANCE.date(2555, 8, 29), LocalDate.of(2012, 8, 29)}, + }; + } + + @Test(dataProvider="samples", groups={"tck"}) + public void test_toLocalDate(ChronoLocalDate jdate, LocalDate iso) { + assertEquals(LocalDate.from(jdate), iso); + } + + @Test(dataProvider="samples", groups={"tck"}) + public void test_fromCalendrical(ChronoLocalDate jdate, LocalDate iso) { + assertEquals(ThaiBuddhistChrono.INSTANCE.date(iso), jdate); + } + + @DataProvider(name="badDates") + Object[][] data_badDates() { + return new Object[][] { + {1728, 0, 0}, + + {1728, -1, 1}, + {1728, 0, 1}, + {1728, 14, 1}, + {1728, 15, 1}, + + {1728, 1, -1}, + {1728, 1, 0}, + {1728, 1, 32}, + + {1728, 12, -1}, + {1728, 12, 0}, + {1728, 12, 32}, + }; + } + + @Test(dataProvider="badDates", groups={"tck"}, expectedExceptions=DateTimeException.class) + public void test_badDates(int year, int month, int dom) { + ThaiBuddhistChrono.INSTANCE.date(year, month, dom); + } + + //----------------------------------------------------------------------- + // with(WithAdjuster) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_adjust1() { + ChronoLocalDate base = ThaiBuddhistChrono.INSTANCE.date(1728, 10, 29); + ChronoLocalDate test = base.with(Adjusters.lastDayOfMonth()); + assertEquals(test, ThaiBuddhistChrono.INSTANCE.date(1728, 10, 31)); + } + + @Test(groups={"tck"}) + public void test_adjust2() { + ChronoLocalDate base = ThaiBuddhistChrono.INSTANCE.date(1728, 12, 2); + ChronoLocalDate test = base.with(Adjusters.lastDayOfMonth()); + assertEquals(test, ThaiBuddhistChrono.INSTANCE.date(1728, 12, 31)); + } + + //----------------------------------------------------------------------- + // withYear() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withYear_BE() { + ChronoLocalDate base = ThaiBuddhistChrono.INSTANCE.date(2555, 8, 29); + ChronoLocalDate test = base.with(YEAR, 2554); + assertEquals(test, ThaiBuddhistChrono.INSTANCE.date(2554, 8, 29)); + } + + @Test(groups={"tck"}) + public void test_withYear_BBE() { + ChronoLocalDate base = ThaiBuddhistChrono.INSTANCE.date(-2554, 8, 29); + ChronoLocalDate test = base.with(YEAR_OF_ERA, 2554); + assertEquals(test, ThaiBuddhistChrono.INSTANCE.date(-2553, 8, 29)); + } + + //----------------------------------------------------------------------- + // withEra() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withEra_BE() { + ChronoLocalDate base = ThaiBuddhistChrono.INSTANCE.date(2555, 8, 29); + ChronoLocalDate test = base.with(ChronoField.ERA, ThaiBuddhistChrono.ERA_BE.getValue()); + assertEquals(test, ThaiBuddhistChrono.INSTANCE.date(2555, 8, 29)); + } + + @Test(groups={"tck"}) + public void test_withEra_BBE() { + ChronoLocalDate base = ThaiBuddhistChrono.INSTANCE.date(-2554, 8, 29); + ChronoLocalDate test = base.with(ChronoField.ERA, ThaiBuddhistChrono.ERA_BEFORE_BE.getValue()); + assertEquals(test, ThaiBuddhistChrono.INSTANCE.date(-2554, 8, 29)); + } + + @Test(groups={"tck"}) + public void test_withEra_swap() { + ChronoLocalDate base = ThaiBuddhistChrono.INSTANCE.date(-2554, 8, 29); + ChronoLocalDate test = base.with(ChronoField.ERA, ThaiBuddhistChrono.ERA_BE.getValue()); + assertEquals(test, ThaiBuddhistChrono.INSTANCE.date(2555, 8, 29)); + } + + //----------------------------------------------------------------------- + // BuddhistDate.with(Local*) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_adjust_toLocalDate() { + ChronoLocalDate jdate = ThaiBuddhistChrono.INSTANCE.date(1726, 1, 4); + ChronoLocalDate test = jdate.with(LocalDate.of(2012, 7, 6)); + assertEquals(test, ThaiBuddhistChrono.INSTANCE.date(2555, 7, 6)); + } + + @Test(groups={"tck"}, expectedExceptions=DateTimeException.class) + public void test_adjust_toMonth() { + ChronoLocalDate jdate = ThaiBuddhistChrono.INSTANCE.date(1726, 1, 4); + jdate.with(Month.APRIL); + } + + //----------------------------------------------------------------------- + // LocalDate.with(BuddhistDate) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_LocalDate_adjustToBuddhistDate() { + ChronoLocalDate jdate = ThaiBuddhistChrono.INSTANCE.date(2555, 10, 29); + LocalDate test = LocalDate.MIN.with(jdate); + assertEquals(test, LocalDate.of(2012, 10, 29)); + } + + @Test(groups={"tck"}) + public void test_LocalDateTime_adjustToBuddhistDate() { + ChronoLocalDate jdate = ThaiBuddhistChrono.INSTANCE.date(2555, 10, 29); + LocalDateTime test = LocalDateTime.MIN.with(jdate); + assertEquals(test, LocalDateTime.of(2012, 10, 29, 0, 0)); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @DataProvider(name="toString") + Object[][] data_toString() { + return new Object[][] { + {ThaiBuddhistChrono.INSTANCE.date(544, 1, 1), "ThaiBuddhist BE 544-01-01"}, + {ThaiBuddhistChrono.INSTANCE.date(2271, 10, 28), "ThaiBuddhist BE 2271-10-28"}, + {ThaiBuddhistChrono.INSTANCE.date(2271, 10, 29), "ThaiBuddhist BE 2271-10-29"}, + {ThaiBuddhistChrono.INSTANCE.date(2270, 12, 5), "ThaiBuddhist BE 2270-12-05"}, + {ThaiBuddhistChrono.INSTANCE.date(2270, 12, 6), "ThaiBuddhist BE 2270-12-06"}, + }; + } + + @Test(dataProvider="toString", groups={"tck"}) + public void test_toString(ChronoLocalDate jdate, String expected) { + assertEquals(jdate.toString(), expected); + } + + //----------------------------------------------------------------------- + // chronology range(ChronoField) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_Chrono_range() { + long minYear = LocalDate.MIN.getYear() + YDIFF; + long maxYear = LocalDate.MAX.getYear() + YDIFF; + assertEquals(ThaiBuddhistChrono.INSTANCE.range(YEAR), ValueRange.of(minYear, maxYear)); + assertEquals(ThaiBuddhistChrono.INSTANCE.range(YEAR_OF_ERA), ValueRange.of(1, -minYear + 1, maxYear)); + + assertEquals(ThaiBuddhistChrono.INSTANCE.range(DAY_OF_MONTH), DAY_OF_MONTH.range()); + assertEquals(ThaiBuddhistChrono.INSTANCE.range(DAY_OF_YEAR), DAY_OF_YEAR.range()); + assertEquals(ThaiBuddhistChrono.INSTANCE.range(MONTH_OF_YEAR), MONTH_OF_YEAR.range()); + } + + //----------------------------------------------------------------------- + // equals() + //----------------------------------------------------------------------- + @Test(groups="tck") + public void test_equals_true() { + assertTrue(ThaiBuddhistChrono.INSTANCE.equals(ThaiBuddhistChrono.INSTANCE)); + } + + @Test(groups="tck") + public void test_equals_false() { + assertFalse(ThaiBuddhistChrono.INSTANCE.equals(ISOChrono.INSTANCE)); + } + +} diff --git a/jdk/test/java/time/tck/java/time/format/TCKDateTimeFormatSymbols.java b/jdk/test/java/time/tck/java/time/format/TCKDateTimeFormatSymbols.java new file mode 100644 index 00000000000..f2be900c908 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/format/TCKDateTimeFormatSymbols.java @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2011-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.format; + +import java.time.format.*; +import test.java.time.format.*; + +import static org.testng.Assert.assertEquals; + +import java.util.Arrays; +import java.util.Locale; + +import org.testng.annotations.Test; + +/** + * Test DateTimeFormatSymbols. + */ +@Test +public class TCKDateTimeFormatSymbols { + + @Test(groups={"tck"}) + public void test_getAvailableLocales() { + Locale[] locales = DateTimeFormatSymbols.getAvailableLocales(); + assertEquals(locales.length > 0, true); + assertEquals(Arrays.asList(locales).contains(Locale.US), true); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_of_Locale() { + DateTimeFormatSymbols loc1 = DateTimeFormatSymbols.of(Locale.CANADA); + assertEquals(loc1.getZeroDigit(), '0'); + assertEquals(loc1.getPositiveSign(), '+'); + assertEquals(loc1.getNegativeSign(), '-'); + assertEquals(loc1.getDecimalSeparator(), '.'); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_STANDARD() { + DateTimeFormatSymbols loc1 = DateTimeFormatSymbols.STANDARD; + assertEquals(loc1.getZeroDigit(), '0'); + assertEquals(loc1.getPositiveSign(), '+'); + assertEquals(loc1.getNegativeSign(), '-'); + assertEquals(loc1.getDecimalSeparator(), '.'); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_zeroDigit() { + DateTimeFormatSymbols base = DateTimeFormatSymbols.STANDARD; + assertEquals(base.withZeroDigit('A').getZeroDigit(), 'A'); + } + + @Test(groups={"tck"}) + public void test_positiveSign() { + DateTimeFormatSymbols base = DateTimeFormatSymbols.STANDARD; + assertEquals(base.withPositiveSign('A').getPositiveSign(), 'A'); + } + + @Test(groups={"tck"}) + public void test_negativeSign() { + DateTimeFormatSymbols base = DateTimeFormatSymbols.STANDARD; + assertEquals(base.withNegativeSign('A').getNegativeSign(), 'A'); + } + + @Test(groups={"tck"}) + public void test_decimalSeparator() { + DateTimeFormatSymbols base = DateTimeFormatSymbols.STANDARD; + assertEquals(base.withDecimalSeparator('A').getDecimalSeparator(), 'A'); + } + + //----------------------------------------------------------------------- + /* TBD: convertToDigit and convertNumberToI18N are package-private methods + @Test(groups={"tck"}) + public void test_convertToDigit_base() { + DateTimeFormatSymbols base = DateTimeFormatSymbols.STANDARD; + assertEquals(base.convertToDigit('0'), 0); + assertEquals(base.convertToDigit('1'), 1); + assertEquals(base.convertToDigit('9'), 9); + assertEquals(base.convertToDigit(' '), -1); + assertEquals(base.convertToDigit('A'), -1); + } + + @Test(groups={"tck"}) + public void test_convertToDigit_altered() { + DateTimeFormatSymbols base = DateTimeFormatSymbols.STANDARD.withZeroDigit('A'); + assertEquals(base.convertToDigit('A'), 0); + assertEquals(base.convertToDigit('B'), 1); + assertEquals(base.convertToDigit('J'), 9); + assertEquals(base.convertToDigit(' '), -1); + assertEquals(base.convertToDigit('0'), -1); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_convertNumberToI18N_base() { + DateTimeFormatSymbols base = DateTimeFormatSymbols.STANDARD; + assertEquals(base.convertNumberToI18N("134"), "134"); + } + + @Test(groups={"tck"}) + public void test_convertNumberToI18N_altered() { + DateTimeFormatSymbols base = DateTimeFormatSymbols.STANDARD.withZeroDigit('A'); + assertEquals(base.convertNumberToI18N("134"), "BDE"); + } + */ + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_equalsHashCode1() { + DateTimeFormatSymbols a = DateTimeFormatSymbols.STANDARD; + DateTimeFormatSymbols b = DateTimeFormatSymbols.STANDARD; + assertEquals(a.equals(b), true); + assertEquals(b.equals(a), true); + assertEquals(a.hashCode(), b.hashCode()); + } + + @Test(groups={"tck"}) + public void test_equalsHashCode2() { + DateTimeFormatSymbols a = DateTimeFormatSymbols.STANDARD.withZeroDigit('A'); + DateTimeFormatSymbols b = DateTimeFormatSymbols.STANDARD.withZeroDigit('A'); + assertEquals(a.equals(b), true); + assertEquals(b.equals(a), true); + assertEquals(a.hashCode(), b.hashCode()); + } + + @Test(groups={"tck"}) + public void test_equalsHashCode3() { + DateTimeFormatSymbols a = DateTimeFormatSymbols.STANDARD.withZeroDigit('A'); + DateTimeFormatSymbols b = DateTimeFormatSymbols.STANDARD.withDecimalSeparator('A'); + assertEquals(a.equals(b), false); + assertEquals(b.equals(a), false); + } + + @Test(groups={"tck"}) + public void test_equalsHashCode_bad() { + DateTimeFormatSymbols a = DateTimeFormatSymbols.STANDARD; + assertEquals(a.equals(""), false); + assertEquals(a.equals(null), false); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toString_base() { + DateTimeFormatSymbols base = DateTimeFormatSymbols.STANDARD; + assertEquals(base.toString(), "Symbols[0+-.]"); + } + + @Test(groups={"tck"}) + public void test_toString_altered() { + DateTimeFormatSymbols base = DateTimeFormatSymbols.of(Locale.US).withZeroDigit('A').withDecimalSeparator('@'); + assertEquals(base.toString(), "Symbols[A+-@]"); + } + +} diff --git a/jdk/test/java/time/tck/java/time/format/TCKDateTimeFormatter.java b/jdk/test/java/time/tck/java/time/format/TCKDateTimeFormatter.java new file mode 100644 index 00000000000..e206a1f28a7 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/format/TCKDateTimeFormatter.java @@ -0,0 +1,705 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.format; + +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.HOUR_OF_DAY; +import static java.time.temporal.ChronoField.YEAR; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +import java.io.IOException; +import java.text.Format; +import java.text.ParseException; +import java.text.ParsePosition; +import java.util.Locale; + +import java.time.DateTimeException; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.calendar.ThaiBuddhistChrono; +import java.time.format.DateTimeFormatSymbols; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.DateTimeFormatters; +import java.time.format.DateTimeParseException; +import java.time.format.DateTimePrintException; +import java.time.format.SignStyle; +import java.time.format.DateTimeBuilder; +import java.time.temporal.Chrono; +import java.time.temporal.ISOChrono; +import java.time.temporal.OffsetDate; +import java.time.temporal.OffsetDateTime; +import java.time.temporal.OffsetTime; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalQuery; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import test.java.time.format.MockIOExceptionAppendable; + +/** + * Test DateTimeFormatter. + */ +@Test(groups={"tck"}) +public class TCKDateTimeFormatter { + + private static final ZoneOffset OFFSET_PONE = ZoneOffset.ofHours(1); + private static final ZoneOffset OFFSET_PTHREE = ZoneOffset.ofHours(3); + private static final ZoneId ZONE_PARIS = ZoneId.of("Europe/Paris"); + + private static final DateTimeFormatter BASIC_FORMATTER = DateTimeFormatters.pattern("'ONE'd"); + private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatters.pattern("'ONE'yyyy MM dd"); + + private DateTimeFormatter fmt; + + @BeforeMethod + public void setUp() { + fmt = new DateTimeFormatterBuilder().appendLiteral("ONE") + .appendValue(DAY_OF_MONTH, 1, 2, SignStyle.NOT_NEGATIVE) + .toFormatter(); + } + + //----------------------------------------------------------------------- + @Test + public void test_withLocale() { + DateTimeFormatter base = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = base.withLocale(Locale.GERMAN); + assertEquals(test.getLocale(), Locale.GERMAN); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_withLocale_null() { + DateTimeFormatter base = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + base.withLocale((Locale) null); + } + + //----------------------------------------------------------------------- + @Test + public void test_withChrono() { + DateTimeFormatter test = fmt; + assertEquals(test.getChrono(), null); + test = test.withChrono(ISOChrono.INSTANCE); + assertEquals(test.getChrono(), ISOChrono.INSTANCE); + test = test.withChrono(null); + assertEquals(test.getChrono(), null); + } + + //----------------------------------------------------------------------- + @Test + public void test_withZone() { + DateTimeFormatter test = fmt; + assertEquals(test.getZone(), null); + test = test.withZone(ZoneId.of("Europe/Paris")); + assertEquals(test.getZone(), ZoneId.of("Europe/Paris")); + test = test.withZone(ZoneOffset.UTC); + assertEquals(test.getZone(), ZoneOffset.UTC); + test = test.withZone(null); + assertEquals(test.getZone(), null); + } + + //----------------------------------------------------------------------- + // print + //----------------------------------------------------------------------- + @DataProvider(name="print") + Object[][] data_print() { + LocalDate ld = LocalDate.of(2008, 6, 30); + LocalTime lt = LocalTime.of(11, 30); + LocalDateTime ldt = LocalDateTime.of(2008, 6, 30, 11, 30); + OffsetDate od = OffsetDate.of(LocalDate.of(2008, 6, 30), OFFSET_PONE); + OffsetTime ot = OffsetTime.of(LocalTime.of(11, 30), OFFSET_PONE); + OffsetDateTime odt = OffsetDateTime.of(LocalDateTime.of(2008, 6, 30, 11, 30), OFFSET_PONE); + ZonedDateTime zdt = ZonedDateTime.of(LocalDateTime.of(2008, 6, 30, 11, 30), ZONE_PARIS); + Instant instant = Instant.ofEpochSecond(3600); + return new Object[][] { + {null, null, ld, "2008::"}, + {null, null, lt, ":11:"}, + {null, null, ldt, "2008:11:"}, + {null, null, od, "2008::+01:00"}, + {null, null, ot, ":11:+01:00"}, + {null, null, odt, "2008:11:+01:00"}, + {null, null, zdt, "2008:11:+02:00Europe/Paris"}, + {null, null, instant, "::"}, + + {null, ZONE_PARIS, ld, "2008::"}, + {null, ZONE_PARIS, lt, ":11:"}, + {null, ZONE_PARIS, ldt, "2008:11:"}, + {null, ZONE_PARIS, od, "2008::+01:00"}, + {null, ZONE_PARIS, ot, ":11:+01:00"}, + {null, ZONE_PARIS, odt, "2008:12:+02:00Europe/Paris"}, + {null, ZONE_PARIS, zdt, "2008:11:+02:00Europe/Paris"}, + {null, ZONE_PARIS, instant, "1970:02:+01:00Europe/Paris"}, + + {null, OFFSET_PTHREE, ld, "2008::"}, + {null, OFFSET_PTHREE, lt, ":11:"}, + {null, OFFSET_PTHREE, ldt, "2008:11:"}, + {null, OFFSET_PTHREE, od, "2008::+01:00"}, + {null, OFFSET_PTHREE, ot, ":11:+01:00"}, + {null, OFFSET_PTHREE, odt, "2008:13:+03:00"}, + {null, OFFSET_PTHREE, zdt, "2008:12:+03:00"}, + {null, OFFSET_PTHREE, instant, "1970:04:+03:00"}, + + {ThaiBuddhistChrono.INSTANCE, null, ld, "2551::"}, + {ThaiBuddhistChrono.INSTANCE, null, lt, ":11:"}, + {ThaiBuddhistChrono.INSTANCE, null, ldt, "2551:11:"}, + {ThaiBuddhistChrono.INSTANCE, null, od, "2551::+01:00"}, + {ThaiBuddhistChrono.INSTANCE, null, ot, ":11:+01:00"}, + {ThaiBuddhistChrono.INSTANCE, null, odt, "2551:11:+01:00"}, + {ThaiBuddhistChrono.INSTANCE, null, zdt, "2551:11:+02:00Europe/Paris"}, + {ThaiBuddhistChrono.INSTANCE, null, instant, "::"}, + + {ThaiBuddhistChrono.INSTANCE, ZONE_PARIS, ld, "2551::"}, + {ThaiBuddhistChrono.INSTANCE, ZONE_PARIS, lt, ":11:"}, + {ThaiBuddhistChrono.INSTANCE, ZONE_PARIS, ldt, "2551:11:"}, + {ThaiBuddhistChrono.INSTANCE, ZONE_PARIS, od, "2551::+01:00"}, + {ThaiBuddhistChrono.INSTANCE, ZONE_PARIS, ot, ":11:+01:00"}, + {ThaiBuddhistChrono.INSTANCE, ZONE_PARIS, odt, "2551:12:+02:00Europe/Paris"}, + {ThaiBuddhistChrono.INSTANCE, ZONE_PARIS, zdt, "2551:11:+02:00Europe/Paris"}, + {ThaiBuddhistChrono.INSTANCE, ZONE_PARIS, instant, "1970:02:+01:00Europe/Paris"}, + }; + } + + @Test(dataProvider="print") + public void test_print_Temporal(Chrono overrideChrono, ZoneId overrideZone, Temporal temporal, String expected) { + DateTimeFormatter test = new DateTimeFormatterBuilder() + .optionalStart().appendValue(YEAR, 4).optionalEnd() + .appendLiteral(':').optionalStart().appendValue(HOUR_OF_DAY, 2).optionalEnd() + .appendLiteral(':').optionalStart().appendOffsetId().optionalStart().appendZoneRegionId().optionalEnd().optionalEnd() + .toFormatter(Locale.ENGLISH) + .withChrono(overrideChrono).withZone(overrideZone); + String result = test.print(temporal); + assertEquals(result, expected); + } + + @Test + public void test_print_Temporal_simple() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + String result = test.print(LocalDate.of(2008, 6, 30)); + assertEquals(result, "ONE30"); + } + + @Test(expectedExceptions=DateTimeException.class) + public void test_print_Temporal_noSuchField() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + test.print(LocalTime.of(11, 30)); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_print_Temporal_null() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + test.print((TemporalAccessor) null); + } + + //----------------------------------------------------------------------- + @Test + public void test_print_TemporalAppendable() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + StringBuilder buf = new StringBuilder(); + test.printTo(LocalDate.of(2008, 6, 30), buf); + assertEquals(buf.toString(), "ONE30"); + } + + @Test(expectedExceptions=DateTimeException.class) + public void test_print_TemporalAppendable_noSuchField() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + StringBuilder buf = new StringBuilder(); + test.printTo(LocalTime.of(11, 30), buf); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_print_TemporalAppendable_nullTemporal() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + StringBuilder buf = new StringBuilder(); + test.printTo((TemporalAccessor) null, buf); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_print_TemporalAppendable_nullAppendable() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + test.printTo(LocalDate.of(2008, 6, 30), (Appendable) null); + } + + @Test(expectedExceptions=IOException.class) // IOException + public void test_print_TemporalAppendable_ioError() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + try { + test.printTo(LocalDate.of(2008, 6, 30), new MockIOExceptionAppendable()); + } catch (DateTimePrintException ex) { + assertEquals(ex.getCause() instanceof IOException, true); + ex.rethrowIOException(); + } + } + + //----------------------------------------------------------------------- + // parse(Query) + //----------------------------------------------------------------------- + @Test + public void test_parse_Query_String() throws Exception { + LocalDate result = DATE_FORMATTER.parse("ONE2012 07 27", LocalDate::from); + assertEquals(result, LocalDate.of(2012, 7, 27)); + } + + @Test + public void test_parse_Query_CharSequence() throws Exception { + LocalDate result = DATE_FORMATTER.parse(new StringBuilder("ONE2012 07 27"), LocalDate::from); + assertEquals(result, LocalDate.of(2012, 7, 27)); + } + + @Test(expectedExceptions=DateTimeParseException.class) + public void test_parse_Query_String_parseError() throws Exception { + try { + DATE_FORMATTER.parse("ONE2012 07 XX", LocalDate::from); + } catch (DateTimeParseException ex) { + assertEquals(ex.getMessage().contains("could not be parsed"), true); + assertEquals(ex.getMessage().contains("ONE2012 07 XX"), true); + assertEquals(ex.getParsedString(), "ONE2012 07 XX"); + assertEquals(ex.getErrorIndex(), 11); + throw ex; + } + } + + @Test(expectedExceptions=DateTimeParseException.class) + public void test_parse_Query_String_parseErrorLongText() throws Exception { + try { + DATE_FORMATTER.parse("ONEXXX67890123456789012345678901234567890123456789012345678901234567890123456789", LocalDate::from); + } catch (DateTimeParseException ex) { + assertEquals(ex.getMessage().contains("could not be parsed"), true); + assertEquals(ex.getMessage().contains("ONEXXX6789012345678901234567890123456789012345678901234567890123..."), true); + assertEquals(ex.getParsedString(), "ONEXXX67890123456789012345678901234567890123456789012345678901234567890123456789"); + assertEquals(ex.getErrorIndex(), 3); + throw ex; + } + } + + @Test(expectedExceptions=DateTimeParseException.class) + public void test_parse_Query_String_parseIncomplete() throws Exception { + try { + DATE_FORMATTER.parse("ONE2012 07 27SomethingElse", LocalDate::from); + } catch (DateTimeParseException ex) { + assertEquals(ex.getMessage().contains("could not be parsed"), true); + assertEquals(ex.getMessage().contains("ONE2012 07 27SomethingElse"), true); + assertEquals(ex.getParsedString(), "ONE2012 07 27SomethingElse"); + assertEquals(ex.getErrorIndex(), 13); + throw ex; + } + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_parse_Query_String_nullText() throws Exception { + DATE_FORMATTER.parse((String) null, LocalDate::from); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_parse_Query_String_nullRule() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + test.parse("30", (TemporalQuery) null); + } + + //----------------------------------------------------------------------- + @Test + public void test_parseBest_firstOption() throws Exception { + DateTimeFormatter test = DateTimeFormatters.pattern("yyyy-MM-dd[ZZZ]"); + TemporalAccessor result = test.parseBest("2011-06-30+03:00", OffsetDate::from, LocalDate::from); + assertEquals(result, OffsetDate.of(LocalDate.of(2011, 6, 30), ZoneOffset.ofHours(3))); + } + + @Test + public void test_parseBest_secondOption() throws Exception { + DateTimeFormatter test = DateTimeFormatters.pattern("yyyy-MM-dd[ZZZ]"); + TemporalAccessor result = test.parseBest("2011-06-30", OffsetDate::from, LocalDate::from); + assertEquals(result, LocalDate.of(2011, 6, 30)); + } + + @Test(expectedExceptions=DateTimeParseException.class) + public void test_parseBest_String_parseError() throws Exception { + DateTimeFormatter test = DateTimeFormatters.pattern("yyyy-MM-dd[ZZZ]"); + try { + test.parseBest("2011-06-XX", OffsetDate::from, LocalDate::from); + } catch (DateTimeParseException ex) { + assertEquals(ex.getMessage().contains("could not be parsed"), true); + assertEquals(ex.getMessage().contains("XX"), true); + assertEquals(ex.getParsedString(), "2011-06-XX"); + assertEquals(ex.getErrorIndex(), 8); + throw ex; + } + } + + @Test(expectedExceptions=DateTimeParseException.class) + public void test_parseBest_String_parseErrorLongText() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + try { + test.parseBest("ONEXXX67890123456789012345678901234567890123456789012345678901234567890123456789", LocalDate::from, OffsetDate::from); + } catch (DateTimeParseException ex) { + assertEquals(ex.getMessage().contains("could not be parsed"), true); + assertEquals(ex.getMessage().contains("ONEXXX6789012345678901234567890123456789012345678901234567890123..."), true); + assertEquals(ex.getParsedString(), "ONEXXX67890123456789012345678901234567890123456789012345678901234567890123456789"); + assertEquals(ex.getErrorIndex(), 3); + throw ex; + } + } + + @Test(expectedExceptions=DateTimeParseException.class) + public void test_parseBest_String_parseIncomplete() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + try { + test.parseBest("ONE30SomethingElse", LocalDate::from, OffsetDate::from); + } catch (DateTimeParseException ex) { + assertEquals(ex.getMessage().contains("could not be parsed"), true); + assertEquals(ex.getMessage().contains("ONE30SomethingElse"), true); + assertEquals(ex.getParsedString(), "ONE30SomethingElse"); + assertEquals(ex.getErrorIndex(), 5); + throw ex; + } + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_parseBest_String_nullText() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + test.parseBest((String) null, LocalDate::from, OffsetDate::from); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_parseBest_String_nullRules() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + test.parseBest("30", (TemporalQuery[]) null); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void test_parseBest_String_zeroRules() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + test.parseBest("30", new TemporalQuery[0]); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void test_parseBest_String_oneRule() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + test.parseBest("30", LocalDate::from); + } + + //----------------------------------------------------------------------- + @Test + public void test_parseToBuilder_String() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeBuilder result = test.parseToBuilder("ONE30"); + assertEquals(result.getFieldValueMap().size(), 1); + assertEquals(result.getFieldValue(DAY_OF_MONTH), 30L); + assertEquals(result.getCalendricalList().size(), 0); + } + + @Test + public void test_parseToBuilder_CharSequence() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeBuilder result = test.parseToBuilder(new StringBuilder("ONE30")); + assertEquals(result.getFieldValueMap().size(), 1); + assertEquals(result.getFieldValue(DAY_OF_MONTH), 30L); + assertEquals(result.getCalendricalList().size(), 0); + } + + @Test(expectedExceptions=DateTimeParseException.class) + public void test_parseToBuilder_String_parseError() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + try { + test.parseToBuilder("ONEXXX"); + } catch (DateTimeParseException ex) { + assertEquals(ex.getMessage().contains("ONEXXX"), true); + assertEquals(ex.getParsedString(), "ONEXXX"); + assertEquals(ex.getErrorIndex(), 3); + throw ex; + } + } + + @Test(expectedExceptions=DateTimeParseException.class) + public void test_parseToBuilder_String_parseErrorLongText() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + try { + test.parseToBuilder("ONEXXX67890123456789012345678901234567890123456789012345678901234567890123456789"); + } catch (DateTimeParseException ex) { + assertEquals(ex.getMessage().contains("ONEXXX6789012345678901234567890123456789012345678901234567890123..."), true); + assertEquals(ex.getParsedString(), "ONEXXX67890123456789012345678901234567890123456789012345678901234567890123456789"); + assertEquals(ex.getErrorIndex(), 3); + throw ex; + } + } + + @Test(expectedExceptions=DateTimeParseException.class) + public void test_parseToBuilder_String_parseIncomplete() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + try { + test.parseToBuilder("ONE30SomethingElse"); + } catch (DateTimeParseException ex) { + assertEquals(ex.getMessage().contains("ONE30SomethingElse"), true); + assertEquals(ex.getParsedString(), "ONE30SomethingElse"); + assertEquals(ex.getErrorIndex(), 5); + throw ex; + } + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_parseToBuilder_String_null() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + test.parseToBuilder((String) null); + } + + //----------------------------------------------------------------------- + @Test + public void test_parseToBuilder_StringParsePosition() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder result = test.parseToBuilder("ONE30XXX", pos); + assertEquals(pos.getIndex(), 5); + assertEquals(pos.getErrorIndex(), -1); + assertEquals(result.getFieldValueMap().size(), 1); + assertEquals(result.getFieldValueMap().get(DAY_OF_MONTH), Long.valueOf(30)); + } + + @Test + public void test_parseToBuilder_StringParsePosition_parseError() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder result = test.parseToBuilder("ONEXXX", pos); + assertEquals(pos.getIndex(), 0); // TODO: is this right? + assertEquals(pos.getErrorIndex(), 3); + assertEquals(result, null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_parseToBuilder_StringParsePosition_nullString() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + ParsePosition pos = new ParsePosition(0); + test.parseToBuilder((String) null, pos); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_parseToBuilder_StringParsePosition_nullParsePosition() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + test.parseToBuilder("ONE30", (ParsePosition) null); + } + + @Test(expectedExceptions=IndexOutOfBoundsException.class) + public void test_parseToBuilder_StringParsePosition_invalidPosition() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + ParsePosition pos = new ParsePosition(6); + test.parseToBuilder("ONE30", pos); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @Test + public void test_toFormat_format() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + Format format = test.toFormat(); + String result = format.format(LocalDate.of(2008, 6, 30)); + assertEquals(result, "ONE30"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_toFormat_format_null() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + Format format = test.toFormat(); + format.format(null); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void test_toFormat_format_notTemporal() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + Format format = test.toFormat(); + format.format("Not a Temporal"); + } + + //----------------------------------------------------------------------- + @Test + public void test_toFormat_parseObject_String() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + Format format = test.toFormat(); + DateTimeBuilder result = (DateTimeBuilder) format.parseObject("ONE30"); + assertEquals(result.getFieldValueMap().size(), 1); + assertEquals(result.getFieldValue(DAY_OF_MONTH), 30L); + } + + @Test(expectedExceptions=ParseException.class) + public void test_toFormat_parseObject_String_parseError() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + Format format = test.toFormat(); + try { + format.parseObject("ONEXXX"); + } catch (ParseException ex) { + assertEquals(ex.getMessage().contains("ONEXXX"), true); + assertEquals(ex.getErrorOffset(), 3); + throw ex; + } + } + + @Test(expectedExceptions=ParseException.class) + public void test_toFormat_parseObject_String_parseErrorLongText() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + Format format = test.toFormat(); + try { + format.parseObject("ONEXXX67890123456789012345678901234567890123456789012345678901234567890123456789"); + } catch (DateTimeParseException ex) { + assertEquals(ex.getMessage().contains("ONEXXX6789012345678901234567890123456789012345678901234567890123..."), true); + assertEquals(ex.getParsedString(), "ONEXXX67890123456789012345678901234567890123456789012345678901234567890123456789"); + assertEquals(ex.getErrorIndex(), 3); + throw ex; + } + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_toFormat_parseObject_String_null() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + Format format = test.toFormat(); + format.parseObject((String) null); + } + + //----------------------------------------------------------------------- + @Test + public void test_toFormat_parseObject_StringParsePosition() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + Format format = test.toFormat(); + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder result = (DateTimeBuilder) format.parseObject("ONE30XXX", pos); + assertEquals(pos.getIndex(), 5); + assertEquals(pos.getErrorIndex(), -1); + assertEquals(result.getFieldValueMap().size(), 1); + assertEquals(result.getFieldValue(DAY_OF_MONTH), 30L); + } + + @Test + public void test_toFormat_parseObject_StringParsePosition_parseError() throws Exception { + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + Format format = test.toFormat(); + ParsePosition pos = new ParsePosition(0); + TemporalAccessor result = (TemporalAccessor) format.parseObject("ONEXXX", pos); + assertEquals(pos.getIndex(), 0); // TODO: is this right? + assertEquals(pos.getErrorIndex(), 3); + assertEquals(result, null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_toFormat_parseObject_StringParsePosition_nullString() throws Exception { + // SimpleDateFormat has this behavior + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + Format format = test.toFormat(); + ParsePosition pos = new ParsePosition(0); + format.parseObject((String) null, pos); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_toFormat_parseObject_StringParsePosition_nullParsePosition() throws Exception { + // SimpleDateFormat has this behavior + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + Format format = test.toFormat(); + format.parseObject("ONE30", (ParsePosition) null); + } + + @Test + public void test_toFormat_parseObject_StringParsePosition_invalidPosition_tooBig() throws Exception { + // SimpleDateFormat has this behavior + DateTimeFormatter dtf = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + ParsePosition pos = new ParsePosition(6); + Format test = dtf.toFormat(); + assertNull(test.parseObject("ONE30", pos)); + assertTrue(pos.getErrorIndex() >= 0); + } + + @Test + public void test_toFormat_parseObject_StringParsePosition_invalidPosition_tooSmall() throws Exception { + // SimpleDateFormat throws StringIndexOutOfBoundException + DateTimeFormatter dtf = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + ParsePosition pos = new ParsePosition(-1); + Format test = dtf.toFormat(); + assertNull(test.parseObject("ONE30", pos)); + assertTrue(pos.getErrorIndex() >= 0); + } + + //----------------------------------------------------------------------- + @Test + public void test_toFormat_Query_format() throws Exception { + Format format = BASIC_FORMATTER.toFormat(); + String result = format.format(LocalDate.of(2008, 6, 30)); + assertEquals(result, "ONE30"); + } + + @Test + public void test_toFormat_Query_parseObject_String() throws Exception { + Format format = DATE_FORMATTER.toFormat(LocalDate::from); + LocalDate result = (LocalDate) format.parseObject("ONE2012 07 27"); + assertEquals(result, LocalDate.of(2012, 7, 27)); + } + + @Test(expectedExceptions=ParseException.class) + public void test_toFormat_parseObject_StringParsePosition_dateTimeError() throws Exception { + Format format = DATE_FORMATTER.toFormat(LocalDate::from); + format.parseObject("ONE2012 07 32"); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_toFormat_Query() throws Exception { + BASIC_FORMATTER.toFormat(null); + } + +} diff --git a/jdk/test/java/time/tck/java/time/format/TCKDateTimeFormatterBuilder.java b/jdk/test/java/time/tck/java/time/format/TCKDateTimeFormatterBuilder.java new file mode 100644 index 00000000000..1fe79b165eb --- /dev/null +++ b/jdk/test/java/time/tck/java/time/format/TCKDateTimeFormatterBuilder.java @@ -0,0 +1,856 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2009-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.format; + +import java.time.LocalDate; +import java.time.ZoneOffset; +import java.time.format.*; + +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.DAY_OF_WEEK; +import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.YEAR; +import static org.testng.Assert.assertEquals; + +import java.text.ParsePosition; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import java.time.format.DateTimeBuilder; +import java.time.temporal.Temporal; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test DateTimeFormatterBuilder. + */ +@Test +public class TCKDateTimeFormatterBuilder { + + private DateTimeFormatterBuilder builder; + + @BeforeMethod(groups={"tck"}) + public void setUp() { + builder = new DateTimeFormatterBuilder(); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toFormatter_empty() throws Exception { + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), ""); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_parseCaseSensitive() throws Exception { + builder.parseCaseSensitive(); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "ParseCaseSensitive(true)"); + } + + @Test(groups={"tck"}) + public void test_parseCaseInsensitive() throws Exception { + builder.parseCaseInsensitive(); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "ParseCaseSensitive(false)"); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_parseStrict() throws Exception { + builder.parseStrict(); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "ParseStrict(true)"); + } + + @Test(groups={"tck"}) + public void test_parseLenient() throws Exception { + builder.parseLenient(); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "ParseStrict(false)"); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_appendValue_1arg() throws Exception { + builder.appendValue(DAY_OF_MONTH); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "Value(DayOfMonth)"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_appendValue_1arg_null() throws Exception { + builder.appendValue(null); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_appendValue_2arg() throws Exception { + builder.appendValue(DAY_OF_MONTH, 3); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "Value(DayOfMonth,3)"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_appendValue_2arg_null() throws Exception { + builder.appendValue(null, 3); + } + + @Test(expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_appendValue_2arg_widthTooSmall() throws Exception { + builder.appendValue(DAY_OF_MONTH, 0); + } + + @Test(expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_appendValue_2arg_widthTooBig() throws Exception { + builder.appendValue(DAY_OF_MONTH, 20); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_appendValue_3arg() throws Exception { + builder.appendValue(DAY_OF_MONTH, 2, 3, SignStyle.NORMAL); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "Value(DayOfMonth,2,3,NORMAL)"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_appendValue_3arg_nullField() throws Exception { + builder.appendValue(null, 2, 3, SignStyle.NORMAL); + } + + @Test(expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_appendValue_3arg_minWidthTooSmall() throws Exception { + builder.appendValue(DAY_OF_MONTH, 0, 2, SignStyle.NORMAL); + } + + @Test(expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_appendValue_3arg_minWidthTooBig() throws Exception { + builder.appendValue(DAY_OF_MONTH, 20, 2, SignStyle.NORMAL); + } + + @Test(expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_appendValue_3arg_maxWidthTooSmall() throws Exception { + builder.appendValue(DAY_OF_MONTH, 2, 0, SignStyle.NORMAL); + } + + @Test(expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_appendValue_3arg_maxWidthTooBig() throws Exception { + builder.appendValue(DAY_OF_MONTH, 2, 20, SignStyle.NORMAL); + } + + @Test(expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_appendValue_3arg_maxWidthMinWidth() throws Exception { + builder.appendValue(DAY_OF_MONTH, 4, 2, SignStyle.NORMAL); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_appendValue_3arg_nullSignStyle() throws Exception { + builder.appendValue(DAY_OF_MONTH, 2, 3, null); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_appendValue_subsequent2_parse3() throws Exception { + builder.appendValue(MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL).appendValue(DAY_OF_MONTH, 2); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "Value(MonthOfYear,1,2,NORMAL)Value(DayOfMonth,2)"); + DateTimeBuilder cal = f.parseToBuilder("123", new ParsePosition(0)); + assertEquals(cal.getFieldValueMap().get(MONTH_OF_YEAR), Long.valueOf(1)); + assertEquals(cal.getFieldValueMap().get(DAY_OF_MONTH), Long.valueOf(23)); + } + + @Test(groups={"tck"}) + public void test_appendValue_subsequent2_parse4() throws Exception { + builder.appendValue(MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL).appendValue(DAY_OF_MONTH, 2); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "Value(MonthOfYear,1,2,NORMAL)Value(DayOfMonth,2)"); + DateTimeBuilder cal = f.parseToBuilder("0123", new ParsePosition(0)); + assertEquals(cal.getFieldValueMap().get(MONTH_OF_YEAR), Long.valueOf(1)); + assertEquals(cal.getFieldValueMap().get(DAY_OF_MONTH), Long.valueOf(23)); + } + + @Test(groups={"tck"}) + public void test_appendValue_subsequent2_parse5() throws Exception { + builder.appendValue(MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL).appendValue(DAY_OF_MONTH, 2).appendLiteral('4'); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "Value(MonthOfYear,1,2,NORMAL)Value(DayOfMonth,2)'4'"); + DateTimeBuilder cal = f.parseToBuilder("01234", new ParsePosition(0)); + assertEquals(cal.getFieldValueMap().get(MONTH_OF_YEAR), Long.valueOf(1)); + assertEquals(cal.getFieldValueMap().get(DAY_OF_MONTH), Long.valueOf(23)); + } + + @Test(groups={"tck"}) + public void test_appendValue_subsequent3_parse6() throws Exception { + builder + .appendValue(YEAR, 4, 10, SignStyle.EXCEEDS_PAD) + .appendValue(MONTH_OF_YEAR, 2) + .appendValue(DAY_OF_MONTH, 2); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "Value(Year,4,10,EXCEEDS_PAD)Value(MonthOfYear,2)Value(DayOfMonth,2)"); + DateTimeBuilder cal = f.parseToBuilder("20090630", new ParsePosition(0)); + assertEquals(cal.getFieldValueMap().get(YEAR), Long.valueOf(2009)); + assertEquals(cal.getFieldValueMap().get(MONTH_OF_YEAR), Long.valueOf(6)); + assertEquals(cal.getFieldValueMap().get(DAY_OF_MONTH), Long.valueOf(30)); + } + + //----------------------------------------------------------------------- + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_appendValueReduced_null() throws Exception { + builder.appendValueReduced(null, 2, 2000); + } + + @Test(groups={"tck"}) + public void test_appendValueReduced() throws Exception { + builder.appendValueReduced(YEAR, 2, 2000); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "ReducedValue(Year,2,2000)"); + DateTimeBuilder cal = f.parseToBuilder("12", new ParsePosition(0)); + assertEquals(cal.getFieldValueMap().get(YEAR), Long.valueOf(2012)); + } + + @Test(groups={"tck"}) + public void test_appendValueReduced_subsequent_parse() throws Exception { + builder.appendValue(MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL).appendValueReduced(YEAR, 2, 2000); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "Value(MonthOfYear,1,2,NORMAL)ReducedValue(Year,2,2000)"); + DateTimeBuilder cal = f.parseToBuilder("123", new ParsePosition(0)); + assertEquals(cal.getFieldValueMap().get(MONTH_OF_YEAR), Long.valueOf(1)); + assertEquals(cal.getFieldValueMap().get(YEAR), Long.valueOf(2023)); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_appendFraction_4arg() throws Exception { + builder.appendFraction(MINUTE_OF_HOUR, 1, 9, false); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "Fraction(MinuteOfHour,1,9)"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_appendFraction_4arg_nullRule() throws Exception { + builder.appendFraction(null, 1, 9, false); + } + + @Test(expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_appendFraction_4arg_invalidRuleNotFixedSet() throws Exception { + builder.appendFraction(DAY_OF_MONTH, 1, 9, false); + } + + @Test(expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_appendFraction_4arg_minTooSmall() throws Exception { + builder.appendFraction(MINUTE_OF_HOUR, -1, 9, false); + } + + @Test(expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_appendFraction_4arg_minTooBig() throws Exception { + builder.appendFraction(MINUTE_OF_HOUR, 10, 9, false); + } + + @Test(expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_appendFraction_4arg_maxTooSmall() throws Exception { + builder.appendFraction(MINUTE_OF_HOUR, 0, -1, false); + } + + @Test(expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_appendFraction_4arg_maxTooBig() throws Exception { + builder.appendFraction(MINUTE_OF_HOUR, 1, 10, false); + } + + @Test(expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_appendFraction_4arg_maxWidthMinWidth() throws Exception { + builder.appendFraction(MINUTE_OF_HOUR, 9, 3, false); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_appendText_1arg() throws Exception { + builder.appendText(MONTH_OF_YEAR); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "Text(MonthOfYear)"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_appendText_1arg_null() throws Exception { + builder.appendText(null); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_appendText_2arg() throws Exception { + builder.appendText(MONTH_OF_YEAR, TextStyle.SHORT); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "Text(MonthOfYear,SHORT)"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_appendText_2arg_nullRule() throws Exception { + builder.appendText(null, TextStyle.SHORT); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_appendText_2arg_nullStyle() throws Exception { + builder.appendText(MONTH_OF_YEAR, (TextStyle) null); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_appendTextMap() throws Exception { + Map map = new HashMap(); + map.put(1L, "JNY"); + map.put(2L, "FBY"); + map.put(3L, "MCH"); + map.put(4L, "APL"); + map.put(5L, "MAY"); + map.put(6L, "JUN"); + map.put(7L, "JLY"); + map.put(8L, "AGT"); + map.put(9L, "SPT"); + map.put(10L, "OBR"); + map.put(11L, "NVR"); + map.put(12L, "DBR"); + builder.appendText(MONTH_OF_YEAR, map); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "Text(MonthOfYear)"); // TODO: toString should be different? + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_appendTextMap_nullRule() throws Exception { + builder.appendText(null, new HashMap()); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_appendTextMap_nullStyle() throws Exception { + builder.appendText(MONTH_OF_YEAR, (Map) null); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_appendOffsetId() throws Exception { + builder.appendOffsetId(); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "Offset('Z',+HH:MM:ss)"); + } + + @DataProvider(name="offsetPatterns") + Object[][] data_offsetPatterns() { + return new Object[][] { + {"+HH", 2, 0, 0, "+02"}, + {"+HH", -2, 0, 0, "-02"}, + {"+HH", 2, 30, 0, "+02"}, + {"+HH", 2, 0, 45, "+02"}, + {"+HH", 2, 30, 45, "+02"}, + + {"+HHMM", 2, 0, 0, "+0200"}, + {"+HHMM", -2, 0, 0, "-0200"}, + {"+HHMM", 2, 30, 0, "+0230"}, + {"+HHMM", 2, 0, 45, "+0200"}, + {"+HHMM", 2, 30, 45, "+0230"}, + + {"+HH:MM", 2, 0, 0, "+02:00"}, + {"+HH:MM", -2, 0, 0, "-02:00"}, + {"+HH:MM", 2, 30, 0, "+02:30"}, + {"+HH:MM", 2, 0, 45, "+02:00"}, + {"+HH:MM", 2, 30, 45, "+02:30"}, + + {"+HHMMss", 2, 0, 0, "+0200"}, + {"+HHMMss", -2, 0, 0, "-0200"}, + {"+HHMMss", 2, 30, 0, "+0230"}, + {"+HHMMss", 2, 0, 45, "+020045"}, + {"+HHMMss", 2, 30, 45, "+023045"}, + + {"+HH:MM:ss", 2, 0, 0, "+02:00"}, + {"+HH:MM:ss", -2, 0, 0, "-02:00"}, + {"+HH:MM:ss", 2, 30, 0, "+02:30"}, + {"+HH:MM:ss", 2, 0, 45, "+02:00:45"}, + {"+HH:MM:ss", 2, 30, 45, "+02:30:45"}, + + {"+HHMMSS", 2, 0, 0, "+020000"}, + {"+HHMMSS", -2, 0, 0, "-020000"}, + {"+HHMMSS", 2, 30, 0, "+023000"}, + {"+HHMMSS", 2, 0, 45, "+020045"}, + {"+HHMMSS", 2, 30, 45, "+023045"}, + + {"+HH:MM:SS", 2, 0, 0, "+02:00:00"}, + {"+HH:MM:SS", -2, 0, 0, "-02:00:00"}, + {"+HH:MM:SS", 2, 30, 0, "+02:30:00"}, + {"+HH:MM:SS", 2, 0, 45, "+02:00:45"}, + {"+HH:MM:SS", 2, 30, 45, "+02:30:45"}, + }; + } + + @Test(dataProvider="offsetPatterns", groups={"tck"}) + public void test_appendOffset_print(String pattern, int h, int m, int s, String expected) throws Exception { + builder.appendOffset(pattern, "Z"); + DateTimeFormatter f = builder.toFormatter(); + ZoneOffset offset = ZoneOffset.ofHoursMinutesSeconds(h, m, s); + assertEquals(f.print(offset), expected); + } + + @Test(dataProvider="offsetPatterns", groups={"tck"}) + public void test_appendOffset_parse(String pattern, int h, int m, int s, String expected) throws Exception { + builder.appendOffset(pattern, "Z"); + DateTimeFormatter f = builder.toFormatter(); + ZoneOffset offset = ZoneOffset.ofHoursMinutesSeconds(h, m, s); + ZoneOffset parsed = f.parse(expected, ZoneOffset::from); + assertEquals(f.print(parsed), expected); + } + + @DataProvider(name="badOffsetPatterns") + Object[][] data_badOffsetPatterns() { + return new Object[][] { + {"HH"}, + {"HHMM"}, + {"HH:MM"}, + {"HHMMss"}, + {"HH:MM:ss"}, + {"HHMMSS"}, + {"HH:MM:SS"}, + {"+H"}, + {"+HMM"}, + {"+HHM"}, + {"+A"}, + }; + } + + @Test(dataProvider="badOffsetPatterns", expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_appendOffset_badPattern(String pattern) throws Exception { + builder.appendOffset(pattern, "Z"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_appendOffset_3arg_nullText() throws Exception { + builder.appendOffset("+HH:MM", null); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_appendOffset_3arg_nullPattern() throws Exception { + builder.appendOffset(null, "Z"); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_appendZoneId() throws Exception { + builder.appendZoneId(); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "ZoneId()"); + } + + @Test(groups={"tck"}) + public void test_appendZoneText_1arg() throws Exception { + builder.appendZoneText(TextStyle.FULL); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "ZoneText(FULL)"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_appendZoneText_1arg_nullText() throws Exception { + builder.appendZoneText(null); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_padNext_1arg() throws Exception { + builder.appendValue(MONTH_OF_YEAR).padNext(2).appendValue(DAY_OF_MONTH).appendValue(DAY_OF_WEEK); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "Value(MonthOfYear)Pad(Value(DayOfMonth),2)Value(DayOfWeek)"); + } + + @Test(expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_padNext_1arg_invalidWidth() throws Exception { + builder.padNext(0); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_padNext_2arg_dash() throws Exception { + builder.appendValue(MONTH_OF_YEAR).padNext(2, '-').appendValue(DAY_OF_MONTH).appendValue(DAY_OF_WEEK); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "Value(MonthOfYear)Pad(Value(DayOfMonth),2,'-')Value(DayOfWeek)"); + } + + @Test(expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_padNext_2arg_invalidWidth() throws Exception { + builder.padNext(0, '-'); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_padOptional() throws Exception { + builder.appendValue(MONTH_OF_YEAR).padNext(5).optionalStart().appendValue(DAY_OF_MONTH).optionalEnd().appendValue(DAY_OF_WEEK); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "Value(MonthOfYear)Pad([Value(DayOfMonth)],5)Value(DayOfWeek)"); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_optionalStart_noEnd() throws Exception { + builder.appendValue(MONTH_OF_YEAR).optionalStart().appendValue(DAY_OF_MONTH).appendValue(DAY_OF_WEEK); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "Value(MonthOfYear)[Value(DayOfMonth)Value(DayOfWeek)]"); + } + + @Test(groups={"tck"}) + public void test_optionalStart2_noEnd() throws Exception { + builder.appendValue(MONTH_OF_YEAR).optionalStart().appendValue(DAY_OF_MONTH).optionalStart().appendValue(DAY_OF_WEEK); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "Value(MonthOfYear)[Value(DayOfMonth)[Value(DayOfWeek)]]"); + } + + @Test(groups={"tck"}) + public void test_optionalStart_doubleStart() throws Exception { + builder.appendValue(MONTH_OF_YEAR).optionalStart().optionalStart().appendValue(DAY_OF_MONTH); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "Value(MonthOfYear)[[Value(DayOfMonth)]]"); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_optionalEnd() throws Exception { + builder.appendValue(MONTH_OF_YEAR).optionalStart().appendValue(DAY_OF_MONTH).optionalEnd().appendValue(DAY_OF_WEEK); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "Value(MonthOfYear)[Value(DayOfMonth)]Value(DayOfWeek)"); + } + + @Test(groups={"tck"}) + public void test_optionalEnd2() throws Exception { + builder.appendValue(MONTH_OF_YEAR).optionalStart().appendValue(DAY_OF_MONTH) + .optionalStart().appendValue(DAY_OF_WEEK).optionalEnd().appendValue(DAY_OF_MONTH).optionalEnd(); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "Value(MonthOfYear)[Value(DayOfMonth)[Value(DayOfWeek)]Value(DayOfMonth)]"); + } + + @Test(groups={"tck"}) + public void test_optionalEnd_doubleStartSingleEnd() throws Exception { + builder.appendValue(MONTH_OF_YEAR).optionalStart().optionalStart().appendValue(DAY_OF_MONTH).optionalEnd(); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "Value(MonthOfYear)[[Value(DayOfMonth)]]"); + } + + @Test(groups={"tck"}) + public void test_optionalEnd_doubleStartDoubleEnd() throws Exception { + builder.appendValue(MONTH_OF_YEAR).optionalStart().optionalStart().appendValue(DAY_OF_MONTH).optionalEnd().optionalEnd(); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "Value(MonthOfYear)[[Value(DayOfMonth)]]"); + } + + @Test(groups={"tck"}) + public void test_optionalStartEnd_immediateStartEnd() throws Exception { + builder.appendValue(MONTH_OF_YEAR).optionalStart().optionalEnd().appendValue(DAY_OF_MONTH); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), "Value(MonthOfYear)Value(DayOfMonth)"); + } + + @Test(expectedExceptions=IllegalStateException.class, groups={"tck"}) + public void test_optionalEnd_noStart() throws Exception { + builder.optionalEnd(); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @DataProvider(name="validPatterns") + Object[][] dataValid() { + return new Object[][] { + {"'a'", "'a'"}, + {"''", "''"}, + {"'!'", "'!'"}, + {"!", "'!'"}, + + {"'hello_people,][)('", "'hello_people,][)('"}, + {"'hi'", "'hi'"}, + {"'yyyy'", "'yyyy'"}, + {"''''", "''"}, + {"'o''clock'", "'o''clock'"}, + + {"G", "Value(Era)"}, + {"GG", "Value(Era,2)"}, + {"GGG", "Text(Era,SHORT)"}, + {"GGGG", "Text(Era)"}, + {"GGGGG", "Text(Era,NARROW)"}, + + {"y", "Value(Year)"}, + {"yy", "ReducedValue(Year,2,2000)"}, + {"yyy", "Value(Year,3,19,NORMAL)"}, + {"yyyy", "Value(Year,4,19,EXCEEDS_PAD)"}, + {"yyyyy", "Value(Year,5,19,EXCEEDS_PAD)"}, + +// {"Y", "Value(WeekBasedYear)"}, +// {"YY", "ReducedValue(WeekBasedYear,2,2000)"}, +// {"YYY", "Value(WeekBasedYear,3,19,NORMAL)"}, +// {"YYYY", "Value(WeekBasedYear,4,19,EXCEEDS_PAD)"}, +// {"YYYYY", "Value(WeekBasedYear,5,19,EXCEEDS_PAD)"}, + + {"M", "Value(MonthOfYear)"}, + {"MM", "Value(MonthOfYear,2)"}, + {"MMM", "Text(MonthOfYear,SHORT)"}, + {"MMMM", "Text(MonthOfYear)"}, + {"MMMMM", "Text(MonthOfYear,NARROW)"}, + + {"D", "Value(DayOfYear)"}, + {"DD", "Value(DayOfYear,2)"}, + {"DDD", "Value(DayOfYear,3)"}, + + {"d", "Value(DayOfMonth)"}, + {"dd", "Value(DayOfMonth,2)"}, + {"ddd", "Value(DayOfMonth,3)"}, + + {"F", "Value(AlignedWeekOfMonth)"}, + {"FF", "Value(AlignedWeekOfMonth,2)"}, + {"FFF", "Value(AlignedWeekOfMonth,3)"}, + + {"Q", "Value(QuarterOfYear)"}, + {"QQ", "Value(QuarterOfYear,2)"}, + {"QQQ", "Text(QuarterOfYear,SHORT)"}, + {"QQQQ", "Text(QuarterOfYear)"}, + {"QQQQQ", "Text(QuarterOfYear,NARROW)"}, + + {"E", "Value(DayOfWeek)"}, + {"EE", "Value(DayOfWeek,2)"}, + {"EEE", "Text(DayOfWeek,SHORT)"}, + {"EEEE", "Text(DayOfWeek)"}, + {"EEEEE", "Text(DayOfWeek,NARROW)"}, + + {"a", "Text(AmPmOfDay,SHORT)"}, + {"aa", "Text(AmPmOfDay,SHORT)"}, + {"aaa", "Text(AmPmOfDay,SHORT)"}, + {"aaaa", "Text(AmPmOfDay)"}, + {"aaaaa", "Text(AmPmOfDay,NARROW)"}, + + {"H", "Value(HourOfDay)"}, + {"HH", "Value(HourOfDay,2)"}, + {"HHH", "Value(HourOfDay,3)"}, + + {"K", "Value(HourOfAmPm)"}, + {"KK", "Value(HourOfAmPm,2)"}, + {"KKK", "Value(HourOfAmPm,3)"}, + + {"k", "Value(ClockHourOfDay)"}, + {"kk", "Value(ClockHourOfDay,2)"}, + {"kkk", "Value(ClockHourOfDay,3)"}, + + {"h", "Value(ClockHourOfAmPm)"}, + {"hh", "Value(ClockHourOfAmPm,2)"}, + {"hhh", "Value(ClockHourOfAmPm,3)"}, + + {"m", "Value(MinuteOfHour)"}, + {"mm", "Value(MinuteOfHour,2)"}, + {"mmm", "Value(MinuteOfHour,3)"}, + + {"s", "Value(SecondOfMinute)"}, + {"ss", "Value(SecondOfMinute,2)"}, + {"sss", "Value(SecondOfMinute,3)"}, + + {"S", "Fraction(NanoOfSecond,1,1)"}, + {"SS", "Fraction(NanoOfSecond,2,2)"}, + {"SSS", "Fraction(NanoOfSecond,3,3)"}, + {"SSSSSSSSS", "Fraction(NanoOfSecond,9,9)"}, + + {"A", "Value(MilliOfDay)"}, + {"AA", "Value(MilliOfDay,2)"}, + {"AAA", "Value(MilliOfDay,3)"}, + + {"n", "Value(NanoOfSecond)"}, + {"nn", "Value(NanoOfSecond,2)"}, + {"nnn", "Value(NanoOfSecond,3)"}, + + {"N", "Value(NanoOfDay)"}, + {"NN", "Value(NanoOfDay,2)"}, + {"NNN", "Value(NanoOfDay,3)"}, + + {"z", "ZoneText(SHORT)"}, + {"zz", "ZoneText(SHORT)"}, + {"zzz", "ZoneText(SHORT)"}, + {"zzzz", "ZoneText(FULL)"}, + {"zzzzz", "ZoneText(FULL)"}, + + {"I", "ZoneId()"}, + {"II", "ZoneId()"}, + {"III", "ZoneId()"}, + {"IIII", "ZoneId()"}, + {"IIIII", "ZoneId()"}, + + {"Z", "Offset('+0000',+HHMM)"}, // SimpleDateFormat compatible + {"ZZ", "Offset('+0000',+HHMM)"}, + {"ZZZ", "Offset('+00:00',+HH:MM)"}, + + {"X", "Offset('Z',+HH)"}, + {"XX", "Offset('Z',+HHMM)"}, + {"XXX", "Offset('Z',+HH:MM)"}, + {"XXXX", "Offset('Z',+HHMMss)"}, + {"XXXXX", "Offset('Z',+HH:MM:ss)"}, + + {"ppH", "Pad(Value(HourOfDay),2)"}, + {"pppDD", "Pad(Value(DayOfYear,2),3)"}, + + {"yyyy[-MM[-dd", "Value(Year,4,19,EXCEEDS_PAD)['-'Value(MonthOfYear,2)['-'Value(DayOfMonth,2)]]"}, + {"yyyy[-MM[-dd]]", "Value(Year,4,19,EXCEEDS_PAD)['-'Value(MonthOfYear,2)['-'Value(DayOfMonth,2)]]"}, + {"yyyy[-MM[]-dd]", "Value(Year,4,19,EXCEEDS_PAD)['-'Value(MonthOfYear,2)'-'Value(DayOfMonth,2)]"}, + + {"yyyy-MM-dd'T'HH:mm:ss.SSS", "Value(Year,4,19,EXCEEDS_PAD)'-'Value(MonthOfYear,2)'-'Value(DayOfMonth,2)" + + "'T'Value(HourOfDay,2)':'Value(MinuteOfHour,2)':'Value(SecondOfMinute,2)'.'Fraction(NanoOfSecond,3,3)"}, + + {"e", "WeekBased(e1)"}, + {"w", "WeekBased(w1)"}, + {"W", "WeekBased(W1)"}, + {"WW", "WeekBased(W2)"}, + + }; + } + + @Test(dataProvider="validPatterns", groups={"implementation"}) + public void test_appendPattern_valid(String input, String expected) throws Exception { + builder.appendPattern(input); + DateTimeFormatter f = builder.toFormatter(); + assertEquals(f.toString(), expected); + } + + //----------------------------------------------------------------------- + @DataProvider(name="invalidPatterns") + Object[][] dataInvalid() { + return new Object[][] { + {"'"}, + {"'hello"}, + {"'hel''lo"}, + {"'hello''"}, + {"{"}, + {"}"}, + {"{}"}, + {"]"}, + {"yyyy]"}, + {"yyyy]MM"}, + {"yyyy[MM]]"}, + + {"MMMMMM"}, + {"QQQQQQ"}, + {"EEEEEE"}, + {"aaaaaa"}, + {"ZZZZ"}, + {"XXXXXX"}, + + {"RO"}, + + {"p"}, + {"pp"}, + {"p:"}, + + {"f"}, + {"ff"}, + {"f:"}, + {"fy"}, + {"fa"}, + {"fM"}, + + {"ww"}, + {"ee"}, + {"WWW"}, + }; + } + + @Test(dataProvider="invalidPatterns", expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_appendPattern_invalid(String input) throws Exception { + try { + builder.appendPattern(input); + } catch (IllegalArgumentException ex) { + throw ex; + } + } + + //----------------------------------------------------------------------- + @DataProvider(name="patternPrint") + Object[][] data_patternPrint() { + return new Object[][] { + {"Q", date(2012, 2, 10), "1"}, + {"QQ", date(2012, 2, 10), "01"}, +// {"QQQ", date(2012, 2, 10), "Q1"}, // TODO: data for quarters? +// {"QQQQ", date(2012, 2, 10), "Q1"}, +// {"QQQQQ", date(2012, 2, 10), "Q1"}, + }; + } + + @Test(dataProvider="patternPrint", groups={"tck"}) + public void test_appendPattern_patternPrint(String input, Temporal temporal, String expected) throws Exception { + DateTimeFormatter f = builder.appendPattern(input).toFormatter(Locale.UK); + String test = f.print(temporal); + assertEquals(test, expected); + } + + private static Temporal date(int y, int m, int d) { + return LocalDate.of(y, m, d); + } + +} diff --git a/jdk/test/java/time/tck/java/time/format/TCKDateTimeFormatters.java b/jdk/test/java/time/tck/java/time/format/TCKDateTimeFormatters.java new file mode 100644 index 00000000000..cbb79b981b9 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/format/TCKDateTimeFormatters.java @@ -0,0 +1,1276 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.format; + +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.DAY_OF_WEEK; +import static java.time.temporal.ChronoField.DAY_OF_YEAR; +import static java.time.temporal.ChronoField.HOUR_OF_DAY; +import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.NANO_OF_SECOND; +import static java.time.temporal.ChronoField.OFFSET_SECONDS; +import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; +import static java.time.temporal.ChronoField.YEAR; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +import java.text.ParsePosition; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeBuilder; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatters; +import java.time.format.DateTimeParseException; +import java.time.format.DateTimePrintException; +import java.time.temporal.ISOFields; +import java.time.temporal.Queries; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalField; +import java.time.temporal.TemporalQuery; +import java.time.temporal.Year; +import java.time.temporal.YearMonth; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test DateTimeFormatters. + */ +@Test +public class TCKDateTimeFormatters { + + @BeforeMethod + public void setUp() { + } + + //----------------------------------------------------------------------- + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_print_nullCalendrical() { + DateTimeFormatters.isoDate().print((TemporalAccessor) null); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_pattern_String() { + DateTimeFormatter test = DateTimeFormatters.pattern("d MMM yyyy"); + assertEquals(test.toString(), "Value(DayOfMonth)' 'Text(MonthOfYear,SHORT)' 'Value(Year,4,19,EXCEEDS_PAD)"); + assertEquals(test.getLocale(), Locale.getDefault()); + } + + @Test(expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_pattern_String_invalid() { + DateTimeFormatters.pattern("p"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_pattern_String_null() { + DateTimeFormatters.pattern(null); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_pattern_StringLocale() { + DateTimeFormatter test = DateTimeFormatters.pattern("d MMM yyyy", Locale.UK); + assertEquals(test.toString(), "Value(DayOfMonth)' 'Text(MonthOfYear,SHORT)' 'Value(Year,4,19,EXCEEDS_PAD)"); + assertEquals(test.getLocale(), Locale.UK); + } + + @Test(expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_pattern_StringLocale_invalid() { + DateTimeFormatters.pattern("p", Locale.UK); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_pattern_StringLocale_nullPattern() { + DateTimeFormatters.pattern(null, Locale.UK); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_pattern_StringLocale_nullLocale() { + DateTimeFormatters.pattern("yyyy", null); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @DataProvider(name="sample_isoLocalDate") + Object[][] provider_sample_isoLocalDate() { + return new Object[][]{ + {2008, null, null, null, null, null, DateTimeException.class}, + {null, 6, null, null, null, null, DateTimeException.class}, + {null, null, 30, null, null, null, DateTimeException.class}, + {null, null, null, "+01:00", null, null, DateTimeException.class}, + {null, null, null, null, "Europe/Paris", null, DateTimeException.class}, + {2008, 6, null, null, null, null, DateTimeException.class}, + {null, 6, 30, null, null, null, DateTimeException.class}, + + {2008, 6, 30, null, null, "2008-06-30", null}, + {2008, 6, 30, "+01:00", null, "2008-06-30", null}, + {2008, 6, 30, "+01:00", "Europe/Paris", "2008-06-30", null}, + {2008, 6, 30, null, "Europe/Paris", "2008-06-30", null}, + + {123456, 6, 30, null, null, "+123456-06-30", null}, + }; + } + + @Test(dataProvider="sample_isoLocalDate", groups={"tck"}) + public void test_print_isoLocalDate( + Integer year, Integer month, Integer day, String offsetId, String zoneId, + String expected, Class expectedEx) { + TemporalAccessor test = buildAccessor(year, month, day, null, null, null, null, offsetId, zoneId); + if (expectedEx == null) { + assertEquals(DateTimeFormatters.isoLocalDate().print(test), expected); + } else { + try { + DateTimeFormatters.isoLocalDate().print(test); + fail(); + } catch (Exception ex) { + assertTrue(expectedEx.isInstance(ex)); + } + } + } + + @Test(dataProvider="sample_isoLocalDate", groups={"tck"}) + public void test_parse_isoLocalDate( + Integer year, Integer month, Integer day, String offsetId, String zoneId, + String input, Class invalid) { + if (input != null) { + DateTimeBuilder expected = createDate(year, month, day); + // offset/zone not expected to be parsed + assertParseMatch(DateTimeFormatters.isoLocalDate().parseToBuilder(input, new ParsePosition(0)), expected); + } + } + + @Test(groups={"tck"}) + public void test_parse_isoLocalDate_999999999() { + DateTimeBuilder expected = createDate(999999999, 8, 6); + assertParseMatch(DateTimeFormatters.isoLocalDate().parseToBuilder("+999999999-08-06", new ParsePosition(0)), expected); + assertEquals(LocalDate.parse("+999999999-08-06"), LocalDate.of(999999999, 8, 6)); + } + + @Test(groups={"tck"}) + public void test_parse_isoLocalDate_1000000000() { + DateTimeBuilder expected = createDate(1000000000, 8, 6); + assertParseMatch(DateTimeFormatters.isoLocalDate().parseToBuilder("+1000000000-08-06", new ParsePosition(0)), expected); + } + + @Test(expectedExceptions = DateTimeException.class, groups={"tck"}) + public void test_parse_isoLocalDate_1000000000_failedCreate() { + LocalDate.parse("+1000000000-08-06"); + } + + @Test(groups={"tck"}) + public void test_parse_isoLocalDate_M999999999() { + DateTimeBuilder expected = createDate(-999999999, 8, 6); + assertParseMatch(DateTimeFormatters.isoLocalDate().parseToBuilder("-999999999-08-06", new ParsePosition(0)), expected); + assertEquals(LocalDate.parse("-999999999-08-06"), LocalDate.of(-999999999, 8, 6)); + } + + @Test(groups={"tck"}) + public void test_parse_isoLocalDate_M1000000000() { + DateTimeBuilder expected = createDate(-1000000000, 8, 6); + assertParseMatch(DateTimeFormatters.isoLocalDate().parseToBuilder("-1000000000-08-06", new ParsePosition(0)), expected); + } + + @Test(expectedExceptions = DateTimeException.class, groups={"tck"}) + public void test_parse_isoLocalDate_M1000000000_failedCreate() { + LocalDate.parse("-1000000000-08-06"); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @DataProvider(name="sample_isoOffsetDate") + Object[][] provider_sample_isoOffsetDate() { + return new Object[][]{ + {2008, null, null, null, null, null, DateTimeException.class}, + {null, 6, null, null, null, null, DateTimeException.class}, + {null, null, 30, null, null, null, DateTimeException.class}, + {null, null, null, "+01:00", null, null, DateTimeException.class}, + {null, null, null, null, "Europe/Paris", null, DateTimeException.class}, + {2008, 6, null, null, null, null, DateTimeException.class}, + {null, 6, 30, null, null, null, DateTimeException.class}, + + {2008, 6, 30, null, null, null, DateTimeException.class}, + {2008, 6, 30, "+01:00", null, "2008-06-30+01:00", null}, + {2008, 6, 30, "+01:00", "Europe/Paris", "2008-06-30+01:00", null}, + {2008, 6, 30, null, "Europe/Paris", null, DateTimeException.class}, + + {123456, 6, 30, "+01:00", null, "+123456-06-30+01:00", null}, + }; + } + + @Test(dataProvider="sample_isoOffsetDate", groups={"tck"}) + public void test_print_isoOffsetDate( + Integer year, Integer month, Integer day, String offsetId, String zoneId, + String expected, Class expectedEx) { + TemporalAccessor test = buildAccessor(year, month, day, null, null, null, null, offsetId, zoneId); + if (expectedEx == null) { + assertEquals(DateTimeFormatters.isoOffsetDate().print(test), expected); + } else { + try { + DateTimeFormatters.isoOffsetDate().print(test); + fail(); + } catch (Exception ex) { + assertTrue(expectedEx.isInstance(ex)); + } + } + } + + @Test(dataProvider="sample_isoOffsetDate", groups={"tck"}) + public void test_parse_isoOffsetDate( + Integer year, Integer month, Integer day, String offsetId, String zoneId, + String input, Class invalid) { + if (input != null) { + DateTimeBuilder expected = createDate(year, month, day); + buildCalendrical(expected, offsetId, null); // zone not expected to be parsed + assertParseMatch(DateTimeFormatters.isoOffsetDate().parseToBuilder(input, new ParsePosition(0)), expected); + } + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @DataProvider(name="sample_isoDate") + Object[][] provider_sample_isoDate() { + return new Object[][]{ + {2008, null, null, null, null, null, DateTimeException.class}, + {null, 6, null, null, null, null, DateTimeException.class}, + {null, null, 30, null, null, null, DateTimeException.class}, + {null, null, null, "+01:00", null, null, DateTimeException.class}, + {null, null, null, null, "Europe/Paris", null, DateTimeException.class}, + {2008, 6, null, null, null, null, DateTimeException.class}, + {null, 6, 30, null, null, null, DateTimeException.class}, + + {2008, 6, 30, null, null, "2008-06-30", null}, + {2008, 6, 30, "+01:00", null, "2008-06-30+01:00", null}, + {2008, 6, 30, "+01:00", "Europe/Paris", "2008-06-30+01:00", null}, + {2008, 6, 30, null, "Europe/Paris", "2008-06-30", null}, + + {123456, 6, 30, "+01:00", "Europe/Paris", "+123456-06-30+01:00", null}, + }; + } + + @Test(dataProvider="sample_isoDate", groups={"tck"}) + public void test_print_isoDate( + Integer year, Integer month, Integer day, String offsetId, String zoneId, + String expected, Class expectedEx) { + TemporalAccessor test = buildAccessor(year, month, day, null, null, null, null, offsetId, zoneId); + if (expectedEx == null) { + assertEquals(DateTimeFormatters.isoDate().print(test), expected); + } else { + try { + DateTimeFormatters.isoDate().print(test); + fail(); + } catch (Exception ex) { + assertTrue(expectedEx.isInstance(ex)); + } + } + } + + @Test(dataProvider="sample_isoDate", groups={"tck"}) + public void test_parse_isoDate( + Integer year, Integer month, Integer day, String offsetId, String zoneId, + String input, Class invalid) { + if (input != null) { + DateTimeBuilder expected = createDate(year, month, day); + if (offsetId != null) { + expected.addFieldValue(OFFSET_SECONDS, ZoneOffset.of(offsetId).getTotalSeconds()); + } + assertParseMatch(DateTimeFormatters.isoDate().parseToBuilder(input, new ParsePosition(0)), expected); + } + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @DataProvider(name="sample_isoLocalTime") + Object[][] provider_sample_isoLocalTime() { + return new Object[][]{ + {11, null, null, null, null, null, null, DateTimeException.class}, + {null, 5, null, null, null, null, null, DateTimeException.class}, + {null, null, 30, null, null, null, null, DateTimeException.class}, + {null, null, null, 1, null, null, null, DateTimeException.class}, + {null, null, null, null, "+01:00", null, null, DateTimeException.class}, + {null, null, null, null, null, "Europe/Paris", null, DateTimeException.class}, + + {11, 5, null, null, null, null, "11:05", null}, + {11, 5, 30, null, null, null, "11:05:30", null}, + {11, 5, 30, 500000000, null, null, "11:05:30.5", null}, + {11, 5, 30, 1, null, null, "11:05:30.000000001", null}, + + {11, 5, null, null, "+01:00", null, "11:05", null}, + {11, 5, 30, null, "+01:00", null, "11:05:30", null}, + {11, 5, 30, 500000000, "+01:00", null, "11:05:30.5", null}, + {11, 5, 30, 1, "+01:00", null, "11:05:30.000000001", null}, + + {11, 5, null, null, "+01:00", "Europe/Paris", "11:05", null}, + {11, 5, 30, null, "+01:00", "Europe/Paris", "11:05:30", null}, + {11, 5, 30, 500000000, "+01:00", "Europe/Paris", "11:05:30.5", null}, + {11, 5, 30, 1, "+01:00", "Europe/Paris", "11:05:30.000000001", null}, + + {11, 5, null, null, null, "Europe/Paris", "11:05", null}, + {11, 5, 30, null, null, "Europe/Paris", "11:05:30", null}, + {11, 5, 30, 500000000, null, "Europe/Paris", "11:05:30.5", null}, + {11, 5, 30, 1, null, "Europe/Paris", "11:05:30.000000001", null}, + }; + } + + @Test(dataProvider="sample_isoLocalTime", groups={"tck"}) + public void test_print_isoLocalTime( + Integer hour, Integer min, Integer sec, Integer nano, String offsetId, String zoneId, + String expected, Class expectedEx) { + TemporalAccessor test = buildAccessor(null, null, null, hour, min, sec, nano, offsetId, zoneId); + if (expectedEx == null) { + assertEquals(DateTimeFormatters.isoLocalTime().print(test), expected); + } else { + try { + DateTimeFormatters.isoLocalTime().print(test); + fail(); + } catch (Exception ex) { + assertTrue(expectedEx.isInstance(ex)); + } + } + } + + @Test(dataProvider="sample_isoLocalTime", groups={"tck"}) + public void test_parse_isoLocalTime( + Integer hour, Integer min, Integer sec, Integer nano, String offsetId, String zoneId, + String input, Class invalid) { + if (input != null) { + DateTimeBuilder expected = createTime(hour, min, sec, nano); + // offset/zone not expected to be parsed + assertParseMatch(DateTimeFormatters.isoLocalTime().parseToBuilder(input, new ParsePosition(0)), expected); + } + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @DataProvider(name="sample_isoOffsetTime") + Object[][] provider_sample_isoOffsetTime() { + return new Object[][]{ + {11, null, null, null, null, null, null, DateTimeException.class}, + {null, 5, null, null, null, null, null, DateTimeException.class}, + {null, null, 30, null, null, null, null, DateTimeException.class}, + {null, null, null, 1, null, null, null, DateTimeException.class}, + {null, null, null, null, "+01:00", null, null, DateTimeException.class}, + {null, null, null, null, null, "Europe/Paris", null, DateTimeException.class}, + + {11, 5, null, null, null, null, null, DateTimeException.class}, + {11, 5, 30, null, null, null, null, DateTimeException.class}, + {11, 5, 30, 500000000, null, null, null, DateTimeException.class}, + {11, 5, 30, 1, null, null, null, DateTimeException.class}, + + {11, 5, null, null, "+01:00", null, "11:05+01:00", null}, + {11, 5, 30, null, "+01:00", null, "11:05:30+01:00", null}, + {11, 5, 30, 500000000, "+01:00", null, "11:05:30.5+01:00", null}, + {11, 5, 30, 1, "+01:00", null, "11:05:30.000000001+01:00", null}, + + {11, 5, null, null, "+01:00", "Europe/Paris", "11:05+01:00", null}, + {11, 5, 30, null, "+01:00", "Europe/Paris", "11:05:30+01:00", null}, + {11, 5, 30, 500000000, "+01:00", "Europe/Paris", "11:05:30.5+01:00", null}, + {11, 5, 30, 1, "+01:00", "Europe/Paris", "11:05:30.000000001+01:00", null}, + + {11, 5, null, null, null, "Europe/Paris", null, DateTimeException.class}, + {11, 5, 30, null, null, "Europe/Paris", null, DateTimeException.class}, + {11, 5, 30, 500000000, null, "Europe/Paris", null, DateTimeException.class}, + {11, 5, 30, 1, null, "Europe/Paris", null, DateTimeException.class}, + }; + } + + @Test(dataProvider="sample_isoOffsetTime", groups={"tck"}) + public void test_print_isoOffsetTime( + Integer hour, Integer min, Integer sec, Integer nano, String offsetId, String zoneId, + String expected, Class expectedEx) { + TemporalAccessor test = buildAccessor(null, null, null, hour, min, sec, nano, offsetId, zoneId); + if (expectedEx == null) { + assertEquals(DateTimeFormatters.isoOffsetTime().print(test), expected); + } else { + try { + DateTimeFormatters.isoOffsetTime().print(test); + fail(); + } catch (Exception ex) { + assertTrue(expectedEx.isInstance(ex)); + } + } + } + + @Test(dataProvider="sample_isoOffsetTime", groups={"tck"}) + public void test_parse_isoOffsetTime( + Integer hour, Integer min, Integer sec, Integer nano, String offsetId, String zoneId, + String input, Class invalid) { + if (input != null) { + DateTimeBuilder expected = createTime(hour, min, sec, nano); + buildCalendrical(expected, offsetId, null); // zoneId is not expected from parse + assertParseMatch(DateTimeFormatters.isoOffsetTime().parseToBuilder(input, new ParsePosition(0)), expected); + } + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @DataProvider(name="sample_isoTime") + Object[][] provider_sample_isoTime() { + return new Object[][]{ + {11, null, null, null, null, null, null, DateTimeException.class}, + {null, 5, null, null, null, null, null, DateTimeException.class}, + {null, null, 30, null, null, null, null, DateTimeException.class}, + {null, null, null, 1, null, null, null, DateTimeException.class}, + {null, null, null, null, "+01:00", null, null, DateTimeException.class}, + {null, null, null, null, null, "Europe/Paris", null, DateTimeException.class}, + + {11, 5, null, null, null, null, "11:05", null}, + {11, 5, 30, null, null, null, "11:05:30", null}, + {11, 5, 30, 500000000, null, null, "11:05:30.5", null}, + {11, 5, 30, 1, null, null, "11:05:30.000000001", null}, + + {11, 5, null, null, "+01:00", null, "11:05+01:00", null}, + {11, 5, 30, null, "+01:00", null, "11:05:30+01:00", null}, + {11, 5, 30, 500000000, "+01:00", null, "11:05:30.5+01:00", null}, + {11, 5, 30, 1, "+01:00", null, "11:05:30.000000001+01:00", null}, + + {11, 5, null, null, "+01:00", "Europe/Paris", "11:05+01:00", null}, + {11, 5, 30, null, "+01:00", "Europe/Paris", "11:05:30+01:00", null}, + {11, 5, 30, 500000000, "+01:00", "Europe/Paris", "11:05:30.5+01:00", null}, + {11, 5, 30, 1, "+01:00", "Europe/Paris", "11:05:30.000000001+01:00", null}, + + {11, 5, null, null, null, "Europe/Paris", "11:05", null}, + {11, 5, 30, null, null, "Europe/Paris", "11:05:30", null}, + {11, 5, 30, 500000000, null, "Europe/Paris", "11:05:30.5", null}, + {11, 5, 30, 1, null, "Europe/Paris", "11:05:30.000000001", null}, + }; + } + + @Test(dataProvider="sample_isoTime", groups={"tck"}) + public void test_print_isoTime( + Integer hour, Integer min, Integer sec, Integer nano, String offsetId, String zoneId, + String expected, Class expectedEx) { + TemporalAccessor test = buildAccessor(null, null, null, hour, min, sec, nano, offsetId, zoneId); + if (expectedEx == null) { + assertEquals(DateTimeFormatters.isoTime().print(test), expected); + } else { + try { + DateTimeFormatters.isoTime().print(test); + fail(); + } catch (Exception ex) { + assertTrue(expectedEx.isInstance(ex)); + } + } + } + + @Test(dataProvider="sample_isoTime", groups={"tck"}) + public void test_parse_isoTime( + Integer hour, Integer min, Integer sec, Integer nano, String offsetId, String zoneId, + String input, Class invalid) { + if (input != null) { + DateTimeBuilder expected = createTime(hour, min, sec, nano); + if (offsetId != null) { + expected.addFieldValue(OFFSET_SECONDS, ZoneOffset.of(offsetId).getTotalSeconds()); + } + assertParseMatch(DateTimeFormatters.isoTime().parseToBuilder(input, new ParsePosition(0)), expected); + } + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @DataProvider(name="sample_isoLocalDateTime") + Object[][] provider_sample_isoLocalDateTime() { + return new Object[][]{ + {2008, null, null, null, null, null, null, null, null, null, DateTimeException.class}, + {null, 6, null, null, null, null, null, null, null, null, DateTimeException.class}, + {null, null, 30, null, null, null, null, null, null, null, DateTimeException.class}, + {null, null, null, 11, null, null, null, null, null, null, DateTimeException.class}, + {null, null, null, null, 5, null, null, null, null, null, DateTimeException.class}, + {null, null, null, null, null, null, null, "+01:00", null, null, DateTimeException.class}, + {null, null, null, null, null, null, null, null, "Europe/Paris", null, DateTimeException.class}, + {2008, 6, 30, 11, null, null, null, null, null, null, DateTimeException.class}, + {2008, 6, 30, null, 5, null, null, null, null, null, DateTimeException.class}, + {2008, 6, null, 11, 5, null, null, null, null, null, DateTimeException.class}, + {2008, null, 30, 11, 5, null, null, null, null, null, DateTimeException.class}, + {null, 6, 30, 11, 5, null, null, null, null, null, DateTimeException.class}, + + {2008, 6, 30, 11, 5, null, null, null, null, "2008-06-30T11:05", null}, + {2008, 6, 30, 11, 5, 30, null, null, null, "2008-06-30T11:05:30", null}, + {2008, 6, 30, 11, 5, 30, 500000000, null, null, "2008-06-30T11:05:30.5", null}, + {2008, 6, 30, 11, 5, 30, 1, null, null, "2008-06-30T11:05:30.000000001", null}, + + {2008, 6, 30, 11, 5, null, null, "+01:00", null, "2008-06-30T11:05", null}, + {2008, 6, 30, 11, 5, 30, null, "+01:00", null, "2008-06-30T11:05:30", null}, + {2008, 6, 30, 11, 5, 30, 500000000, "+01:00", null, "2008-06-30T11:05:30.5", null}, + {2008, 6, 30, 11, 5, 30, 1, "+01:00", null, "2008-06-30T11:05:30.000000001", null}, + + {2008, 6, 30, 11, 5, null, null, "+01:00", "Europe/Paris", "2008-06-30T11:05", null}, + {2008, 6, 30, 11, 5, 30, null, "+01:00", "Europe/Paris", "2008-06-30T11:05:30", null}, + {2008, 6, 30, 11, 5, 30, 500000000, "+01:00", "Europe/Paris", "2008-06-30T11:05:30.5", null}, + {2008, 6, 30, 11, 5, 30, 1, "+01:00", "Europe/Paris", "2008-06-30T11:05:30.000000001", null}, + + {2008, 6, 30, 11, 5, null, null, null, "Europe/Paris", "2008-06-30T11:05", null}, + {2008, 6, 30, 11, 5, 30, null, null, "Europe/Paris", "2008-06-30T11:05:30", null}, + {2008, 6, 30, 11, 5, 30, 500000000, null, "Europe/Paris", "2008-06-30T11:05:30.5", null}, + {2008, 6, 30, 11, 5, 30, 1, null, "Europe/Paris", "2008-06-30T11:05:30.000000001", null}, + + {123456, 6, 30, 11, 5, null, null, null, null, "+123456-06-30T11:05", null}, + }; + } + + @Test(dataProvider="sample_isoLocalDateTime", groups={"tck"}) + public void test_print_isoLocalDateTime( + Integer year, Integer month, Integer day, + Integer hour, Integer min, Integer sec, Integer nano, String offsetId, String zoneId, + String expected, Class expectedEx) { + TemporalAccessor test = buildAccessor(year, month, day, hour, min, sec, nano, offsetId, zoneId); + if (expectedEx == null) { + assertEquals(DateTimeFormatters.isoLocalDateTime().print(test), expected); + } else { + try { + DateTimeFormatters.isoLocalDateTime().print(test); + fail(); + } catch (Exception ex) { + assertTrue(expectedEx.isInstance(ex)); + } + } + } + + @Test(dataProvider="sample_isoLocalDateTime", groups={"tck"}) + public void test_parse_isoLocalDateTime( + Integer year, Integer month, Integer day, + Integer hour, Integer min, Integer sec, Integer nano, String offsetId, String zoneId, + String input, Class invalid) { + if (input != null) { + DateTimeBuilder expected = createDateTime(year, month, day, hour, min, sec, nano); + assertParseMatch(DateTimeFormatters.isoLocalDateTime().parseToBuilder(input, new ParsePosition(0)), expected); + } + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @DataProvider(name="sample_isoOffsetDateTime") + Object[][] provider_sample_isoOffsetDateTime() { + return new Object[][]{ + {2008, null, null, null, null, null, null, null, null, null, DateTimeException.class}, + {null, 6, null, null, null, null, null, null, null, null, DateTimeException.class}, + {null, null, 30, null, null, null, null, null, null, null, DateTimeException.class}, + {null, null, null, 11, null, null, null, null, null, null, DateTimeException.class}, + {null, null, null, null, 5, null, null, null, null, null, DateTimeException.class}, + {null, null, null, null, null, null, null, "+01:00", null, null, DateTimeException.class}, + {null, null, null, null, null, null, null, null, "Europe/Paris", null, DateTimeException.class}, + {2008, 6, 30, 11, null, null, null, null, null, null, DateTimeException.class}, + {2008, 6, 30, null, 5, null, null, null, null, null, DateTimeException.class}, + {2008, 6, null, 11, 5, null, null, null, null, null, DateTimeException.class}, + {2008, null, 30, 11, 5, null, null, null, null, null, DateTimeException.class}, + {null, 6, 30, 11, 5, null, null, null, null, null, DateTimeException.class}, + + {2008, 6, 30, 11, 5, null, null, null, null, null, DateTimeException.class}, + {2008, 6, 30, 11, 5, 30, null, null, null, null, DateTimeException.class}, + {2008, 6, 30, 11, 5, 30, 500000000, null, null, null, DateTimeException.class}, + {2008, 6, 30, 11, 5, 30, 1, null, null, null, DateTimeException.class}, + + {2008, 6, 30, 11, 5, null, null, "+01:00", null, "2008-06-30T11:05+01:00", null}, + {2008, 6, 30, 11, 5, 30, null, "+01:00", null, "2008-06-30T11:05:30+01:00", null}, + {2008, 6, 30, 11, 5, 30, 500000000, "+01:00", null, "2008-06-30T11:05:30.5+01:00", null}, + {2008, 6, 30, 11, 5, 30, 1, "+01:00", null, "2008-06-30T11:05:30.000000001+01:00", null}, + + {2008, 6, 30, 11, 5, null, null, "+01:00", "Europe/Paris", "2008-06-30T11:05+01:00", null}, + {2008, 6, 30, 11, 5, 30, null, "+01:00", "Europe/Paris", "2008-06-30T11:05:30+01:00", null}, + {2008, 6, 30, 11, 5, 30, 500000000, "+01:00", "Europe/Paris", "2008-06-30T11:05:30.5+01:00", null}, + {2008, 6, 30, 11, 5, 30, 1, "+01:00", "Europe/Paris", "2008-06-30T11:05:30.000000001+01:00", null}, + + {2008, 6, 30, 11, 5, null, null, null, "Europe/Paris", null, DateTimeException.class}, + {2008, 6, 30, 11, 5, 30, null, null, "Europe/Paris", null, DateTimeException.class}, + {2008, 6, 30, 11, 5, 30, 500000000, null, "Europe/Paris", null, DateTimeException.class}, + {2008, 6, 30, 11, 5, 30, 1, null, "Europe/Paris", null, DateTimeException.class}, + + {123456, 6, 30, 11, 5, null, null, "+01:00", null, "+123456-06-30T11:05+01:00", null}, + }; + } + + @Test(dataProvider="sample_isoOffsetDateTime", groups={"tck"}) + public void test_print_isoOffsetDateTime( + Integer year, Integer month, Integer day, + Integer hour, Integer min, Integer sec, Integer nano, String offsetId, String zoneId, + String expected, Class expectedEx) { + TemporalAccessor test = buildAccessor(year, month, day, hour, min, sec, nano, offsetId, zoneId); + if (expectedEx == null) { + assertEquals(DateTimeFormatters.isoOffsetDateTime().print(test), expected); + } else { + try { + DateTimeFormatters.isoOffsetDateTime().print(test); + fail(); + } catch (Exception ex) { + assertTrue(expectedEx.isInstance(ex)); + } + } + } + + @Test(dataProvider="sample_isoOffsetDateTime", groups={"tck"}) + public void test_parse_isoOffsetDateTime( + Integer year, Integer month, Integer day, + Integer hour, Integer min, Integer sec, Integer nano, String offsetId, String zoneId, + String input, Class invalid) { + if (input != null) { + DateTimeBuilder expected = createDateTime(year, month, day, hour, min, sec, nano); + buildCalendrical(expected, offsetId, null); // zone not expected to be parsed + assertParseMatch(DateTimeFormatters.isoOffsetDateTime().parseToBuilder(input, new ParsePosition(0)), expected); + } + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @DataProvider(name="sample_isoZonedDateTime") + Object[][] provider_sample_isoZonedDateTime() { + return new Object[][]{ + {2008, null, null, null, null, null, null, null, null, null, DateTimeException.class}, + {null, 6, null, null, null, null, null, null, null, null, DateTimeException.class}, + {null, null, 30, null, null, null, null, null, null, null, DateTimeException.class}, + {null, null, null, 11, null, null, null, null, null, null, DateTimeException.class}, + {null, null, null, null, 5, null, null, null, null, null, DateTimeException.class}, + {null, null, null, null, null, null, null, "+01:00", null, null, DateTimeException.class}, + {null, null, null, null, null, null, null, null, "Europe/Paris", null, DateTimeException.class}, + {2008, 6, 30, 11, null, null, null, null, null, null, DateTimeException.class}, + {2008, 6, 30, null, 5, null, null, null, null, null, DateTimeException.class}, + {2008, 6, null, 11, 5, null, null, null, null, null, DateTimeException.class}, + {2008, null, 30, 11, 5, null, null, null, null, null, DateTimeException.class}, + {null, 6, 30, 11, 5, null, null, null, null, null, DateTimeException.class}, + + {2008, 6, 30, 11, 5, null, null, null, null, null, DateTimeException.class}, + {2008, 6, 30, 11, 5, 30, null, null, null, null, DateTimeException.class}, + {2008, 6, 30, 11, 5, 30, 500000000, null, null, null, DateTimeException.class}, + {2008, 6, 30, 11, 5, 30, 1, null, null, null, DateTimeException.class}, + + // allow OffsetDateTime (no harm comes of this AFAICT) + {2008, 6, 30, 11, 5, null, null, "+01:00", null, "2008-06-30T11:05+01:00", null}, + {2008, 6, 30, 11, 5, 30, null, "+01:00", null, "2008-06-30T11:05:30+01:00", null}, + {2008, 6, 30, 11, 5, 30, 500000000, "+01:00", null, "2008-06-30T11:05:30.5+01:00", null}, + {2008, 6, 30, 11, 5, 30, 1, "+01:00", null, "2008-06-30T11:05:30.000000001+01:00", null}, + + // ZonedDateTime with ZoneId of ZoneOffset + {2008, 6, 30, 11, 5, null, null, "+01:00", "+01:00", "2008-06-30T11:05+01:00", null}, + {2008, 6, 30, 11, 5, 30, null, "+01:00", "+01:00", "2008-06-30T11:05:30+01:00", null}, + {2008, 6, 30, 11, 5, 30, 500000000, "+01:00", "+01:00", "2008-06-30T11:05:30.5+01:00", null}, + {2008, 6, 30, 11, 5, 30, 1, "+01:00", "+01:00", "2008-06-30T11:05:30.000000001+01:00", null}, + + // ZonedDateTime with ZoneId of ZoneRegion + {2008, 6, 30, 11, 5, null, null, "+01:00", "Europe/Paris", "2008-06-30T11:05+01:00[Europe/Paris]", null}, + {2008, 6, 30, 11, 5, 30, null, "+01:00", "Europe/Paris", "2008-06-30T11:05:30+01:00[Europe/Paris]", null}, + {2008, 6, 30, 11, 5, 30, 500000000, "+01:00", "Europe/Paris", "2008-06-30T11:05:30.5+01:00[Europe/Paris]", null}, + {2008, 6, 30, 11, 5, 30, 1, "+01:00", "Europe/Paris", "2008-06-30T11:05:30.000000001+01:00[Europe/Paris]", null}, + + // offset required + {2008, 6, 30, 11, 5, null, null, null, "Europe/Paris", null, DateTimeException.class}, + {2008, 6, 30, 11, 5, 30, null, null, "Europe/Paris", null, DateTimeException.class}, + {2008, 6, 30, 11, 5, 30, 500000000, null, "Europe/Paris", null, DateTimeException.class}, + {2008, 6, 30, 11, 5, 30, 1, null, "Europe/Paris", null, DateTimeException.class}, + + {123456, 6, 30, 11, 5, null, null, "+01:00", "Europe/Paris", "+123456-06-30T11:05+01:00[Europe/Paris]", null}, + }; + } + + @Test(dataProvider="sample_isoZonedDateTime", groups={"tck"}) + public void test_print_isoZonedDateTime( + Integer year, Integer month, Integer day, + Integer hour, Integer min, Integer sec, Integer nano, String offsetId, String zoneId, + String expected, Class expectedEx) { + TemporalAccessor test = buildAccessor(year, month, day, hour, min, sec, nano, offsetId, zoneId); + if (expectedEx == null) { + assertEquals(DateTimeFormatters.isoZonedDateTime().print(test), expected); + } else { + try { + DateTimeFormatters.isoZonedDateTime().print(test); + fail(test.toString()); + } catch (Exception ex) { + assertTrue(expectedEx.isInstance(ex)); + } + } + } + + @Test(dataProvider="sample_isoZonedDateTime", groups={"tck"}) + public void test_parse_isoZonedDateTime( + Integer year, Integer month, Integer day, + Integer hour, Integer min, Integer sec, Integer nano, String offsetId, String zoneId, + String input, Class invalid) { + if (input != null) { + DateTimeBuilder expected = createDateTime(year, month, day, hour, min, sec, nano); + if (offsetId.equals(zoneId)) { + buildCalendrical(expected, offsetId, null); + } else { + buildCalendrical(expected, offsetId, zoneId); + } + assertParseMatch(DateTimeFormatters.isoZonedDateTime().parseToBuilder(input, new ParsePosition(0)), expected); + } + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @DataProvider(name="sample_isoDateTime") + Object[][] provider_sample_isoDateTime() { + return new Object[][]{ + {2008, null, null, null, null, null, null, null, null, null, DateTimeException.class}, + {null, 6, null, null, null, null, null, null, null, null, DateTimeException.class}, + {null, null, 30, null, null, null, null, null, null, null, DateTimeException.class}, + {null, null, null, 11, null, null, null, null, null, null, DateTimeException.class}, + {null, null, null, null, 5, null, null, null, null, null, DateTimeException.class}, + {null, null, null, null, null, null, null, "+01:00", null, null, DateTimeException.class}, + {null, null, null, null, null, null, null, null, "Europe/Paris", null, DateTimeException.class}, + {2008, 6, 30, 11, null, null, null, null, null, null, DateTimeException.class}, + {2008, 6, 30, null, 5, null, null, null, null, null, DateTimeException.class}, + {2008, 6, null, 11, 5, null, null, null, null, null, DateTimeException.class}, + {2008, null, 30, 11, 5, null, null, null, null, null, DateTimeException.class}, + {null, 6, 30, 11, 5, null, null, null, null, null, DateTimeException.class}, + + {2008, 6, 30, 11, 5, null, null, null, null, "2008-06-30T11:05", null}, + {2008, 6, 30, 11, 5, 30, null, null, null, "2008-06-30T11:05:30", null}, + {2008, 6, 30, 11, 5, 30, 500000000, null, null, "2008-06-30T11:05:30.5", null}, + {2008, 6, 30, 11, 5, 30, 1, null, null, "2008-06-30T11:05:30.000000001", null}, + + {2008, 6, 30, 11, 5, null, null, "+01:00", null, "2008-06-30T11:05+01:00", null}, + {2008, 6, 30, 11, 5, 30, null, "+01:00", null, "2008-06-30T11:05:30+01:00", null}, + {2008, 6, 30, 11, 5, 30, 500000000, "+01:00", null, "2008-06-30T11:05:30.5+01:00", null}, + {2008, 6, 30, 11, 5, 30, 1, "+01:00", null, "2008-06-30T11:05:30.000000001+01:00", null}, + + {2008, 6, 30, 11, 5, null, null, "+01:00", "Europe/Paris", "2008-06-30T11:05+01:00[Europe/Paris]", null}, + {2008, 6, 30, 11, 5, 30, null, "+01:00", "Europe/Paris", "2008-06-30T11:05:30+01:00[Europe/Paris]", null}, + {2008, 6, 30, 11, 5, 30, 500000000, "+01:00", "Europe/Paris", "2008-06-30T11:05:30.5+01:00[Europe/Paris]", null}, + {2008, 6, 30, 11, 5, 30, 1, "+01:00", "Europe/Paris", "2008-06-30T11:05:30.000000001+01:00[Europe/Paris]", null}, + + {2008, 6, 30, 11, 5, null, null, null, "Europe/Paris", "2008-06-30T11:05", null}, + {2008, 6, 30, 11, 5, 30, null, null, "Europe/Paris", "2008-06-30T11:05:30", null}, + {2008, 6, 30, 11, 5, 30, 500000000, null, "Europe/Paris", "2008-06-30T11:05:30.5", null}, + {2008, 6, 30, 11, 5, 30, 1, null, "Europe/Paris", "2008-06-30T11:05:30.000000001", null}, + + {123456, 6, 30, 11, 5, null, null, null, null, "+123456-06-30T11:05", null}, + }; + } + + @Test(dataProvider="sample_isoDateTime", groups={"tck"}) + public void test_print_isoDateTime( + Integer year, Integer month, Integer day, + Integer hour, Integer min, Integer sec, Integer nano, String offsetId, String zoneId, + String expected, Class expectedEx) { + TemporalAccessor test = buildAccessor(year, month, day, hour, min, sec, nano, offsetId, zoneId); + if (expectedEx == null) { + assertEquals(DateTimeFormatters.isoDateTime().print(test), expected); + } else { + try { + DateTimeFormatters.isoDateTime().print(test); + fail(); + } catch (Exception ex) { + assertTrue(expectedEx.isInstance(ex)); + } + } + } + + @Test(dataProvider="sample_isoDateTime", groups={"tck"}) + public void test_parse_isoDateTime( + Integer year, Integer month, Integer day, + Integer hour, Integer min, Integer sec, Integer nano, String offsetId, String zoneId, + String input, Class invalid) { + if (input != null) { + DateTimeBuilder expected = createDateTime(year, month, day, hour, min, sec, nano); + if (offsetId != null) { + expected.addFieldValue(OFFSET_SECONDS, ZoneOffset.of(offsetId).getTotalSeconds()); + if (zoneId != null) { + expected.addCalendrical(ZoneId.of(zoneId)); + } + } + assertParseMatch(DateTimeFormatters.isoDateTime().parseToBuilder(input, new ParsePosition(0)), expected); + } + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_print_isoOrdinalDate() { + TemporalAccessor test = buildAccessor(LocalDateTime.of(2008, 6, 3, 11, 5, 30), null, null); + assertEquals(DateTimeFormatters.isoOrdinalDate().print(test), "2008-155"); + } + + @Test(groups={"tck"}) + public void test_print_isoOrdinalDate_offset() { + TemporalAccessor test = buildAccessor(LocalDateTime.of(2008, 6, 3, 11, 5, 30), "Z", null); + assertEquals(DateTimeFormatters.isoOrdinalDate().print(test), "2008-155Z"); + } + + @Test(groups={"tck"}) + public void test_print_isoOrdinalDate_zoned() { + TemporalAccessor test = buildAccessor(LocalDateTime.of(2008, 6, 3, 11, 5, 30), "+02:00", "Europe/Paris"); + assertEquals(DateTimeFormatters.isoOrdinalDate().print(test), "2008-155+02:00"); + } + + @Test(groups={"tck"}) + public void test_print_isoOrdinalDate_zoned_largeYear() { + TemporalAccessor test = buildAccessor(LocalDateTime.of(123456, 6, 3, 11, 5, 30), "Z", null); + assertEquals(DateTimeFormatters.isoOrdinalDate().print(test), "+123456-155Z"); + } + + @Test(groups={"tck"}) + public void test_print_isoOrdinalDate_fields() { + TemporalAccessor test = new DateTimeBuilder(YEAR, 2008).addFieldValue(DAY_OF_YEAR, 231); + assertEquals(DateTimeFormatters.isoOrdinalDate().print(test), "2008-231"); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_print_isoOrdinalDate_missingField() { + TemporalAccessor test = Year.of(2008); + DateTimeFormatters.isoOrdinalDate().print(test); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_parse_isoOrdinalDate() { + DateTimeBuilder expected = new DateTimeBuilder(YEAR, 2008).addFieldValue(DAY_OF_YEAR, 123); + assertParseMatch(DateTimeFormatters.isoOrdinalDate().parseToBuilder("2008-123", new ParsePosition(0)), expected); + } + + @Test(groups={"tck"}) + public void test_parse_isoOrdinalDate_largeYear() { + DateTimeBuilder expected = new DateTimeBuilder(YEAR, 123456).addFieldValue(DAY_OF_YEAR, 123); + assertParseMatch(DateTimeFormatters.isoOrdinalDate().parseToBuilder("+123456-123", new ParsePosition(0)), expected); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_print_basicIsoDate() { + TemporalAccessor test = buildAccessor(LocalDateTime.of(2008, 6, 3, 11, 5, 30), null, null); + assertEquals(DateTimeFormatters.basicIsoDate().print(test), "20080603"); + } + + @Test(groups={"tck"}) + public void test_print_basicIsoDate_offset() { + TemporalAccessor test = buildAccessor(LocalDateTime.of(2008, 6, 3, 11, 5, 30), "Z", null); + assertEquals(DateTimeFormatters.basicIsoDate().print(test), "20080603Z"); + } + + @Test(groups={"tck"}) + public void test_print_basicIsoDate_zoned() { + TemporalAccessor test = buildAccessor(LocalDateTime.of(2008, 6, 3, 11, 5, 30), "+02:00", "Europe/Paris"); + assertEquals(DateTimeFormatters.basicIsoDate().print(test), "20080603+0200"); + } + + @Test(expectedExceptions=DateTimePrintException.class, groups={"tck"}) + public void test_print_basicIsoDate_largeYear() { + TemporalAccessor test = buildAccessor(LocalDateTime.of(123456, 6, 3, 11, 5, 30), "Z", null); + DateTimeFormatters.basicIsoDate().print(test); + } + + @Test(groups={"tck"}) + public void test_print_basicIsoDate_fields() { + TemporalAccessor test = buildAccessor(LocalDate.of(2008, 6, 3), null, null); + assertEquals(DateTimeFormatters.basicIsoDate().print(test), "20080603"); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_print_basicIsoDate_missingField() { + TemporalAccessor test = YearMonth.of(2008, 6); + DateTimeFormatters.basicIsoDate().print(test); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_parse_basicIsoDate() { + LocalDate expected = LocalDate.of(2008, 6, 3); + assertEquals(DateTimeFormatters.basicIsoDate().parse("20080603", LocalDate::from), expected); + } + + @Test(expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void test_parse_basicIsoDate_largeYear() { + try { + LocalDate expected = LocalDate.of(123456, 6, 3); + assertEquals(DateTimeFormatters.basicIsoDate().parse("+1234560603", LocalDate::from), expected); + } catch (DateTimeParseException ex) { + assertEquals(ex.getErrorIndex(), 0); + assertEquals(ex.getParsedString(), "+1234560603"); + throw ex; + } + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @DataProvider(name="weekDate") + Iterator weekDate() { + return new Iterator() { + private ZonedDateTime date = ZonedDateTime.of(LocalDateTime.of(2003, 12, 29, 11, 5, 30), ZoneId.of("Europe/Paris")); + private ZonedDateTime endDate = date.withYear(2005).withMonth(1).withDayOfMonth(2); + private int week = 1; + private int day = 1; + + public boolean hasNext() { + return !date.isAfter(endDate); + } + public Object[] next() { + StringBuilder sb = new StringBuilder("2004-W"); + if (week < 10) { + sb.append('0'); + } + sb.append(week).append('-').append(day).append(date.getOffset()); + Object[] ret = new Object[] {date, sb.toString()}; + date = date.plusDays(1); + day += 1; + if (day == 8) { + day = 1; + week++; + } + return ret; + } + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + @Test(dataProvider="weekDate", groups={"tck"}) + public void test_print_isoWeekDate(TemporalAccessor test, String expected) { + assertEquals(DateTimeFormatters.isoWeekDate().print(test), expected); + } + + @Test(groups={"tck"}) + public void test_print_isoWeekDate_zoned_largeYear() { + TemporalAccessor test = buildAccessor(LocalDateTime.of(123456, 6, 3, 11, 5, 30), "Z", null); + assertEquals(DateTimeFormatters.isoWeekDate().print(test), "+123456-W23-2Z"); + } + + @Test(groups={"tck"}) + public void test_print_isoWeekDate_fields() { + TemporalAccessor test = buildAccessor(LocalDate.of(2004, 1, 27), null, null); + assertEquals(DateTimeFormatters.isoWeekDate().print(test), "2004-W05-2"); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_print_isoWeekDate_missingField() { + TemporalAccessor test = YearMonth.of(2008, 6); + DateTimeFormatters.isoWeekDate().print(test); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_parse_weekDate() { + LocalDate expected = LocalDate.of(2004, 1, 28); + assertEquals(DateTimeFormatters.isoWeekDate().parse("2004-W05-3", LocalDate::from), expected); + } + + @Test(groups={"tck"}) + public void test_parse_weekDate_largeYear() { + DateTimeBuilder builder = DateTimeFormatters.isoWeekDate().parseToBuilder("+123456-W04-5", new ParsePosition(0)); + assertEquals(builder.getFieldValue(ISOFields.WEEK_BASED_YEAR), 123456); + assertEquals(builder.getFieldValue(ISOFields.WEEK_OF_WEEK_BASED_YEAR), 4); + assertEquals(builder.getFieldValue(DAY_OF_WEEK), 5); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @DataProvider(name="rfc") + Object[][] data_rfc() { + return new Object[][] { + {LocalDateTime.of(2008, 6, 3, 11, 5, 30), "Z", "Tue, 3 Jun 2008 11:05:30 GMT"}, + {LocalDateTime.of(2008, 6, 30, 11, 5, 30), "Z", "Mon, 30 Jun 2008 11:05:30 GMT"}, + {LocalDateTime.of(2008, 6, 3, 11, 5, 30), "+02:00", "Tue, 3 Jun 2008 11:05:30 +0200"}, + {LocalDateTime.of(2008, 6, 30, 11, 5, 30), "-03:00", "Mon, 30 Jun 2008 11:05:30 -0300"}, + }; + } + + @Test(groups={"tck"}, dataProvider="rfc") + public void test_print_rfc1123(LocalDateTime base, String offsetId, String expected) { + TemporalAccessor test = buildAccessor(base, offsetId, null); + assertEquals(DateTimeFormatters.rfc1123().print(test), expected); + } + + @Test(groups={"tck"}, dataProvider="rfc") + public void test_print_rfc1123_french(LocalDateTime base, String offsetId, String expected) { + TemporalAccessor test = buildAccessor(base, offsetId, null); + assertEquals(DateTimeFormatters.rfc1123().withLocale(Locale.FRENCH).print(test), expected); + } + + @Test(groups={"tck"}, expectedExceptions=DateTimeException.class) + public void test_print_rfc1123_missingField() { + TemporalAccessor test = YearMonth.of(2008, 6); + DateTimeFormatters.rfc1123().print(test); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + private DateTimeBuilder createDate(Integer year, Integer month, Integer day) { + DateTimeBuilder test = new DateTimeBuilder(); + if (year != null) { + test.addFieldValue(YEAR, year); + } + if (month != null) { + test.addFieldValue(MONTH_OF_YEAR, month); + } + if (day != null) { + test.addFieldValue(DAY_OF_MONTH, day); + } + return test; + } + + private DateTimeBuilder createTime(Integer hour, Integer min, Integer sec, Integer nano) { + DateTimeBuilder test = new DateTimeBuilder(); + if (hour != null) { + test.addFieldValue(HOUR_OF_DAY, hour); + } + if (min != null) { + test.addFieldValue(MINUTE_OF_HOUR, min); + } + if (sec != null) { + test.addFieldValue(SECOND_OF_MINUTE, sec); + } + if (nano != null) { + test.addFieldValue(NANO_OF_SECOND, nano); + } + return test; + } + + private DateTimeBuilder createDateTime( + Integer year, Integer month, Integer day, + Integer hour, Integer min, Integer sec, Integer nano) { + DateTimeBuilder test = new DateTimeBuilder(); + if (year != null) { + test.addFieldValue(YEAR, year); + } + if (month != null) { + test.addFieldValue(MONTH_OF_YEAR, month); + } + if (day != null) { + test.addFieldValue(DAY_OF_MONTH, day); + } + if (hour != null) { + test.addFieldValue(HOUR_OF_DAY, hour); + } + if (min != null) { + test.addFieldValue(MINUTE_OF_HOUR, min); + } + if (sec != null) { + test.addFieldValue(SECOND_OF_MINUTE, sec); + } + if (nano != null) { + test.addFieldValue(NANO_OF_SECOND, nano); + } + return test; + } + + private TemporalAccessor buildAccessor( + Integer year, Integer month, Integer day, + Integer hour, Integer min, Integer sec, Integer nano, + String offsetId, String zoneId) { + MockAccessor mock = new MockAccessor(); + if (year != null) { + mock.fields.put(YEAR, (long) year); + } + if (month != null) { + mock.fields.put(MONTH_OF_YEAR, (long) month); + } + if (day != null) { + mock.fields.put(DAY_OF_MONTH, (long) day); + } + if (hour != null) { + mock.fields.put(HOUR_OF_DAY, (long) hour); + } + if (min != null) { + mock.fields.put(MINUTE_OF_HOUR, (long) min); + } + if (sec != null) { + mock.fields.put(SECOND_OF_MINUTE, (long) sec); + } + if (nano != null) { + mock.fields.put(NANO_OF_SECOND, (long) nano); + } + mock.setOffset(offsetId); + mock.setZone(zoneId); + return mock; + } + + private TemporalAccessor buildAccessor(LocalDateTime base, String offsetId, String zoneId) { + MockAccessor mock = new MockAccessor(); + mock.setFields(base); + mock.setOffset(offsetId); + mock.setZone(zoneId); + return mock; + } + + private TemporalAccessor buildAccessor(LocalDate base, String offsetId, String zoneId) { + MockAccessor mock = new MockAccessor(); + mock.setFields(base); + mock.setOffset(offsetId); + mock.setZone(zoneId); + return mock; + } + + private void buildCalendrical(DateTimeBuilder cal, String offsetId, String zoneId) { + if (offsetId != null) { + cal.addFieldValue(OFFSET_SECONDS, ZoneOffset.of(offsetId).getTotalSeconds()); + } + if (zoneId != null) { + cal.addCalendrical(ZoneId.of(zoneId)); + } + } + + private void assertParseMatch(DateTimeBuilder parsed, DateTimeBuilder expected) { + Map parsedFVMap = parsed.getFieldValueMap(); + Map expectedFVMap = expected.getFieldValueMap(); + assertEquals(parsedFVMap, expectedFVMap); + + List parsedCMap = parsed.getCalendricalList(); + List expectedCMap = expected.getCalendricalList(); + assertEquals(parsedCMap, expectedCMap); + } + + //------------------------------------------------------------------------- + Map fields = new HashMap<>(); + ZoneId zoneId; + static class MockAccessor implements TemporalAccessor { + Map fields = new HashMap<>(); + ZoneId zoneId; + + void setFields(LocalDate dt) { + if (dt != null) { + fields.put(YEAR, (long) dt.getYear()); + fields.put(MONTH_OF_YEAR, (long) dt.getMonthValue()); + fields.put(DAY_OF_MONTH, (long) dt.getDayOfMonth()); + fields.put(DAY_OF_YEAR, (long) dt.getDayOfYear()); + fields.put(DAY_OF_WEEK, (long) dt.getDayOfWeek().getValue()); + fields.put(ISOFields.WEEK_BASED_YEAR, dt.getLong(ISOFields.WEEK_BASED_YEAR)); + fields.put(ISOFields.WEEK_OF_WEEK_BASED_YEAR, dt.getLong(ISOFields.WEEK_OF_WEEK_BASED_YEAR)); + } + } + + void setFields(LocalDateTime dt) { + if (dt != null) { + fields.put(YEAR, (long) dt.getYear()); + fields.put(MONTH_OF_YEAR, (long) dt.getMonthValue()); + fields.put(DAY_OF_MONTH, (long) dt.getDayOfMonth()); + fields.put(DAY_OF_YEAR, (long) dt.getDayOfYear()); + fields.put(DAY_OF_WEEK, (long) dt.getDayOfWeek().getValue()); + fields.put(ISOFields.WEEK_BASED_YEAR, dt.getLong(ISOFields.WEEK_BASED_YEAR)); + fields.put(ISOFields.WEEK_OF_WEEK_BASED_YEAR, dt.getLong(ISOFields.WEEK_OF_WEEK_BASED_YEAR)); + fields.put(HOUR_OF_DAY, (long) dt.getHour()); + fields.put(MINUTE_OF_HOUR, (long) dt.getMinute()); + fields.put(SECOND_OF_MINUTE, (long) dt.getSecond()); + fields.put(NANO_OF_SECOND, (long) dt.getNano()); + } + } + + void setOffset(String offsetId) { + if (offsetId != null) { + this.fields.put(OFFSET_SECONDS, (long) ZoneOffset.of(offsetId).getTotalSeconds()); + } + } + + void setZone(String zoneId) { + if (zoneId != null) { + this.zoneId = ZoneId.of(zoneId); + } + } + + @Override + public boolean isSupported(TemporalField field) { + return fields.containsKey(field); + } + + @Override + public long getLong(TemporalField field) { + try { + return fields.get(field); + } catch (NullPointerException ex) { + throw new DateTimeException("Field missing: " + field); + } + } + + @SuppressWarnings("unchecked") + @Override + public R query(TemporalQuery query) { + if (query == Queries.zoneId()) { + return (R) zoneId; + } + return TemporalAccessor.super.query(query); + } + + @Override + public String toString() { + return fields + (zoneId != null ? " " + zoneId : ""); + } + } + +} diff --git a/jdk/test/java/time/tck/java/time/format/TCKDateTimePrintException.java b/jdk/test/java/time/tck/java/time/format/TCKDateTimePrintException.java new file mode 100644 index 00000000000..0117629f8cb --- /dev/null +++ b/jdk/test/java/time/tck/java/time/format/TCKDateTimePrintException.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.format; + +import java.time.format.*; +import test.java.time.format.*; + +import static org.testng.Assert.assertEquals; + +import java.io.IOException; + +import org.testng.annotations.Test; + +/** + * Test DateTimePrintException. + */ +@Test +public class TCKDateTimePrintException { + + @Test(groups={"tck"}) + public void test_constructor_String() throws Exception { + DateTimePrintException ex = new DateTimePrintException("TEST"); + assertEquals(ex.getMessage(), "TEST"); + } + + @Test(groups={"tck"}) + public void test_constructor_StringThrowable_notIOException_equal() throws Exception { + IllegalArgumentException iaex = new IllegalArgumentException("INNER"); + DateTimePrintException ex = new DateTimePrintException("TEST", iaex); + assertEquals(ex.getMessage(), "TEST"); + assertEquals(ex.getCause(), iaex); + ex.rethrowIOException(); // no effect + } + + @Test(expectedExceptions=IOException.class, groups={"tck"}) + public void test_constructor_StringThrowable_IOException() throws Exception { + IOException ioex = new IOException("INNER"); + DateTimePrintException ex = new DateTimePrintException("TEST", ioex); + assertEquals(ex.getMessage(), "TEST"); + assertEquals(ex.getCause(), ioex); + ex.rethrowIOException(); // rethrows + } + +} diff --git a/jdk/test/java/time/tck/java/time/format/TCKDateTimeTextPrinting.java b/jdk/test/java/time/tck/java/time/format/TCKDateTimeTextPrinting.java new file mode 100644 index 00000000000..5af0ebf41a9 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/format/TCKDateTimeTextPrinting.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2009-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.format; + +import java.time.format.*; + +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.DAY_OF_WEEK; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static org.testng.Assert.assertEquals; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import java.time.LocalDateTime; +import java.time.Month; +import java.time.temporal.TemporalField; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test text printing. + */ +@Test +public class TCKDateTimeTextPrinting { + + private DateTimeFormatterBuilder builder; + + @BeforeMethod(groups={"tck"}) + public void setUp() { + builder = new DateTimeFormatterBuilder(); + } + + //----------------------------------------------------------------------- + @DataProvider(name="printText") + Object[][] data_text() { + return new Object[][] { + {DAY_OF_WEEK, TextStyle.FULL, 1, "Monday"}, + {DAY_OF_WEEK, TextStyle.FULL, 2, "Tuesday"}, + {DAY_OF_WEEK, TextStyle.FULL, 3, "Wednesday"}, + {DAY_OF_WEEK, TextStyle.FULL, 4, "Thursday"}, + {DAY_OF_WEEK, TextStyle.FULL, 5, "Friday"}, + {DAY_OF_WEEK, TextStyle.FULL, 6, "Saturday"}, + {DAY_OF_WEEK, TextStyle.FULL, 7, "Sunday"}, + + {DAY_OF_WEEK, TextStyle.SHORT, 1, "Mon"}, + {DAY_OF_WEEK, TextStyle.SHORT, 2, "Tue"}, + {DAY_OF_WEEK, TextStyle.SHORT, 3, "Wed"}, + {DAY_OF_WEEK, TextStyle.SHORT, 4, "Thu"}, + {DAY_OF_WEEK, TextStyle.SHORT, 5, "Fri"}, + {DAY_OF_WEEK, TextStyle.SHORT, 6, "Sat"}, + {DAY_OF_WEEK, TextStyle.SHORT, 7, "Sun"}, + + {DAY_OF_MONTH, TextStyle.FULL, 1, "1"}, + {DAY_OF_MONTH, TextStyle.FULL, 2, "2"}, + {DAY_OF_MONTH, TextStyle.FULL, 3, "3"}, + {DAY_OF_MONTH, TextStyle.FULL, 28, "28"}, + {DAY_OF_MONTH, TextStyle.FULL, 29, "29"}, + {DAY_OF_MONTH, TextStyle.FULL, 30, "30"}, + {DAY_OF_MONTH, TextStyle.FULL, 31, "31"}, + + {DAY_OF_MONTH, TextStyle.SHORT, 1, "1"}, + {DAY_OF_MONTH, TextStyle.SHORT, 2, "2"}, + {DAY_OF_MONTH, TextStyle.SHORT, 3, "3"}, + {DAY_OF_MONTH, TextStyle.SHORT, 28, "28"}, + {DAY_OF_MONTH, TextStyle.SHORT, 29, "29"}, + {DAY_OF_MONTH, TextStyle.SHORT, 30, "30"}, + {DAY_OF_MONTH, TextStyle.SHORT, 31, "31"}, + + {MONTH_OF_YEAR, TextStyle.FULL, 1, "January"}, + {MONTH_OF_YEAR, TextStyle.FULL, 12, "December"}, + + {MONTH_OF_YEAR, TextStyle.SHORT, 1, "Jan"}, + {MONTH_OF_YEAR, TextStyle.SHORT, 12, "Dec"}, + }; + } + + @Test(dataProvider="printText", groups={"tck"}) + public void test_appendText2arg_print(TemporalField field, TextStyle style, int value, String expected) throws Exception { + DateTimeFormatter f = builder.appendText(field, style).toFormatter(Locale.ENGLISH); + LocalDateTime dt = LocalDateTime.of(2010, 1, 1, 0, 0); + dt = dt.with(field, value); + String text = f.print(dt); + assertEquals(text, expected); + } + + @Test(dataProvider="printText", groups={"tck"}) + public void test_appendText1arg_print(TemporalField field, TextStyle style, int value, String expected) throws Exception { + if (style == TextStyle.FULL) { + DateTimeFormatter f = builder.appendText(field).toFormatter(Locale.ENGLISH); + LocalDateTime dt = LocalDateTime.of(2010, 1, 1, 0, 0); + dt = dt.with(field, value); + String text = f.print(dt); + assertEquals(text, expected); + } + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_print_appendText2arg_french_long() throws Exception { + DateTimeFormatter f = builder.appendText(MONTH_OF_YEAR, TextStyle.FULL).toFormatter(Locale.FRENCH); + LocalDateTime dt = LocalDateTime.of(2010, 1, 1, 0, 0); + String text = f.print(dt); + assertEquals(text, "janvier"); + } + + @Test(groups={"tck"}) + public void test_print_appendText2arg_french_short() throws Exception { + DateTimeFormatter f = builder.appendText(MONTH_OF_YEAR, TextStyle.SHORT).toFormatter(Locale.FRENCH); + LocalDateTime dt = LocalDateTime.of(2010, 1, 1, 0, 0); + String text = f.print(dt); + assertEquals(text, "janv."); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_appendTextMap() throws Exception { + Map map = new HashMap(); + map.put(1L, "JNY"); + map.put(2L, "FBY"); + map.put(3L, "MCH"); + map.put(4L, "APL"); + map.put(5L, "MAY"); + map.put(6L, "JUN"); + map.put(7L, "JLY"); + map.put(8L, "AGT"); + map.put(9L, "SPT"); + map.put(10L, "OBR"); + map.put(11L, "NVR"); + map.put(12L, "DBR"); + builder.appendText(MONTH_OF_YEAR, map); + DateTimeFormatter f = builder.toFormatter(); + LocalDateTime dt = LocalDateTime.of(2010, 1, 1, 0, 0); + for (Month month : Month.values()) { + assertEquals(f.print(dt.with(month)), map.get((long) month.getValue())); + } + } + + @Test(groups={"tck"}) + public void test_appendTextMap_DOM() throws Exception { + Map map = new HashMap(); + map.put(1L, "1st"); + map.put(2L, "2nd"); + map.put(3L, "3rd"); + builder.appendText(DAY_OF_MONTH, map); + DateTimeFormatter f = builder.toFormatter(); + LocalDateTime dt = LocalDateTime.of(2010, 1, 1, 0, 0); + assertEquals(f.print(dt.withDayOfMonth(1)), "1st"); + assertEquals(f.print(dt.withDayOfMonth(2)), "2nd"); + assertEquals(f.print(dt.withDayOfMonth(3)), "3rd"); + } + + @Test(groups={"tck"}) + public void test_appendTextMapIncomplete() throws Exception { + Map map = new HashMap(); + map.put(1L, "JNY"); + builder.appendText(MONTH_OF_YEAR, map); + DateTimeFormatter f = builder.toFormatter(); + LocalDateTime dt = LocalDateTime.of(2010, 2, 1, 0, 0); + assertEquals(f.print(dt), "2"); + } + +} diff --git a/jdk/test/java/time/tck/java/time/format/TCKLocalizedFieldParser.java b/jdk/test/java/time/tck/java/time/format/TCKLocalizedFieldParser.java new file mode 100644 index 00000000000..d6f5f1619bb --- /dev/null +++ b/jdk/test/java/time/tck/java/time/format/TCKLocalizedFieldParser.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2010-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.format; + +import java.time.format.*; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +import java.text.ParsePosition; +import java.time.format.DateTimeBuilder; + +import java.time.LocalDate; +import java.time.temporal.TemporalField; +import java.time.temporal.WeekFields; + +import test.java.time.format.AbstractTestPrinterParser; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test TCKLocalizedFieldParser. + */ +@Test(groups={"tck"}) +public class TCKLocalizedFieldParser extends AbstractTestPrinterParser { + + //----------------------------------------------------------------------- + @DataProvider(name="FieldPatterns") + Object[][] provider_fieldPatterns() { + return new Object[][] { + {"e", "6", 0, 1, 6}, + {"w", "3", 0, 1, 3}, + {"W", "29", 0, 2, 29}, + {"WW", "29", 0, 2, 29}, + }; + } + + @Test(dataProvider="FieldPatterns",groups={"tck"}) + public void test_parse_textField(String pattern, String text, int pos, int expectedPos, long expectedValue) { + WeekFields weekDef = WeekFields.of(locale); + TemporalField field = null; + switch(pattern.charAt(0)) { + case 'e' : + field = weekDef.dayOfWeek(); + break; + case 'w': + field = weekDef.weekOfMonth(); + break; + case 'W': + field = weekDef.weekOfYear(); + break; + default: + throw new IllegalStateException("bad format letter from pattern"); + } + ParsePosition ppos = new ParsePosition(pos); + DateTimeFormatterBuilder b + = new DateTimeFormatterBuilder().appendPattern(pattern); + DateTimeFormatter dtf = b.toFormatter(locale); + DateTimeBuilder dtb = dtf.parseToBuilder(text, ppos); + if (ppos.getErrorIndex() != -1) { + assertEquals(ppos.getErrorIndex(), expectedPos); + } else { + assertEquals(ppos.getIndex(), expectedPos, "Incorrect ending parse position"); + long value = dtb.getLong(field); + assertEquals(value, expectedValue, "Value incorrect for " + field); + } + } + + //----------------------------------------------------------------------- + @DataProvider(name="LocalDatePatterns") + Object[][] provider_patternLocalDate() { + return new Object[][] { + {"e w M y", "1 1 1 2012", 0, 10, LocalDate.of(2012, 1, 1)}, + {"e w M y", "1 2 1 2012", 0, 10, LocalDate.of(2012, 1, 8)}, + {"e w M y", "2 2 1 2012", 0, 10, LocalDate.of(2012, 1, 9)}, + {"e w M y", "3 2 1 2012", 0, 10, LocalDate.of(2012, 1, 10)}, + {"e w M y", "1 3 1 2012", 0, 10, LocalDate.of(2012, 1, 15)}, + {"e w M y", "2 3 1 2012", 0, 10, LocalDate.of(2012, 1, 16)}, + {"e w M y", "6 2 1 2012", 0, 10, LocalDate.of(2012, 1, 13)}, + {"e w M y", "6 2 7 2012", 0, 10, LocalDate.of(2012, 7, 13)}, + {"e W y", "6 29 2012", 0, 9, LocalDate.of(2012, 7, 20)}, + {"'Date: 'y-MM', day-of-week: 'e', week-of-month: 'w", + "Date: 2012-07, day-of-week: 6, week-of-month: 3", 0, 47, LocalDate.of(2012, 7, 20)}, + {"'Date: 'y', day-of-week: 'e', week-of-year: 'W", + "Date: 2012, day-of-week: 6, week-of-year: 29", 0, 44, LocalDate.of(2012, 7, 20)}, + }; + } + @Test(dataProvider="LocalDatePatterns",groups={"tck"}) + public void test_parse_textLocalDate(String pattern, String text, int pos, int expectedPos, LocalDate expectedValue) { + WeekFields weekDef = WeekFields.of(locale); + ParsePosition ppos = new ParsePosition(pos); + DateTimeFormatterBuilder b + = new DateTimeFormatterBuilder().appendPattern(pattern); + DateTimeFormatter dtf = b.toFormatter(locale); + DateTimeBuilder dtb = dtf.parseToBuilder(text, ppos); + if (ppos.getErrorIndex() != -1) { + assertEquals(ppos.getErrorIndex(), expectedPos); + } else { + assertEquals(ppos.getIndex(), expectedPos, "Incorrect ending parse position"); + dtb.resolve(); + LocalDate result = dtb.query(LocalDate::from); + assertEquals(result, expectedValue, "LocalDate incorrect for " + pattern); + } + } + +} diff --git a/jdk/test/java/time/tck/java/time/format/TCKLocalizedFieldPrinter.java b/jdk/test/java/time/tck/java/time/format/TCKLocalizedFieldPrinter.java new file mode 100644 index 00000000000..dac65b6b110 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/format/TCKLocalizedFieldPrinter.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2010-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.format; + +import java.time.format.*; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +import java.time.LocalDate; + +import test.java.time.format.AbstractTestPrinterParser; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test LocalizedFieldPrinterParser. + */ +@Test(groups={"tck"}) +public class TCKLocalizedFieldPrinter extends AbstractTestPrinterParser { + + //----------------------------------------------------------------------- + @DataProvider(name="Patterns") + Object[][] provider_pad() { + return new Object[][] { + {"e", "6"}, + {"w", "3"}, + {"W", "29"}, + {"WW", "29"}, + {"'Date: 'y-MM-d', week-of-month: 'w', week-of-year: 'W", + "Date: 2012-07-20, week-of-month: 3, week-of-year: 29"}, + + }; + } + + //----------------------------------------------------------------------- + @Test(dataProvider="Patterns",groups={"tck"}) + public void test_localizedDayOfWeek(String pattern, String expected) { + DateTimeFormatterBuilder b + = new DateTimeFormatterBuilder().appendPattern(pattern); + LocalDate date = LocalDate.of(2012, 7, 20); + + String result = b.toFormatter(locale).print(date); + assertEquals(result, expected, "Wrong output for pattern '" + pattern + "'."); + } + +} diff --git a/jdk/test/java/time/tck/java/time/temporal/TCKDateTimeAdjusters.java b/jdk/test/java/time/tck/java/time/temporal/TCKDateTimeAdjusters.java new file mode 100644 index 00000000000..231a16b5674 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/temporal/TCKDateTimeAdjusters.java @@ -0,0 +1,601 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.temporal; + +import java.time.temporal.*; + +import static java.time.DayOfWeek.MONDAY; +import static java.time.DayOfWeek.TUESDAY; +import static java.time.Month.DECEMBER; +import static java.time.Month.JANUARY; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.Month; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test Adjusters. + */ +@Test +public class TCKDateTimeAdjusters { + + //----------------------------------------------------------------------- + // firstDayOfMonth() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_firstDayOfMonth() { + assertNotNull(Adjusters.firstDayOfMonth()); + } + + @Test(groups={"tck"}) + public void test_firstDayOfMonth_nonLeap() { + for (Month month : Month.values()) { + for (int i = 1; i <= month.length(false); i++) { + LocalDate date = date(2007, month, i); + LocalDate test = (LocalDate) Adjusters.firstDayOfMonth().adjustInto(date); + assertEquals(test.getYear(), 2007); + assertEquals(test.getMonth(), month); + assertEquals(test.getDayOfMonth(), 1); + } + } + } + + @Test(groups={"tck"}) + public void test_firstDayOfMonth_leap() { + for (Month month : Month.values()) { + for (int i = 1; i <= month.length(true); i++) { + LocalDate date = date(2008, month, i); + LocalDate test = (LocalDate) Adjusters.firstDayOfMonth().adjustInto(date); + assertEquals(test.getYear(), 2008); + assertEquals(test.getMonth(), month); + assertEquals(test.getDayOfMonth(), 1); + } + } + } + + //----------------------------------------------------------------------- + // lastDayOfMonth() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_lastDayOfMonth() { + assertNotNull(Adjusters.lastDayOfMonth()); + } + + @Test(groups={"tck"}) + public void test_lastDayOfMonth_nonLeap() { + for (Month month : Month.values()) { + for (int i = 1; i <= month.length(false); i++) { + LocalDate date = date(2007, month, i); + LocalDate test = (LocalDate) Adjusters.lastDayOfMonth().adjustInto(date); + assertEquals(test.getYear(), 2007); + assertEquals(test.getMonth(), month); + assertEquals(test.getDayOfMonth(), month.length(false)); + } + } + } + + @Test(groups={"tck"}) + public void test_lastDayOfMonth_leap() { + for (Month month : Month.values()) { + for (int i = 1; i <= month.length(true); i++) { + LocalDate date = date(2008, month, i); + LocalDate test = (LocalDate) Adjusters.lastDayOfMonth().adjustInto(date); + assertEquals(test.getYear(), 2008); + assertEquals(test.getMonth(), month); + assertEquals(test.getDayOfMonth(), month.length(true)); + } + } + } + + //----------------------------------------------------------------------- + // firstDayOfNextMonth() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_firstDayOfNextMonth() { + assertNotNull(Adjusters.firstDayOfNextMonth()); + } + + @Test(groups={"tck"}) + public void test_firstDayOfNextMonth_nonLeap() { + for (Month month : Month.values()) { + for (int i = 1; i <= month.length(false); i++) { + LocalDate date = date(2007, month, i); + LocalDate test = (LocalDate) Adjusters.firstDayOfNextMonth().adjustInto(date); + assertEquals(test.getYear(), month == DECEMBER ? 2008 : 2007); + assertEquals(test.getMonth(), month.plus(1)); + assertEquals(test.getDayOfMonth(), 1); + } + } + } + + @Test(groups={"tck"}) + public void test_firstDayOfNextMonth_leap() { + for (Month month : Month.values()) { + for (int i = 1; i <= month.length(true); i++) { + LocalDate date = date(2008, month, i); + LocalDate test = (LocalDate) Adjusters.firstDayOfNextMonth().adjustInto(date); + assertEquals(test.getYear(), month == DECEMBER ? 2009 : 2008); + assertEquals(test.getMonth(), month.plus(1)); + assertEquals(test.getDayOfMonth(), 1); + } + } + } + + //----------------------------------------------------------------------- + // firstDayOfYear() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_firstDayOfYear() { + assertNotNull(Adjusters.firstDayOfYear()); + } + + @Test(groups={"tck"}) + public void test_firstDayOfYear_nonLeap() { + for (Month month : Month.values()) { + for (int i = 1; i <= month.length(false); i++) { + LocalDate date = date(2007, month, i); + LocalDate test = (LocalDate) Adjusters.firstDayOfYear().adjustInto(date); + assertEquals(test.getYear(), 2007); + assertEquals(test.getMonth(), Month.JANUARY); + assertEquals(test.getDayOfMonth(), 1); + } + } + } + + @Test(groups={"tck"}) + public void test_firstDayOfYear_leap() { + for (Month month : Month.values()) { + for (int i = 1; i <= month.length(true); i++) { + LocalDate date = date(2008, month, i); + LocalDate test = (LocalDate) Adjusters.firstDayOfYear().adjustInto(date); + assertEquals(test.getYear(), 2008); + assertEquals(test.getMonth(), Month.JANUARY); + assertEquals(test.getDayOfMonth(), 1); + } + } + } + + //----------------------------------------------------------------------- + // lastDayOfYear() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_lastDayOfYear() { + assertNotNull(Adjusters.lastDayOfYear()); + } + + @Test(groups={"tck"}) + public void test_lastDayOfYear_nonLeap() { + for (Month month : Month.values()) { + for (int i = 1; i <= month.length(false); i++) { + LocalDate date = date(2007, month, i); + LocalDate test = (LocalDate) Adjusters.lastDayOfYear().adjustInto(date); + assertEquals(test.getYear(), 2007); + assertEquals(test.getMonth(), Month.DECEMBER); + assertEquals(test.getDayOfMonth(), 31); + } + } + } + + @Test(groups={"tck"}) + public void test_lastDayOfYear_leap() { + for (Month month : Month.values()) { + for (int i = 1; i <= month.length(true); i++) { + LocalDate date = date(2008, month, i); + LocalDate test = (LocalDate) Adjusters.lastDayOfYear().adjustInto(date); + assertEquals(test.getYear(), 2008); + assertEquals(test.getMonth(), Month.DECEMBER); + assertEquals(test.getDayOfMonth(), 31); + } + } + } + + //----------------------------------------------------------------------- + // firstDayOfNextYear() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_firstDayOfNextYear() { + assertNotNull(Adjusters.firstDayOfNextYear()); + } + + @Test(groups={"tck"}) + public void test_firstDayOfNextYear_nonLeap() { + for (Month month : Month.values()) { + for (int i = 1; i <= month.length(false); i++) { + LocalDate date = date(2007, month, i); + LocalDate test = (LocalDate) Adjusters.firstDayOfNextYear().adjustInto(date); + assertEquals(test.getYear(), 2008); + assertEquals(test.getMonth(), JANUARY); + assertEquals(test.getDayOfMonth(), 1); + } + } + } + + @Test(groups={"tck"}) + public void test_firstDayOfNextYear_leap() { + for (Month month : Month.values()) { + for (int i = 1; i <= month.length(true); i++) { + LocalDate date = date(2008, month, i); + LocalDate test = (LocalDate) Adjusters.firstDayOfNextYear().adjustInto(date); + assertEquals(test.getYear(), 2009); + assertEquals(test.getMonth(), JANUARY); + assertEquals(test.getDayOfMonth(), 1); + } + } + } + + //----------------------------------------------------------------------- + // dayOfWeekInMonth() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_dayOfWeekInMonth() { + assertNotNull(Adjusters.dayOfWeekInMonth(1, MONDAY)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_dayOfWeekInMonth_nullDayOfWeek() { + Adjusters.dayOfWeekInMonth(1, null); + } + + @DataProvider(name = "dayOfWeekInMonth_positive") + Object[][] data_dayOfWeekInMonth_positive() { + return new Object[][] { + {2011, 1, TUESDAY, date(2011, 1, 4)}, + {2011, 2, TUESDAY, date(2011, 2, 1)}, + {2011, 3, TUESDAY, date(2011, 3, 1)}, + {2011, 4, TUESDAY, date(2011, 4, 5)}, + {2011, 5, TUESDAY, date(2011, 5, 3)}, + {2011, 6, TUESDAY, date(2011, 6, 7)}, + {2011, 7, TUESDAY, date(2011, 7, 5)}, + {2011, 8, TUESDAY, date(2011, 8, 2)}, + {2011, 9, TUESDAY, date(2011, 9, 6)}, + {2011, 10, TUESDAY, date(2011, 10, 4)}, + {2011, 11, TUESDAY, date(2011, 11, 1)}, + {2011, 12, TUESDAY, date(2011, 12, 6)}, + }; + } + + @Test(groups={"tck"}, dataProvider = "dayOfWeekInMonth_positive") + public void test_dayOfWeekInMonth_positive(int year, int month, DayOfWeek dow, LocalDate expected) { + for (int ordinal = 1; ordinal <= 5; ordinal++) { + for (int day = 1; day <= Month.of(month).length(false); day++) { + LocalDate date = date(year, month, day); + LocalDate test = (LocalDate) Adjusters.dayOfWeekInMonth(ordinal, dow).adjustInto(date); + assertEquals(test, expected.plusWeeks(ordinal - 1)); + } + } + } + + @DataProvider(name = "dayOfWeekInMonth_zero") + Object[][] data_dayOfWeekInMonth_zero() { + return new Object[][] { + {2011, 1, TUESDAY, date(2010, 12, 28)}, + {2011, 2, TUESDAY, date(2011, 1, 25)}, + {2011, 3, TUESDAY, date(2011, 2, 22)}, + {2011, 4, TUESDAY, date(2011, 3, 29)}, + {2011, 5, TUESDAY, date(2011, 4, 26)}, + {2011, 6, TUESDAY, date(2011, 5, 31)}, + {2011, 7, TUESDAY, date(2011, 6, 28)}, + {2011, 8, TUESDAY, date(2011, 7, 26)}, + {2011, 9, TUESDAY, date(2011, 8, 30)}, + {2011, 10, TUESDAY, date(2011, 9, 27)}, + {2011, 11, TUESDAY, date(2011, 10, 25)}, + {2011, 12, TUESDAY, date(2011, 11, 29)}, + }; + } + + @Test(groups={"tck"}, dataProvider = "dayOfWeekInMonth_zero") + public void test_dayOfWeekInMonth_zero(int year, int month, DayOfWeek dow, LocalDate expected) { + for (int day = 1; day <= Month.of(month).length(false); day++) { + LocalDate date = date(year, month, day); + LocalDate test = (LocalDate) Adjusters.dayOfWeekInMonth(0, dow).adjustInto(date); + assertEquals(test, expected); + } + } + + @DataProvider(name = "dayOfWeekInMonth_negative") + Object[][] data_dayOfWeekInMonth_negative() { + return new Object[][] { + {2011, 1, TUESDAY, date(2011, 1, 25)}, + {2011, 2, TUESDAY, date(2011, 2, 22)}, + {2011, 3, TUESDAY, date(2011, 3, 29)}, + {2011, 4, TUESDAY, date(2011, 4, 26)}, + {2011, 5, TUESDAY, date(2011, 5, 31)}, + {2011, 6, TUESDAY, date(2011, 6, 28)}, + {2011, 7, TUESDAY, date(2011, 7, 26)}, + {2011, 8, TUESDAY, date(2011, 8, 30)}, + {2011, 9, TUESDAY, date(2011, 9, 27)}, + {2011, 10, TUESDAY, date(2011, 10, 25)}, + {2011, 11, TUESDAY, date(2011, 11, 29)}, + {2011, 12, TUESDAY, date(2011, 12, 27)}, + }; + } + + @Test(groups={"tck"}, dataProvider = "dayOfWeekInMonth_negative") + public void test_dayOfWeekInMonth_negative(int year, int month, DayOfWeek dow, LocalDate expected) { + for (int ordinal = 0; ordinal < 5; ordinal++) { + for (int day = 1; day <= Month.of(month).length(false); day++) { + LocalDate date = date(year, month, day); + LocalDate test = (LocalDate) Adjusters.dayOfWeekInMonth(-1 - ordinal, dow).adjustInto(date); + assertEquals(test, expected.minusWeeks(ordinal)); + } + } + } + + //----------------------------------------------------------------------- + // firstInMonth() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_firstInMonth() { + assertNotNull(Adjusters.firstInMonth(MONDAY)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_firstInMonth_nullDayOfWeek() { + Adjusters.firstInMonth(null); + } + + @Test(groups={"tck"}, dataProvider = "dayOfWeekInMonth_positive") + public void test_firstInMonth(int year, int month, DayOfWeek dow, LocalDate expected) { + for (int day = 1; day <= Month.of(month).length(false); day++) { + LocalDate date = date(year, month, day); + LocalDate test = (LocalDate) Adjusters.firstInMonth(dow).adjustInto(date); + assertEquals(test, expected, "day-of-month=" + day); + } + } + + //----------------------------------------------------------------------- + // lastInMonth() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_lastInMonth() { + assertNotNull(Adjusters.lastInMonth(MONDAY)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_lastInMonth_nullDayOfWeek() { + Adjusters.lastInMonth(null); + } + + @Test(groups={"tck"}, dataProvider = "dayOfWeekInMonth_negative") + public void test_lastInMonth(int year, int month, DayOfWeek dow, LocalDate expected) { + for (int day = 1; day <= Month.of(month).length(false); day++) { + LocalDate date = date(year, month, day); + LocalDate test = (LocalDate) Adjusters.lastInMonth(dow).adjustInto(date); + assertEquals(test, expected, "day-of-month=" + day); + } + } + + //----------------------------------------------------------------------- + // next() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_next() { + assertNotNull(Adjusters.next(MONDAY)); + } + + @Test(expectedExceptions = NullPointerException.class, groups={"tck"}) + public void factory_next_nullDayOfWeek() { + Adjusters.next(null); + } + + @Test(groups={"tck"}) + public void test_next() { + for (Month month : Month.values()) { + for (int i = 1; i <= month.length(false); i++) { + LocalDate date = date(2007, month, i); + + for (DayOfWeek dow : DayOfWeek.values()) { + LocalDate test = (LocalDate) Adjusters.next(dow).adjustInto(date); + + assertSame(test.getDayOfWeek(), dow, date + " " + test); + + if (test.getYear() == 2007) { + int dayDiff = test.getDayOfYear() - date.getDayOfYear(); + assertTrue(dayDiff > 0 && dayDiff < 8); + } else { + assertSame(month, Month.DECEMBER); + assertTrue(date.getDayOfMonth() > 24); + assertEquals(test.getYear(), 2008); + assertSame(test.getMonth(), Month.JANUARY); + assertTrue(test.getDayOfMonth() < 8); + } + } + } + } + } + + //----------------------------------------------------------------------- + // nextOrSame() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_nextOrCurrent() { + assertNotNull(Adjusters.nextOrSame(MONDAY)); + } + + @Test(expectedExceptions = NullPointerException.class, groups={"tck"}) + public void factory_nextOrCurrent_nullDayOfWeek() { + Adjusters.nextOrSame(null); + } + + @Test(groups={"tck"}) + public void test_nextOrCurrent() { + for (Month month : Month.values()) { + for (int i = 1; i <= month.length(false); i++) { + LocalDate date = date(2007, month, i); + + for (DayOfWeek dow : DayOfWeek.values()) { + LocalDate test = (LocalDate) Adjusters.nextOrSame(dow).adjustInto(date); + + assertSame(test.getDayOfWeek(), dow); + + if (test.getYear() == 2007) { + int dayDiff = test.getDayOfYear() - date.getDayOfYear(); + assertTrue(dayDiff < 8); + assertEquals(date.equals(test), date.getDayOfWeek() == dow); + } else { + assertFalse(date.getDayOfWeek() == dow); + assertSame(month, Month.DECEMBER); + assertTrue(date.getDayOfMonth() > 24); + assertEquals(test.getYear(), 2008); + assertSame(test.getMonth(), Month.JANUARY); + assertTrue(test.getDayOfMonth() < 8); + } + } + } + } + } + + //----------------------------------------------------------------------- + // previous() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_previous() { + assertNotNull(Adjusters.previous(MONDAY)); + } + + @Test(expectedExceptions = NullPointerException.class, groups={"tck"}) + public void factory_previous_nullDayOfWeek() { + Adjusters.previous(null); + } + + @Test(groups={"tck"}) + public void test_previous() { + for (Month month : Month.values()) { + for (int i = 1; i <= month.length(false); i++) { + LocalDate date = date(2007, month, i); + + for (DayOfWeek dow : DayOfWeek.values()) { + LocalDate test = (LocalDate) Adjusters.previous(dow).adjustInto(date); + + assertSame(test.getDayOfWeek(), dow, date + " " + test); + + if (test.getYear() == 2007) { + int dayDiff = test.getDayOfYear() - date.getDayOfYear(); + assertTrue(dayDiff < 0 && dayDiff > -8, dayDiff + " " + test); + } else { + assertSame(month, Month.JANUARY); + assertTrue(date.getDayOfMonth() < 8); + assertEquals(test.getYear(), 2006); + assertSame(test.getMonth(), Month.DECEMBER); + assertTrue(test.getDayOfMonth() > 24); + } + } + } + } + } + + //----------------------------------------------------------------------- + // previousOrSame() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_previousOrCurrent() { + assertNotNull(Adjusters.previousOrSame(MONDAY)); + } + + @Test(expectedExceptions = NullPointerException.class, groups={"tck"}) + public void factory_previousOrCurrent_nullDayOfWeek() { + Adjusters.previousOrSame(null); + } + + @Test(groups={"tck"}) + public void test_previousOrCurrent() { + for (Month month : Month.values()) { + for (int i = 1; i <= month.length(false); i++) { + LocalDate date = date(2007, month, i); + + for (DayOfWeek dow : DayOfWeek.values()) { + LocalDate test = (LocalDate) Adjusters.previousOrSame(dow).adjustInto(date); + + assertSame(test.getDayOfWeek(), dow); + + if (test.getYear() == 2007) { + int dayDiff = test.getDayOfYear() - date.getDayOfYear(); + assertTrue(dayDiff <= 0 && dayDiff > -7); + assertEquals(date.equals(test), date.getDayOfWeek() == dow); + } else { + assertFalse(date.getDayOfWeek() == dow); + assertSame(month, Month.JANUARY); + assertTrue(date.getDayOfMonth() < 7); + assertEquals(test.getYear(), 2006); + assertSame(test.getMonth(), Month.DECEMBER); + assertTrue(test.getDayOfMonth() > 25); + } + } + } + } + } + + private LocalDate date(int year, Month month, int day) { + return LocalDate.of(year, month, day); + } + + private LocalDate date(int year, int month, int day) { + return LocalDate.of(year, month, day); + } + +} diff --git a/jdk/test/java/time/tck/java/time/temporal/TCKISOFields.java b/jdk/test/java/time/tck/java/time/temporal/TCKISOFields.java new file mode 100644 index 00000000000..c3acbdb6f82 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/temporal/TCKISOFields.java @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.temporal; + +import java.time.format.DateTimeBuilder; +import java.time.temporal.*; + +import static java.time.DayOfWeek.FRIDAY; +import static java.time.DayOfWeek.MONDAY; +import static java.time.DayOfWeek.SATURDAY; +import static java.time.DayOfWeek.SUNDAY; +import static java.time.DayOfWeek.THURSDAY; +import static java.time.DayOfWeek.TUESDAY; +import static java.time.DayOfWeek.WEDNESDAY; +import static java.time.temporal.ChronoField.DAY_OF_WEEK; +import static java.time.temporal.ChronoField.YEAR; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.time.DayOfWeek; +import java.time.LocalDate; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test. + */ +@Test(groups={"tck"}) +public class TCKISOFields { + + @DataProvider(name="quarter") + Object[][] data_quarter() { + return new Object[][] { + {LocalDate.of(1969, 12, 29), 90, 4}, + {LocalDate.of(1969, 12, 30), 91, 4}, + {LocalDate.of(1969, 12, 31), 92, 4}, + + {LocalDate.of(1970, 1, 1), 1, 1}, + {LocalDate.of(1970, 1, 2), 2, 1}, + {LocalDate.of(1970, 2, 28), 59, 1}, + {LocalDate.of(1970, 3, 1), 60, 1}, + {LocalDate.of(1970, 3, 31), 90, 1}, + + {LocalDate.of(1970, 4, 1), 1, 2}, + {LocalDate.of(1970, 6, 30), 91, 2}, + + {LocalDate.of(1970, 7, 1), 1, 3}, + {LocalDate.of(1970, 9, 30), 92, 3}, + + {LocalDate.of(1970, 10, 1), 1, 4}, + {LocalDate.of(1970, 12, 31), 92, 4}, + + {LocalDate.of(1972, 2, 28), 59, 1}, + {LocalDate.of(1972, 2, 29), 60, 1}, + {LocalDate.of(1972, 3, 1), 61, 1}, + {LocalDate.of(1972, 3, 31), 91, 1}, + }; + } + + //----------------------------------------------------------------------- + // DAY_OF_QUARTER + //----------------------------------------------------------------------- + @Test(dataProvider="quarter") + public void test_DOQ(LocalDate date, int doq, int qoy) { + assertEquals(ISOFields.DAY_OF_QUARTER.doGet(date), doq); + assertEquals(date.get(ISOFields.DAY_OF_QUARTER), doq); + } + + //----------------------------------------------------------------------- + // QUARTER_OF_YEAR + //----------------------------------------------------------------------- + @Test(dataProvider="quarter") + public void test_QOY(LocalDate date, int doq, int qoy) { + assertEquals(ISOFields.QUARTER_OF_YEAR.doGet(date), qoy); + assertEquals(date.get(ISOFields.QUARTER_OF_YEAR), qoy); + } + + //----------------------------------------------------------------------- + // builder + //----------------------------------------------------------------------- + @Test(dataProvider="quarter") + public void test_builder_quarters(LocalDate date, int doq, int qoy) { + DateTimeBuilder builder = new DateTimeBuilder(); + builder.addFieldValue(ISOFields.DAY_OF_QUARTER, doq); + builder.addFieldValue(ISOFields.QUARTER_OF_YEAR, qoy); + builder.addFieldValue(YEAR, date.getYear()); + builder.resolve(); + assertEquals(builder.query(LocalDate::from), date); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @DataProvider(name="week") + Object[][] data_week() { + return new Object[][] { + {LocalDate.of(1969, 12, 29), MONDAY, 1, 1970}, + {LocalDate.of(2012, 12, 23), SUNDAY, 51, 2012}, + {LocalDate.of(2012, 12, 24), MONDAY, 52, 2012}, + {LocalDate.of(2012, 12, 27), THURSDAY, 52, 2012}, + {LocalDate.of(2012, 12, 28), FRIDAY, 52, 2012}, + {LocalDate.of(2012, 12, 29), SATURDAY, 52, 2012}, + {LocalDate.of(2012, 12, 30), SUNDAY, 52, 2012}, + {LocalDate.of(2012, 12, 31), MONDAY, 1, 2013}, + {LocalDate.of(2013, 1, 1), TUESDAY, 1, 2013}, + {LocalDate.of(2013, 1, 2), WEDNESDAY, 1, 2013}, + {LocalDate.of(2013, 1, 6), SUNDAY, 1, 2013}, + {LocalDate.of(2013, 1, 7), MONDAY, 2, 2013}, + }; + } + + //----------------------------------------------------------------------- + // WEEK_OF_WEEK_BASED_YEAR + //----------------------------------------------------------------------- + @Test(dataProvider="week") + public void test_WOWBY(LocalDate date, DayOfWeek dow, int week, int wby) { + assertEquals(date.getDayOfWeek(), dow); + assertEquals(ISOFields.WEEK_OF_WEEK_BASED_YEAR.doGet(date), week); + assertEquals(date.get(ISOFields.WEEK_OF_WEEK_BASED_YEAR), week); + } + + //----------------------------------------------------------------------- + // WEEK_BASED_YEAR + //----------------------------------------------------------------------- + @Test(dataProvider="week") + public void test_WBY(LocalDate date, DayOfWeek dow, int week, int wby) { + assertEquals(date.getDayOfWeek(), dow); + assertEquals(ISOFields.WEEK_BASED_YEAR.doGet(date), wby); + assertEquals(date.get(ISOFields.WEEK_BASED_YEAR), wby); + } + + //----------------------------------------------------------------------- + // builder + //----------------------------------------------------------------------- + @Test(dataProvider="week") + public void test_builder_weeks(LocalDate date, DayOfWeek dow, int week, int wby) { + DateTimeBuilder builder = new DateTimeBuilder(); + builder.addFieldValue(ISOFields.WEEK_BASED_YEAR, wby); + builder.addFieldValue(ISOFields.WEEK_OF_WEEK_BASED_YEAR, week); + builder.addFieldValue(DAY_OF_WEEK, dow.getValue()); + builder.resolve(); + assertEquals(builder.query(LocalDate::from), date); + } + + //----------------------------------------------------------------------- + public void test_loop() { + // loop round at least one 400 year cycle, including before 1970 + LocalDate date = LocalDate.of(1960, 1, 5); // Tuseday of week 1 1960 + int year = 1960; + int wby = 1960; + int weekLen = 52; + int week = 1; + while (date.getYear() < 2400) { + DayOfWeek loopDow = date.getDayOfWeek(); + if (date.getYear() != year) { + year = date.getYear(); + } + if (loopDow == MONDAY) { + week++; + if ((week == 53 && weekLen == 52) || week == 54) { + week = 1; + LocalDate firstDayOfWeekBasedYear = date.plusDays(14).withDayOfYear(1); + DayOfWeek firstDay = firstDayOfWeekBasedYear.getDayOfWeek(); + weekLen = (firstDay == THURSDAY || (firstDay == WEDNESDAY && firstDayOfWeekBasedYear.isLeapYear()) ? 53 : 52); + wby++; + } + } + assertEquals(ISOFields.WEEK_OF_WEEK_BASED_YEAR.doRange(date), ValueRange.of(1, weekLen), "Failed on " + date + " " + date.getDayOfWeek()); + assertEquals(ISOFields.WEEK_OF_WEEK_BASED_YEAR.doGet(date), week, "Failed on " + date + " " + date.getDayOfWeek()); + assertEquals(date.get(ISOFields.WEEK_OF_WEEK_BASED_YEAR), week, "Failed on " + date + " " + date.getDayOfWeek()); + assertEquals(ISOFields.WEEK_BASED_YEAR.doGet(date), wby, "Failed on " + date + " " + date.getDayOfWeek()); + assertEquals(date.get(ISOFields.WEEK_BASED_YEAR), wby, "Failed on " + date + " " + date.getDayOfWeek()); + date = date.plusDays(1); + } + } + + // TODO: more tests +} diff --git a/jdk/test/java/time/tck/java/time/temporal/TCKJulianFields.java b/jdk/test/java/time/tck/java/time/temporal/TCKJulianFields.java new file mode 100644 index 00000000000..81e8109c329 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/temporal/TCKJulianFields.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.temporal; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertSame; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import java.time.LocalDate; + +import java.time.temporal.*; + + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test. + */ +@Test +public class TCKJulianFields { + + private static final LocalDate JAN01_1970 = LocalDate.of(1970, 1, 1); + private static final LocalDate DEC31_1969 = LocalDate.of(1969, 12, 31); + private static final LocalDate NOV12_1945 = LocalDate.of(1945, 11, 12); + private static final LocalDate JAN01_0001 = LocalDate.of(1, 1, 1); + + @BeforeMethod + public void setUp() { + } + + //----------------------------------------------------------------------- + @DataProvider(name="julian_fields") + Object[][] julian_samples() { + return new Object[][] { + {JulianFields.JULIAN_DAY}, + {JulianFields.MODIFIED_JULIAN_DAY}, + {JulianFields.RATA_DIE}, + }; + } + + @DataProvider(name="samples") + Object[][] data_samples() { + return new Object[][] { + {ChronoField.EPOCH_DAY, JAN01_1970, 0L}, + {JulianFields.JULIAN_DAY, JAN01_1970, 2400001L + 40587L}, + {JulianFields.MODIFIED_JULIAN_DAY, JAN01_1970, 40587L}, + {JulianFields.RATA_DIE, JAN01_1970, 710347L + (40587L - 31771L)}, + + {ChronoField.EPOCH_DAY, DEC31_1969, -1L}, + {JulianFields.JULIAN_DAY, DEC31_1969, 2400001L + 40586L}, + {JulianFields.MODIFIED_JULIAN_DAY, DEC31_1969, 40586L}, + {JulianFields.RATA_DIE, DEC31_1969, 710347L + (40586L - 31771L)}, + + {ChronoField.EPOCH_DAY, NOV12_1945, (-24 * 365 - 6) - 31 - 30 + 11}, + {JulianFields.JULIAN_DAY, NOV12_1945, 2431772L}, + {JulianFields.MODIFIED_JULIAN_DAY, NOV12_1945, 31771L}, + {JulianFields.RATA_DIE, NOV12_1945, 710347L}, + + {ChronoField.EPOCH_DAY, JAN01_0001, (-24 * 365 - 6) - 31 - 30 + 11 - 710346L}, + {JulianFields.JULIAN_DAY, JAN01_0001, 2431772L - 710346L}, + {JulianFields.MODIFIED_JULIAN_DAY, JAN01_0001, 31771L - 710346L}, + {JulianFields.RATA_DIE, JAN01_0001, 1}, + }; + } + + @Test(dataProvider="samples", groups={"tck"}) + public void test_samples_get(TemporalField field, LocalDate date, long expected) { + assertEquals(date.getLong(field), expected); + } + + @Test(dataProvider="samples", groups={"tck"}) + public void test_samples_set(TemporalField field, LocalDate date, long value) { + assertEquals(field.doWith(LocalDate.MAX, value), date); + assertEquals(field.doWith(LocalDate.MIN, value), date); + assertEquals(field.doWith(JAN01_1970, value), date); + assertEquals(field.doWith(DEC31_1969, value), date); + assertEquals(field.doWith(NOV12_1945, value), date); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toString() { + assertEquals(JulianFields.JULIAN_DAY.toString(), "JulianDay"); + assertEquals(JulianFields.MODIFIED_JULIAN_DAY.toString(), "ModifiedJulianDay"); + assertEquals(JulianFields.RATA_DIE.toString(), "RataDie"); + } + + @Test(groups = {"tck"},dataProvider="julian_fields") + public void test_JulianFieldsSingleton(TemporalField field) throws IOException, ClassNotFoundException { + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos)) { + oos.writeObject(field); + + ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream( + baos.toByteArray())); + TemporalField result = (TemporalField)ois.readObject(); + assertSame(result, field, "Deserialized object same as serialized."); + } + // Exceptions will be handled as failures by TestNG + } + +} diff --git a/jdk/test/java/time/tck/java/time/temporal/TCKMonthDay.java b/jdk/test/java/time/tck/java/time/temporal/TCKMonthDay.java new file mode 100644 index 00000000000..774777cab03 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/temporal/TCKMonthDay.java @@ -0,0 +1,756 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.temporal; + +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import java.time.Clock; +import java.time.DateTimeException; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Month; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatters; +import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoField; +import java.time.temporal.JulianFields; +import java.time.temporal.MonthDay; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalField; +import java.time.temporal.YearMonth; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import tck.java.time.AbstractDateTimeTest; + +/** + * Test MonthDay. + */ +@Test +public class TCKMonthDay extends AbstractDateTimeTest { + + private MonthDay TEST_07_15; + + @BeforeMethod(groups={"tck","implementation"}) + public void setUp() { + TEST_07_15 = MonthDay.of(7, 15); + } + + //----------------------------------------------------------------------- + @Override + protected List samples() { + TemporalAccessor[] array = {TEST_07_15, }; + return Arrays.asList(array); + } + + @Override + protected List validFields() { + TemporalField[] array = { + DAY_OF_MONTH, + MONTH_OF_YEAR, + }; + return Arrays.asList(array); + } + + @Override + protected List invalidFields() { + List list = new ArrayList<>(Arrays.asList(ChronoField.values())); + list.removeAll(validFields()); + list.add(JulianFields.JULIAN_DAY); + list.add(JulianFields.MODIFIED_JULIAN_DAY); + list.add(JulianFields.RATA_DIE); + return list; + } + + //----------------------------------------------------------------------- + @Test + public void test_serialization() throws ClassNotFoundException, IOException { + assertSerializable(TEST_07_15); + } + + @Test + public void test_serialization_format() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baos) ) { + dos.writeByte(6); + dos.writeByte(9); + dos.writeByte(16); + } + byte[] bytes = baos.toByteArray(); + assertSerializedBySer(MonthDay.of(9, 16), bytes); + } + + //----------------------------------------------------------------------- + void check(MonthDay test, int m, int d) { + assertEquals(test.getMonth().getValue(), m); + assertEquals(test.getDayOfMonth(), d); + } + + //----------------------------------------------------------------------- + // now() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void now() { + MonthDay expected = MonthDay.now(Clock.systemDefaultZone()); + MonthDay test = MonthDay.now(); + for (int i = 0; i < 100; i++) { + if (expected.equals(test)) { + return; + } + expected = MonthDay.now(Clock.systemDefaultZone()); + test = MonthDay.now(); + } + assertEquals(test, expected); + } + + //----------------------------------------------------------------------- + // now(ZoneId) + //----------------------------------------------------------------------- + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void now_ZoneId_nullZoneId() { + MonthDay.now((ZoneId) null); + } + + @Test(groups={"tck"}) + public void now_ZoneId() { + ZoneId zone = ZoneId.of("UTC+01:02:03"); + MonthDay expected = MonthDay.now(Clock.system(zone)); + MonthDay test = MonthDay.now(zone); + for (int i = 0; i < 100; i++) { + if (expected.equals(test)) { + return; + } + expected = MonthDay.now(Clock.system(zone)); + test = MonthDay.now(zone); + } + assertEquals(test, expected); + } + + //----------------------------------------------------------------------- + // now(Clock) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void now_Clock() { + Instant instant = LocalDateTime.of(2010, 12, 31, 0, 0).toInstant(ZoneOffset.UTC); + Clock clock = Clock.fixed(instant, ZoneOffset.UTC); + MonthDay test = MonthDay.now(clock); + assertEquals(test.getMonth(), Month.DECEMBER); + assertEquals(test.getDayOfMonth(), 31); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void now_Clock_nullClock() { + MonthDay.now((Clock) null); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_intMonth() { + assertEquals(TEST_07_15, MonthDay.of(Month.JULY, 15)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_intMonth_dayTooLow() { + MonthDay.of(Month.JANUARY, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_intMonth_dayTooHigh() { + MonthDay.of(Month.JANUARY, 32); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_intMonth_nullMonth() { + MonthDay.of(null, 15); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_ints() { + check(TEST_07_15, 7, 15); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_ints_dayTooLow() { + MonthDay.of(1, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_ints_dayTooHigh() { + MonthDay.of(1, 32); + } + + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_ints_monthTooLow() { + MonthDay.of(0, 1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_ints_monthTooHigh() { + MonthDay.of(13, 1); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_factory_CalendricalObject() { + assertEquals(MonthDay.from(LocalDate.of(2007, 7, 15)), TEST_07_15); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_CalendricalObject_invalid_noDerive() { + MonthDay.from(LocalTime.of(12, 30)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_factory_CalendricalObject_null() { + MonthDay.from((TemporalAccessor) null); + } + + //----------------------------------------------------------------------- + // parse() + //----------------------------------------------------------------------- + @DataProvider(name="goodParseData") + Object[][] provider_goodParseData() { + return new Object[][] { + {"--01-01", MonthDay.of(1, 1)}, + {"--01-31", MonthDay.of(1, 31)}, + {"--02-01", MonthDay.of(2, 1)}, + {"--02-29", MonthDay.of(2, 29)}, + {"--03-01", MonthDay.of(3, 1)}, + {"--03-31", MonthDay.of(3, 31)}, + {"--04-01", MonthDay.of(4, 1)}, + {"--04-30", MonthDay.of(4, 30)}, + {"--05-01", MonthDay.of(5, 1)}, + {"--05-31", MonthDay.of(5, 31)}, + {"--06-01", MonthDay.of(6, 1)}, + {"--06-30", MonthDay.of(6, 30)}, + {"--07-01", MonthDay.of(7, 1)}, + {"--07-31", MonthDay.of(7, 31)}, + {"--08-01", MonthDay.of(8, 1)}, + {"--08-31", MonthDay.of(8, 31)}, + {"--09-01", MonthDay.of(9, 1)}, + {"--09-30", MonthDay.of(9, 30)}, + {"--10-01", MonthDay.of(10, 1)}, + {"--10-31", MonthDay.of(10, 31)}, + {"--11-01", MonthDay.of(11, 1)}, + {"--11-30", MonthDay.of(11, 30)}, + {"--12-01", MonthDay.of(12, 1)}, + {"--12-31", MonthDay.of(12, 31)}, + }; + } + + @Test(dataProvider="goodParseData", groups={"tck"}) + public void factory_parse_success(String text, MonthDay expected) { + MonthDay monthDay = MonthDay.parse(text); + assertEquals(monthDay, expected); + } + + //----------------------------------------------------------------------- + @DataProvider(name="badParseData") + Object[][] provider_badParseData() { + return new Object[][] { + {"", 0}, + {"-00", 0}, + {"--FEB-23", 2}, + {"--01-0", 5}, + {"--01-3A", 5}, + }; + } + + @Test(dataProvider="badParseData", expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_fail(String text, int pos) { + try { + MonthDay.parse(text); + fail(String.format("Parse should have failed for %s at position %d", text, pos)); + } + catch (DateTimeParseException ex) { + assertEquals(ex.getParsedString(), text); + assertEquals(ex.getErrorIndex(), pos); + throw ex; + } + } + + //----------------------------------------------------------------------- + @Test(expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_illegalValue_Day() { + MonthDay.parse("--06-32"); + } + + @Test(expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_invalidValue_Day() { + MonthDay.parse("--06-31"); + } + + @Test(expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_illegalValue_Month() { + MonthDay.parse("--13-25"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_nullText() { + MonthDay.parse(null); + } + + //----------------------------------------------------------------------- + // parse(DateTimeFormatter) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_parse_formatter() { + DateTimeFormatter f = DateTimeFormatters.pattern("M d"); + MonthDay test = MonthDay.parse("12 3", f); + assertEquals(test, MonthDay.of(12, 3)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_formatter_nullText() { + DateTimeFormatter f = DateTimeFormatters.pattern("M d"); + MonthDay.parse((String) null, f); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_formatter_nullFormatter() { + MonthDay.parse("ANY", null); + } + + //----------------------------------------------------------------------- + // get(TemporalField) + //----------------------------------------------------------------------- + @Test + public void test_get_TemporalField() { + assertEquals(TEST_07_15.get(ChronoField.DAY_OF_MONTH), 15); + assertEquals(TEST_07_15.get(ChronoField.MONTH_OF_YEAR), 7); + } + + @Test + public void test_getLong_TemporalField() { + assertEquals(TEST_07_15.getLong(ChronoField.DAY_OF_MONTH), 15); + assertEquals(TEST_07_15.getLong(ChronoField.MONTH_OF_YEAR), 7); + } + + //----------------------------------------------------------------------- + // get*() + //----------------------------------------------------------------------- + @DataProvider(name="sampleDates") + Object[][] provider_sampleDates() { + return new Object[][] { + {1, 1}, + {1, 31}, + {2, 1}, + {2, 28}, + {2, 29}, + {7, 4}, + {7, 5}, + }; + } + + @Test(dataProvider="sampleDates", groups={"tck"}) + public void test_get(int m, int d) { + MonthDay a = MonthDay.of(m, d); + assertEquals(a.getMonth(), Month.of(m)); + assertEquals(a.getDayOfMonth(), d); + } + + //----------------------------------------------------------------------- + // with(Month) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_with_Month() { + assertEquals(MonthDay.of(6, 30).with(Month.JANUARY), MonthDay.of(1, 30)); + } + + @Test(groups={"tck"}) + public void test_with_Month_adjustToValid() { + assertEquals(MonthDay.of(7, 31).with(Month.JUNE), MonthDay.of(6, 30)); + } + + @Test(groups={"tck"}) + public void test_with_Month_adjustToValidFeb() { + assertEquals(MonthDay.of(7, 31).with(Month.FEBRUARY), MonthDay.of(2, 29)); + } + + @Test(groups={"tck"}) + public void test_with_Month_noChangeEqual() { + MonthDay test = MonthDay.of(6, 30); + assertEquals(test.with(Month.JUNE), test); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_with_Month_null() { + MonthDay.of(6, 30).with((Month) null); + } + + //----------------------------------------------------------------------- + // withMonth() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withMonth() { + assertEquals(MonthDay.of(6, 30).withMonth(1), MonthDay.of(1, 30)); + } + + @Test(groups={"tck"}) + public void test_withMonth_adjustToValid() { + assertEquals(MonthDay.of(7, 31).withMonth(6), MonthDay.of(6, 30)); + } + + @Test(groups={"tck"}) + public void test_withMonth_adjustToValidFeb() { + assertEquals(MonthDay.of(7, 31).withMonth(2), MonthDay.of(2, 29)); + } + + @Test(groups={"tck"}) + public void test_withMonth_int_noChangeEqual() { + MonthDay test = MonthDay.of(6, 30); + assertEquals(test.withMonth(6), test); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withMonth_tooLow() { + MonthDay.of(6, 30).withMonth(0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withMonth_tooHigh() { + MonthDay.of(6, 30).withMonth(13); + } + + //----------------------------------------------------------------------- + // withDayOfMonth() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withDayOfMonth() { + assertEquals(MonthDay.of(6, 30).withDayOfMonth(1), MonthDay.of(6, 1)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withDayOfMonth_invalid() { + MonthDay.of(6, 30).withDayOfMonth(31); + } + + @Test(groups={"tck"}) + public void test_withDayOfMonth_adjustToValidFeb() { + assertEquals(MonthDay.of(2, 1).withDayOfMonth(29), MonthDay.of(2, 29)); + } + + @Test(groups={"tck"}) + public void test_withDayOfMonth_noChangeEqual() { + MonthDay test = MonthDay.of(6, 30); + assertEquals(test.withDayOfMonth(30), test); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withDayOfMonth_tooLow() { + MonthDay.of(6, 30).withDayOfMonth(0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withDayOfMonth_tooHigh() { + MonthDay.of(6, 30).withDayOfMonth(32); + } + + //----------------------------------------------------------------------- + // adjustInto() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_adjustDate() { + MonthDay test = MonthDay.of(6, 30); + LocalDate date = LocalDate.of(2007, 1, 1); + assertEquals(test.adjustInto(date), LocalDate.of(2007, 6, 30)); + } + + @Test(groups={"tck"}) + public void test_adjustDate_resolve() { + MonthDay test = MonthDay.of(2, 29); + LocalDate date = LocalDate.of(2007, 6, 30); + assertEquals(test.adjustInto(date), LocalDate.of(2007, 2, 28)); + } + + @Test(groups={"tck"}) + public void test_adjustDate_equal() { + MonthDay test = MonthDay.of(6, 30); + LocalDate date = LocalDate.of(2007, 6, 30); + assertEquals(test.adjustInto(date), date); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_adjustDate_null() { + TEST_07_15.adjustInto((LocalDate) null); + } + + //----------------------------------------------------------------------- + // isValidYear(int) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_isValidYear_june() { + MonthDay test = MonthDay.of(6, 30); + assertEquals(test.isValidYear(2007), true); + } + + @Test(groups={"tck"}) + public void test_isValidYear_febNonLeap() { + MonthDay test = MonthDay.of(2, 29); + assertEquals(test.isValidYear(2007), false); + } + + @Test(groups={"tck"}) + public void test_isValidYear_febLeap() { + MonthDay test = MonthDay.of(2, 29); + assertEquals(test.isValidYear(2008), true); + } + + //----------------------------------------------------------------------- + // atYear(int) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_atYear_int() { + MonthDay test = MonthDay.of(6, 30); + assertEquals(test.atYear(2008), LocalDate.of(2008, 6, 30)); + } + + @Test(groups={"tck"}) + public void test_atYear_int_leapYearAdjust() { + MonthDay test = MonthDay.of(2, 29); + assertEquals(test.atYear(2005), LocalDate.of(2005, 2, 28)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atYear_int_invalidYear() { + MonthDay test = MonthDay.of(6, 30); + test.atYear(Integer.MIN_VALUE); + } + + //----------------------------------------------------------------------- + // compareTo() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_comparisons() { + doTest_comparisons_MonthDay( + MonthDay.of(1, 1), + MonthDay.of(1, 31), + MonthDay.of(2, 1), + MonthDay.of(2, 29), + MonthDay.of(3, 1), + MonthDay.of(12, 31) + ); + } + + void doTest_comparisons_MonthDay(MonthDay... localDates) { + for (int i = 0; i < localDates.length; i++) { + MonthDay a = localDates[i]; + for (int j = 0; j < localDates.length; j++) { + MonthDay b = localDates[j]; + if (i < j) { + assertTrue(a.compareTo(b) < 0, a + " <=> " + b); + assertEquals(a.isBefore(b), true, a + " <=> " + b); + assertEquals(a.isAfter(b), false, a + " <=> " + b); + assertEquals(a.equals(b), false, a + " <=> " + b); + } else if (i > j) { + assertTrue(a.compareTo(b) > 0, a + " <=> " + b); + assertEquals(a.isBefore(b), false, a + " <=> " + b); + assertEquals(a.isAfter(b), true, a + " <=> " + b); + assertEquals(a.equals(b), false, a + " <=> " + b); + } else { + assertEquals(a.compareTo(b), 0, a + " <=> " + b); + assertEquals(a.isBefore(b), false, a + " <=> " + b); + assertEquals(a.isAfter(b), false, a + " <=> " + b); + assertEquals(a.equals(b), true, a + " <=> " + b); + } + } + } + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_compareTo_ObjectNull() { + TEST_07_15.compareTo(null); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_isBefore_ObjectNull() { + TEST_07_15.isBefore(null); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_isAfter_ObjectNull() { + TEST_07_15.isAfter(null); + } + + //----------------------------------------------------------------------- + // equals() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_equals() { + MonthDay a = MonthDay.of(1, 1); + MonthDay b = MonthDay.of(1, 1); + MonthDay c = MonthDay.of(2, 1); + MonthDay d = MonthDay.of(1, 2); + + assertEquals(a.equals(a), true); + assertEquals(a.equals(b), true); + assertEquals(a.equals(c), false); + assertEquals(a.equals(d), false); + + assertEquals(b.equals(a), true); + assertEquals(b.equals(b), true); + assertEquals(b.equals(c), false); + assertEquals(b.equals(d), false); + + assertEquals(c.equals(a), false); + assertEquals(c.equals(b), false); + assertEquals(c.equals(c), true); + assertEquals(c.equals(d), false); + + assertEquals(d.equals(a), false); + assertEquals(d.equals(b), false); + assertEquals(d.equals(c), false); + assertEquals(d.equals(d), true); + } + + @Test(groups={"tck"}) + public void test_equals_itself_true() { + assertEquals(TEST_07_15.equals(TEST_07_15), true); + } + + @Test(groups={"tck"}) + public void test_equals_string_false() { + assertEquals(TEST_07_15.equals("2007-07-15"), false); + } + + @Test(groups={"tck"}) + public void test_equals_null_false() { + assertEquals(TEST_07_15.equals(null), false); + } + + //----------------------------------------------------------------------- + // hashCode() + //----------------------------------------------------------------------- + @Test(dataProvider="sampleDates", groups={"tck"}) + public void test_hashCode(int m, int d) { + MonthDay a = MonthDay.of(m, d); + assertEquals(a.hashCode(), a.hashCode()); + MonthDay b = MonthDay.of(m, d); + assertEquals(a.hashCode(), b.hashCode()); + } + + @Test(groups={"tck"}) + public void test_hashCode_unique() { + int leapYear = 2008; + Set uniques = new HashSet(366); + for (int i = 1; i <= 12; i++) { + for (int j = 1; j <= 31; j++) { + if (YearMonth.of(leapYear, i).isValidDay(j)) { + assertTrue(uniques.add(MonthDay.of(i, j).hashCode())); + } + } + } + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @DataProvider(name="sampleToString") + Object[][] provider_sampleToString() { + return new Object[][] { + {7, 5, "--07-05"}, + {12, 31, "--12-31"}, + {1, 2, "--01-02"}, + }; + } + + @Test(dataProvider="sampleToString", groups={"tck"}) + public void test_toString(int m, int d, String expected) { + MonthDay test = MonthDay.of(m, d); + String str = test.toString(); + assertEquals(str, expected); + } + + //----------------------------------------------------------------------- + // toString(DateTimeFormatter) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toString_formatter() { + DateTimeFormatter f = DateTimeFormatters.pattern("M d"); + String t = MonthDay.of(12, 3).toString(f); + assertEquals(t, "12 3"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_toString_formatter_null() { + MonthDay.of(12, 3).toString(null); + } + +} diff --git a/jdk/test/java/time/tck/java/time/temporal/TCKOffsetDate.java b/jdk/test/java/time/tck/java/time/temporal/TCKOffsetDate.java new file mode 100644 index 00000000000..77720220d69 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/temporal/TCKOffsetDate.java @@ -0,0 +1,1949 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * +9 * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.temporal; + +import static java.time.Month.DECEMBER; +import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; +import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; +import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; +import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.DAY_OF_WEEK; +import static java.time.temporal.ChronoField.DAY_OF_YEAR; +import static java.time.temporal.ChronoField.EPOCH_DAY; +import static java.time.temporal.ChronoField.EPOCH_MONTH; +import static java.time.temporal.ChronoField.ERA; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.OFFSET_SECONDS; +import static java.time.temporal.ChronoField.YEAR; +import static java.time.temporal.ChronoField.YEAR_OF_ERA; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import java.time.Clock; +import java.time.DateTimeException; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Month; +import java.time.Period; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatters; +import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoUnit; +import java.time.temporal.ISOChrono; +import java.time.temporal.JulianFields; +import java.time.temporal.OffsetDate; +import java.time.temporal.OffsetDateTime; +import java.time.temporal.Queries; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalAdder; +import java.time.temporal.TemporalAdjuster; +import java.time.temporal.TemporalField; +import java.time.temporal.TemporalSubtractor; +import java.time.temporal.Year; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import tck.java.time.AbstractDateTimeTest; +import test.java.time.MockSimplePeriod; + +/** + * Test OffsetDate. + */ +@Test +public class TCKOffsetDate extends AbstractDateTimeTest { + private static final ZoneOffset OFFSET_PONE = ZoneOffset.ofHours(1); + private static final ZoneOffset OFFSET_PTWO = ZoneOffset.ofHours(2); + + private OffsetDate TEST_2007_07_15_PONE; + + @BeforeMethod(groups={"tck","implementation"}) + public void setUp() { + TEST_2007_07_15_PONE = OffsetDate.of(LocalDate.of(2007, 7, 15), OFFSET_PONE); + } + + //----------------------------------------------------------------------- + @Override + protected List samples() { + TemporalAccessor[] array = {TEST_2007_07_15_PONE, OffsetDate.MIN, OffsetDate.MAX}; + return Arrays.asList(array); + } + + @Override + protected List validFields() { + TemporalField[] array = { + DAY_OF_WEEK, + ALIGNED_DAY_OF_WEEK_IN_MONTH, + ALIGNED_DAY_OF_WEEK_IN_YEAR, + DAY_OF_MONTH, + DAY_OF_YEAR, + EPOCH_DAY, + ALIGNED_WEEK_OF_MONTH, + ALIGNED_WEEK_OF_YEAR, + MONTH_OF_YEAR, + EPOCH_MONTH, + YEAR_OF_ERA, + YEAR, + ERA, + OFFSET_SECONDS, + JulianFields.JULIAN_DAY, + JulianFields.MODIFIED_JULIAN_DAY, + JulianFields.RATA_DIE, + }; + return Arrays.asList(array); + } + + @Override + protected List invalidFields() { + List list = new ArrayList<>(Arrays.asList(ChronoField.values())); + list.removeAll(validFields()); + return list; + } + + //----------------------------------------------------------------------- + @Test + public void test_serialization() throws ClassNotFoundException, IOException { + assertSerializable(TEST_2007_07_15_PONE); + assertSerializable(OffsetDate.MIN); + assertSerializable(OffsetDate.MAX); + } + + @Test + public void test_serialization_format() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baos) ) { + dos.writeByte(1); + } + byte[] bytes = baos.toByteArray(); + ByteArrayOutputStream baosDate = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baosDate) ) { + dos.writeByte(3); + dos.writeInt(2012); + dos.writeByte(9); + dos.writeByte(16); + } + byte[] bytesDate = baosDate.toByteArray(); + ByteArrayOutputStream baosOffset = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baosOffset) ) { + dos.writeByte(8); + dos.writeByte(4); // quarter hours stored: 3600 / 900 + } + byte[] bytesOffset = baosOffset.toByteArray(); + assertSerializedBySer(OffsetDate.of(LocalDate.of(2012, 9, 16), ZoneOffset.ofHours(1)), bytes, + bytesDate, bytesOffset); + } + + //----------------------------------------------------------------------- + // constants + //----------------------------------------------------------------------- + @Test + public void constant_MIN() { + check(OffsetDate.MIN, Year.MIN_VALUE, 1, 1, ZoneOffset.MAX); + } + + @Test + public void constant_MAX() { + check(OffsetDate.MAX, Year.MAX_VALUE, 12, 31, ZoneOffset.MIN); + } + + //----------------------------------------------------------------------- + // now() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void now() { + OffsetDate expected = OffsetDate.now(Clock.systemDefaultZone()); + OffsetDate test = OffsetDate.now(); + for (int i = 0; i < 100; i++) { + if (expected.equals(test)) { + return; + } + expected = OffsetDate.now(Clock.systemDefaultZone()); + test = OffsetDate.now(); + } + assertEquals(test, expected); + } + + @Test(groups={"tck"}) + public void now_Clock_allSecsInDay_utc() { + for (int i = 0; i < (2 * 24 * 60 * 60); i++) { + Instant instant = Instant.ofEpochSecond(i); + Clock clock = Clock.fixed(instant, ZoneOffset.UTC); + OffsetDate test = OffsetDate.now(clock); + check(test, 1970, 1, (i < 24 * 60 * 60 ? 1 : 2), ZoneOffset.UTC); + } + } + + @Test(groups={"tck"}) + public void now_Clock_allSecsInDay_beforeEpoch() { + for (int i =-1; i >= -(2 * 24 * 60 * 60); i--) { + Instant instant = Instant.ofEpochSecond(i); + Clock clock = Clock.fixed(instant, ZoneOffset.UTC); + OffsetDate test = OffsetDate.now(clock); + check(test, 1969, 12, (i >= -24 * 60 * 60 ? 31 : 30), ZoneOffset.UTC); + } + } + + @Test(groups={"tck"}) + public void now_Clock_offsets() { + Instant base = LocalDateTime.of(1970, 1, 1, 12, 0).toInstant(ZoneOffset.UTC); + for (int i = -9; i < 15; i++) { + ZoneOffset offset = ZoneOffset.ofHours(i); + Clock clock = Clock.fixed(base, offset); + OffsetDate test = OffsetDate.now(clock); + check(test, 1970, 1, (i >= 12 ? 2 : 1), offset); + } + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void now_Clock_nullZoneId() { + OffsetDate.now((ZoneId) null); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void now_Clock_nullClock() { + OffsetDate.now((Clock) null); + } + + //----------------------------------------------------------------------- + // factories + //----------------------------------------------------------------------- + private void check(OffsetDate test, int y, int mo, int d, ZoneOffset offset) { + assertEquals(test.getDate(), LocalDate.of(y, mo, d)); + assertEquals(test.getOffset(), offset); + + assertEquals(test.getYear(), y); + assertEquals(test.getMonth().getValue(), mo); + assertEquals(test.getDayOfMonth(), d); + + assertEquals(test, test); + assertEquals(test.hashCode(), test.hashCode()); + assertEquals(OffsetDate.of(LocalDate.of(y, mo, d), offset), test); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_of_intMonthInt() { + OffsetDate test = OffsetDate.of(LocalDate.of(2007, Month.JULY, 15), OFFSET_PONE); + check(test, 2007, 7, 15, OFFSET_PONE); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_of_ints() { + OffsetDate test = OffsetDate.of(LocalDate.of(2007, 7, 15), OFFSET_PONE); + check(test, 2007, 7, 15, OFFSET_PONE); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_of_intsMonthOffset() { + assertEquals(TEST_2007_07_15_PONE, OffsetDate.of(LocalDate.of(2007, Month.JULY, 15), OFFSET_PONE)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_intsMonthOffset_dayTooLow() { + OffsetDate.of(LocalDate.of(2007, Month.JANUARY, 0), OFFSET_PONE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_intsMonthOffset_dayTooHigh() { + OffsetDate.of(LocalDate.of(2007, Month.JANUARY, 32), OFFSET_PONE); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_of_intsMonthOffset_nullMonth() { + OffsetDate.of(LocalDate.of(2007, null, 30), OFFSET_PONE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_intsMonthOffset_yearTooLow() { + OffsetDate.of(LocalDate.of(Integer.MIN_VALUE, Month.JANUARY, 1), OFFSET_PONE); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_of_intsMonthOffset_nullOffset() { + OffsetDate.of(LocalDate.of(2007, Month.JANUARY, 30), null); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_of_intsOffset() { + OffsetDate test = OffsetDate.of(LocalDate.of(2007, 7, 15), OFFSET_PONE); + check(test, 2007, 7, 15, OFFSET_PONE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_ints_dayTooLow() { + OffsetDate.of(LocalDate.of(2007, 1, 0), OFFSET_PONE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_ints_dayTooHigh() { + OffsetDate.of(LocalDate.of(2007, 1, 32), OFFSET_PONE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_ints_monthTooLow() { + OffsetDate.of(LocalDate.of(2007, 0, 1), OFFSET_PONE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_ints_monthTooHigh() { + OffsetDate.of(LocalDate.of(2007, 13, 1), OFFSET_PONE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_of_ints_yearTooLow() { + OffsetDate.of(LocalDate.of(Integer.MIN_VALUE, 1, 1), OFFSET_PONE); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_of_ints_nullOffset() { + OffsetDate.of(LocalDate.of(2007, 1, 1), (ZoneOffset) null); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_of_LocalDateZoneOffset() { + LocalDate localDate = LocalDate.of(2008, 6, 30); + OffsetDate test = OffsetDate.of(localDate, OFFSET_PONE); + check(test, 2008, 6, 30, OFFSET_PONE); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_of_LocalDateZoneOffset_nullDate() { + OffsetDate.of((LocalDate) null, OFFSET_PONE); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_of_LocalDateZoneOffset_nullOffset() { + LocalDate localDate = LocalDate.of(2008, 6, 30); + OffsetDate.of(localDate, (ZoneOffset) null); + } + + //----------------------------------------------------------------------- + // from(TemporalAccessor) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_from_TemporalAccessor_OD() { + assertEquals(OffsetDate.from(TEST_2007_07_15_PONE), TEST_2007_07_15_PONE); + } + + @Test(groups={"tck"}) + public void test_from_TemporalAccessor_ZDT() { + ZonedDateTime base = LocalDateTime.of(2007, 7, 15, 17, 30).atZone(OFFSET_PONE); + assertEquals(OffsetDate.from(base), TEST_2007_07_15_PONE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_from_TemporalAccessor_invalid_noDerive() { + OffsetDate.from(LocalTime.of(12, 30)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_from_TemporalAccessor_null() { + OffsetDate.from((TemporalAccessor) null); + } + + //----------------------------------------------------------------------- + // parse() + //----------------------------------------------------------------------- + @Test(dataProvider="sampleToString", groups={"tck"}) + public void factory_parse_validText(int y, int m, int d, String offsetId, String parsable) { + OffsetDate t = OffsetDate.parse(parsable); + assertNotNull(t, parsable); + assertEquals(t.getYear(), y, parsable); + assertEquals(t.getMonth().getValue(), m, parsable); + assertEquals(t.getDayOfMonth(), d, parsable); + assertEquals(t.getOffset(), ZoneOffset.of(offsetId)); + } + + @DataProvider(name="sampleBadParse") + Object[][] provider_sampleBadParse() { + return new Object[][]{ + {"2008/07/05"}, + {"10000-01-01"}, + {"2008-1-1"}, + {"2008--01"}, + {"ABCD-02-01"}, + {"2008-AB-01"}, + {"2008-02-AB"}, + {"-0000-02-01"}, + {"2008-02-01Y"}, + {"2008-02-01+19:00"}, + {"2008-02-01+01/00"}, + {"2008-02-01+1900"}, + {"2008-02-01+01:60"}, + {"2008-02-01+01:30:123"}, + {"2008-02-01"}, + {"2008-02-01+01:00[Europe/Paris]"}, + }; + } + + @Test(dataProvider="sampleBadParse", expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_invalidText(String unparsable) { + OffsetDate.parse(unparsable); + } + + @Test(expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_illegalValue() { + OffsetDate.parse("2008-06-32+01:00"); + } + + @Test(expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_invalidValue() { + OffsetDate.parse("2008-06-31+01:00"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_nullText() { + OffsetDate.parse((String) null); + } + + //----------------------------------------------------------------------- + // parse(DateTimeFormatter) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_parse_formatter() { + DateTimeFormatter f = DateTimeFormatters.pattern("y M d XXX"); + OffsetDate test = OffsetDate.parse("2010 12 3 +01:00", f); + assertEquals(test, OffsetDate.of(LocalDate.of(2010, 12, 3), ZoneOffset.ofHours(1))); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_formatter_nullText() { + DateTimeFormatter f = DateTimeFormatters.pattern("y M d"); + OffsetDate.parse((String) null, f); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_formatter_nullFormatter() { + OffsetDate.parse("ANY", null); + } + + //----------------------------------------------------------------------- + // constructor + //----------------------------------------------------------------------- + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void constructor_nullDate() throws Throwable { + Constructor con = OffsetDate.class.getDeclaredConstructor(LocalDate.class, ZoneOffset.class); + con.setAccessible(true); + try { + con.newInstance(null, OFFSET_PONE); + } catch (InvocationTargetException ex) { + throw ex.getCause(); + } + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void constructor_nullOffset() throws Throwable { + Constructor con = OffsetDate.class.getDeclaredConstructor(LocalDate.class, ZoneOffset.class); + con.setAccessible(true); + try { + con.newInstance(LocalDate.of(2008, 6, 30), null); + } catch (InvocationTargetException ex) { + throw ex.getCause(); + } + } + + //----------------------------------------------------------------------- + // basics + //----------------------------------------------------------------------- + @DataProvider(name="sampleDates") + Object[][] provider_sampleDates() { + return new Object[][] { + {2008, 7, 5, OFFSET_PTWO}, + {2007, 7, 5, OFFSET_PONE}, + {2006, 7, 5, OFFSET_PTWO}, + {2005, 7, 5, OFFSET_PONE}, + {2004, 1, 1, OFFSET_PTWO}, + {-1, 1, 2, OFFSET_PONE}, + {999999, 11, 20, ZoneOffset.ofHoursMinutesSeconds(6, 9, 12)}, + }; + } + + @Test(dataProvider="sampleDates", groups={"tck"}) + public void test_get_OffsetDate(int y, int m, int d, ZoneOffset offset) { + LocalDate localDate = LocalDate.of(y, m, d); + OffsetDate a = OffsetDate.of(localDate, offset); + + assertEquals(a.getDate(), localDate); + assertEquals(a.getOffset(), offset); + assertEquals(a.toString(), localDate.toString() + offset.toString()); + assertEquals(a.getYear(), localDate.getYear()); + assertEquals(a.getMonth(), localDate.getMonth()); + assertEquals(a.getDayOfMonth(), localDate.getDayOfMonth()); + assertEquals(a.getDayOfYear(), localDate.getDayOfYear()); + assertEquals(a.getDayOfWeek(), localDate.getDayOfWeek()); + } + + //----------------------------------------------------------------------- + // get(TemporalField) + //----------------------------------------------------------------------- + @Test + public void test_get_TemporalField() { + OffsetDate test = OffsetDate.of(LocalDate.of(2008, 6, 30), OFFSET_PONE); + assertEquals(test.get(ChronoField.YEAR), 2008); + assertEquals(test.get(ChronoField.MONTH_OF_YEAR), 6); + assertEquals(test.get(ChronoField.DAY_OF_MONTH), 30); + assertEquals(test.get(ChronoField.DAY_OF_WEEK), 1); + assertEquals(test.get(ChronoField.DAY_OF_YEAR), 182); + + assertEquals(test.get(ChronoField.OFFSET_SECONDS), 3600); + } + + @Test + public void test_getLong_TemporalField() { + OffsetDate test = OffsetDate.of(LocalDate.of(2008, 6, 30), OFFSET_PONE); + assertEquals(test.getLong(ChronoField.YEAR), 2008); + assertEquals(test.getLong(ChronoField.MONTH_OF_YEAR), 6); + assertEquals(test.getLong(ChronoField.DAY_OF_MONTH), 30); + assertEquals(test.getLong(ChronoField.DAY_OF_WEEK), 1); + assertEquals(test.getLong(ChronoField.DAY_OF_YEAR), 182); + + assertEquals(test.getLong(ChronoField.OFFSET_SECONDS), 3600); + } + + //----------------------------------------------------------------------- + // query(TemporalQuery) + //----------------------------------------------------------------------- + @Test + public void test_query_chrono() { + assertEquals(TEST_2007_07_15_PONE.query(Queries.chrono()), ISOChrono.INSTANCE); + assertEquals(Queries.chrono().queryFrom(TEST_2007_07_15_PONE), ISOChrono.INSTANCE); + } + + @Test + public void test_query_zoneId() { + assertEquals(TEST_2007_07_15_PONE.query(Queries.zoneId()), null); + assertEquals(Queries.zoneId().queryFrom(TEST_2007_07_15_PONE), null); + } + + @Test + public void test_query_precision() { + assertEquals(TEST_2007_07_15_PONE.query(Queries.precision()), ChronoUnit.DAYS); + assertEquals(Queries.precision().queryFrom(TEST_2007_07_15_PONE), ChronoUnit.DAYS); + } + + @Test + public void test_query_offset() { + assertEquals(TEST_2007_07_15_PONE.query(Queries.offset()), OFFSET_PONE); + assertEquals(Queries.offset().queryFrom(TEST_2007_07_15_PONE), OFFSET_PONE); + } + + @Test + public void test_query_zone() { + assertEquals(TEST_2007_07_15_PONE.query(Queries.zone()), OFFSET_PONE); + assertEquals(Queries.zone().queryFrom(TEST_2007_07_15_PONE), OFFSET_PONE); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_query_null() { + TEST_2007_07_15_PONE.query(null); + } + + //----------------------------------------------------------------------- + // withOffset() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withOffset() { + OffsetDate base = OffsetDate.of(LocalDate.of(2008, 6, 30), OFFSET_PONE); + OffsetDate test = base.withOffset(OFFSET_PTWO); + assertEquals(test.getDate(), base.getDate()); + assertEquals(test.getOffset(), OFFSET_PTWO); + } + + @Test(groups={"tck"}) + public void test_withOffset_noChange() { + OffsetDate base = OffsetDate.of(LocalDate.of(2008, 6, 30), OFFSET_PONE); + OffsetDate test = base.withOffset(OFFSET_PONE); + assertEquals(test, base); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_withOffset_null() { + TEST_2007_07_15_PONE.withOffset(null); + } + + //----------------------------------------------------------------------- + // with(WithAdjuster) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_with_adjustment() { + final OffsetDate sample = OffsetDate.of(LocalDate.of(2012, 3, 4), OFFSET_PONE); + TemporalAdjuster adjuster = new TemporalAdjuster() { + @Override + public Temporal adjustInto(Temporal dateTime) { + return sample; + } + }; + assertEquals(TEST_2007_07_15_PONE.with(adjuster), sample); + } + + @Test(groups={"tck"}) + public void test_with_adjustment_LocalDate() { + OffsetDate test = TEST_2007_07_15_PONE.with(LocalDate.of(2008, 6, 30)); + assertEquals(test, OffsetDate.of(LocalDate.of(2008, 6, 30), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_with_adjustment_OffsetDate() { + OffsetDate test = TEST_2007_07_15_PONE.with(OffsetDate.of(LocalDate.of(2008, 6, 30), OFFSET_PTWO)); + assertEquals(test, OffsetDate.of(LocalDate.of(2008, 6, 30), OFFSET_PTWO)); + } + + @Test(groups={"tck"}) + public void test_with_adjustment_ZoneOffset() { + OffsetDate test = TEST_2007_07_15_PONE.with(OFFSET_PTWO); + assertEquals(test, OffsetDate.of(LocalDate.of(2007, 7, 15), OFFSET_PTWO)); + } + + @Test(groups={"tck"}) + public void test_with_adjustment_Month() { + OffsetDate test = TEST_2007_07_15_PONE.with(DECEMBER); + assertEquals(test, OffsetDate.of(LocalDate.of(2007, 12, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_with_adjustment_offsetUnchanged() { + OffsetDate base = OffsetDate.of(LocalDate.of(2008, 6, 30), OFFSET_PONE); + OffsetDate test = base.with(Year.of(2008)); + assertEquals(test, base); + } + + @Test(groups={"tck"}) + public void test_with_adjustment_noChange() { + LocalDate date = LocalDate.of(2008, 6, 30); + OffsetDate base = OffsetDate.of(date, OFFSET_PONE); + OffsetDate test = base.with(date); + assertEquals(test, base); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_with_adjustment_null() { + TEST_2007_07_15_PONE.with((TemporalAdjuster) null); + } + + //----------------------------------------------------------------------- + // with(TemporalField, long) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_with_TemporalField() { + OffsetDate test = OffsetDate.of(LocalDate.of(2008, 6, 30), OFFSET_PONE); + assertEquals(test.with(ChronoField.YEAR, 2009), OffsetDate.of(LocalDate.of(2009, 6, 30), OFFSET_PONE)); + assertEquals(test.with(ChronoField.MONTH_OF_YEAR, 7), OffsetDate.of(LocalDate.of(2008, 7, 30), OFFSET_PONE)); + assertEquals(test.with(ChronoField.DAY_OF_MONTH, 1), OffsetDate.of(LocalDate.of(2008, 6, 1), OFFSET_PONE)); + assertEquals(test.with(ChronoField.DAY_OF_WEEK, 2), OffsetDate.of(LocalDate.of(2008, 7, 1), OFFSET_PONE)); + assertEquals(test.with(ChronoField.DAY_OF_YEAR, 183), OffsetDate.of(LocalDate.of(2008, 7, 1), OFFSET_PONE)); + + assertEquals(test.with(ChronoField.OFFSET_SECONDS, 7205), OffsetDate.of(LocalDate.of(2008, 6, 30), ZoneOffset.ofHoursMinutesSeconds(2, 0, 5))); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"} ) + public void test_with_TemporalField_null() { + TEST_2007_07_15_PONE.with((TemporalField) null, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"} ) + public void test_with_TemporalField_invalidField() { + TEST_2007_07_15_PONE.with(ChronoField.AMPM_OF_DAY, 0); + } + + //----------------------------------------------------------------------- + // withYear() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withYear_int_normal() { + OffsetDate t = TEST_2007_07_15_PONE.withYear(2008); + assertEquals(t, OffsetDate.of(LocalDate.of(2008, 7, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_withYear_int_noChange() { + OffsetDate t = TEST_2007_07_15_PONE.withYear(2007); + assertEquals(t, TEST_2007_07_15_PONE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withYear_int_invalid() { + TEST_2007_07_15_PONE.withYear(Year.MIN_VALUE - 1); + } + + @Test(groups={"tck"}) + public void test_withYear_int_adjustDay() { + OffsetDate t = OffsetDate.of(LocalDate.of(2008, 2, 29), OFFSET_PONE).withYear(2007); + OffsetDate expected = OffsetDate.of(LocalDate.of(2007, 2, 28), OFFSET_PONE); + assertEquals(t, expected); + } + + //----------------------------------------------------------------------- + // withMonth() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withMonth_int_normal() { + OffsetDate t = TEST_2007_07_15_PONE.withMonth(1); + assertEquals(t, OffsetDate.of(LocalDate.of(2007, 1, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_withMonth_int_noChange() { + OffsetDate t = TEST_2007_07_15_PONE.withMonth(7); + assertEquals(t, TEST_2007_07_15_PONE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withMonth_int_invalid() { + TEST_2007_07_15_PONE.withMonth(13); + } + + @Test(groups={"tck"}) + public void test_withMonth_int_adjustDay() { + OffsetDate t = OffsetDate.of(LocalDate.of(2007, 12, 31), OFFSET_PONE).withMonth(11); + OffsetDate expected = OffsetDate.of(LocalDate.of(2007, 11, 30), OFFSET_PONE); + assertEquals(t, expected); + } + + //----------------------------------------------------------------------- + // withDayOfMonth() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withDayOfMonth_normal() { + OffsetDate t = TEST_2007_07_15_PONE.withDayOfMonth(1); + assertEquals(t, OffsetDate.of(LocalDate.of(2007, 7, 1), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_withDayOfMonth_noChange() { + OffsetDate t = TEST_2007_07_15_PONE.withDayOfMonth(15); + assertEquals(t, OffsetDate.of(LocalDate.of(2007, 7, 15), OFFSET_PONE)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withDayOfMonth_invalidForMonth() { + OffsetDate.of(LocalDate.of(2007, 11, 30), OFFSET_PONE).withDayOfMonth(31); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withDayOfMonth_invalidAlways() { + OffsetDate.of(LocalDate.of(2007, 11, 30), OFFSET_PONE).withDayOfMonth(32); + } + + //----------------------------------------------------------------------- + // withDayOfYear(int) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withDayOfYear_normal() { + OffsetDate t = TEST_2007_07_15_PONE.withDayOfYear(33); + assertEquals(t, OffsetDate.of(LocalDate.of(2007, 2, 2), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_withDayOfYear_noChange() { + OffsetDate t = TEST_2007_07_15_PONE.withDayOfYear(31 + 28 + 31 + 30 + 31 + 30 + 15); + assertEquals(t, TEST_2007_07_15_PONE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withDayOfYear_illegal() { + TEST_2007_07_15_PONE.withDayOfYear(367); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withDayOfYear_invalid() { + TEST_2007_07_15_PONE.withDayOfYear(366); + } + + //----------------------------------------------------------------------- + // plus(PlusAdjuster) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plus_PlusAdjuster() { + MockSimplePeriod period = MockSimplePeriod.of(7, ChronoUnit.MONTHS); + OffsetDate t = TEST_2007_07_15_PONE.plus(period); + assertEquals(t, OffsetDate.of(LocalDate.of(2008, 2, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plus_PlusAdjuster_noChange() { + MockSimplePeriod period = MockSimplePeriod.of(0, ChronoUnit.MONTHS); + OffsetDate t = TEST_2007_07_15_PONE.plus(period); + assertEquals(t, TEST_2007_07_15_PONE); + } + + @Test(groups={"tck"}) + public void test_plus_PlusAdjuster_zero() { + OffsetDate t = TEST_2007_07_15_PONE.plus(Period.ZERO); + assertEquals(t, TEST_2007_07_15_PONE); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_plus_PlusAdjuster_null() { + TEST_2007_07_15_PONE.plus((TemporalAdder) null); + } + + //----------------------------------------------------------------------- + // plusYears() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusYears_long_normal() { + OffsetDate t = TEST_2007_07_15_PONE.plusYears(1); + assertEquals(t, OffsetDate.of(LocalDate.of(2008, 7, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plusYears_long_negative() { + OffsetDate t = TEST_2007_07_15_PONE.plusYears(-1); + assertEquals(t, OffsetDate.of(LocalDate.of(2006, 7, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plusYears_long_noChange() { + OffsetDate t = TEST_2007_07_15_PONE.plusYears(0); + assertEquals(t, TEST_2007_07_15_PONE); + } + + @Test(groups={"tck"}) + public void test_plusYears_long_adjustDay() { + OffsetDate t = OffsetDate.of(LocalDate.of(2008, 2, 29), OFFSET_PONE).plusYears(1); + OffsetDate expected = OffsetDate.of(LocalDate.of(2009, 2, 28), OFFSET_PONE); + assertEquals(t, expected); + } + + @Test(groups={"tck"}) + public void test_plusYears_long_big() { + long years = 20L + Year.MAX_VALUE; + OffsetDate test = OffsetDate.of(LocalDate.of(-40, 6, 1), OFFSET_PONE).plusYears(years); + assertEquals(test, OffsetDate.of(LocalDate.of((int) (-40L + years), 6, 1), OFFSET_PONE)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusYears_long_invalidTooLarge() { + OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 1, 1), OFFSET_PONE).plusYears(1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusYears_long_invalidTooLargeMaxAddMax() { + OffsetDate test = OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 1), OFFSET_PONE); + test.plusYears(Long.MAX_VALUE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusYears_long_invalidTooLargeMaxAddMin() { + OffsetDate test = OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 1), OFFSET_PONE); + test.plusYears(Long.MIN_VALUE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusYears_long_invalidTooSmall() { + OffsetDate.of(LocalDate.of(Year.MIN_VALUE, 1, 1), OFFSET_PONE).plusYears(-1); + } + + //----------------------------------------------------------------------- + // plusMonths() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusMonths_long_normal() { + OffsetDate t = TEST_2007_07_15_PONE.plusMonths(1); + assertEquals(t, OffsetDate.of(LocalDate.of(2007, 8, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plusMonths_long_overYears() { + OffsetDate t = TEST_2007_07_15_PONE.plusMonths(25); + assertEquals(t, OffsetDate.of(LocalDate.of(2009, 8, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plusMonths_long_negative() { + OffsetDate t = TEST_2007_07_15_PONE.plusMonths(-1); + assertEquals(t, OffsetDate.of(LocalDate.of(2007, 6, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plusMonths_long_negativeAcrossYear() { + OffsetDate t = TEST_2007_07_15_PONE.plusMonths(-7); + assertEquals(t, OffsetDate.of(LocalDate.of(2006, 12, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plusMonths_long_negativeOverYears() { + OffsetDate t = TEST_2007_07_15_PONE.plusMonths(-31); + assertEquals(t, OffsetDate.of(LocalDate.of(2004, 12, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plusMonths_long_noChange() { + OffsetDate t = TEST_2007_07_15_PONE.plusMonths(0); + assertEquals(t, TEST_2007_07_15_PONE); + } + + @Test(groups={"tck"}) + public void test_plusMonths_long_adjustDayFromLeapYear() { + OffsetDate t = OffsetDate.of(LocalDate.of(2008, 2, 29), OFFSET_PONE).plusMonths(12); + OffsetDate expected = OffsetDate.of(LocalDate.of(2009, 2, 28), OFFSET_PONE); + assertEquals(t, expected); + } + + @Test(groups={"tck"}) + public void test_plusMonths_long_adjustDayFromMonthLength() { + OffsetDate t = OffsetDate.of(LocalDate.of(2007, 3, 31), OFFSET_PONE).plusMonths(1); + OffsetDate expected = OffsetDate.of(LocalDate.of(2007, 4, 30), OFFSET_PONE); + assertEquals(t, expected); + } + + @Test(groups={"tck"}) + public void test_plusMonths_long_big() { + long months = 20L + Integer.MAX_VALUE; + OffsetDate test = OffsetDate.of(LocalDate.of(-40, 6, 1), OFFSET_PONE).plusMonths(months); + assertEquals(test, OffsetDate.of(LocalDate.of((int) (-40L + months / 12), 6 + (int) (months % 12), 1), OFFSET_PONE)); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_plusMonths_long_invalidTooLarge() { + OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 1), OFFSET_PONE).plusMonths(1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusMonths_long_invalidTooLargeMaxAddMax() { + OffsetDate test = OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 1), OFFSET_PONE); + test.plusMonths(Long.MAX_VALUE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusMonths_long_invalidTooLargeMaxAddMin() { + OffsetDate test = OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 1), OFFSET_PONE); + test.plusMonths(Long.MIN_VALUE); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_plusMonths_long_invalidTooSmall() { + OffsetDate.of(LocalDate.of(Year.MIN_VALUE, 1, 1), OFFSET_PONE).plusMonths(-1); + } + + //----------------------------------------------------------------------- + // plusWeeks() + //----------------------------------------------------------------------- + @DataProvider(name="samplePlusWeeksSymmetry") + Object[][] provider_samplePlusWeeksSymmetry() { + return new Object[][] { + {OffsetDate.of(LocalDate.of(-1, 1, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(-1, 2, 28), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(-1, 3, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(-1, 12, 31), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(0, 1, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(0, 2, 28), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(0, 2, 29), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(0, 3, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(0, 12, 31), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2007, 1, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2007, 2, 28), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2007, 3, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2007, 12, 31), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2008, 1, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2008, 2, 28), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2008, 2, 29), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2008, 3, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2008, 12, 31), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2099, 1, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2099, 2, 28), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2099, 3, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2099, 12, 31), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2100, 1, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2100, 2, 28), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2100, 3, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2100, 12, 31), OFFSET_PTWO)}, + }; + } + + @Test(dataProvider="samplePlusWeeksSymmetry", groups={"tck"}) + public void test_plusWeeks_symmetry(OffsetDate reference) { + for (int weeks = 0; weeks < 365 * 8; weeks++) { + OffsetDate t = reference.plusWeeks(weeks).plusWeeks(-weeks); + assertEquals(t, reference); + + t = reference.plusWeeks(-weeks).plusWeeks(weeks); + assertEquals(t, reference); + } + } + + @Test(groups={"tck"}) + public void test_plusWeeks_normal() { + OffsetDate t = TEST_2007_07_15_PONE.plusWeeks(1); + assertEquals(t, OffsetDate.of(LocalDate.of(2007, 7, 22), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_overMonths() { + OffsetDate t = TEST_2007_07_15_PONE.plusWeeks(9); + assertEquals(t, OffsetDate.of(LocalDate.of(2007, 9, 16), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_overYears() { + OffsetDate t = OffsetDate.of(LocalDate.of(2006, 7, 16), OFFSET_PONE).plusWeeks(52); + assertEquals(t, TEST_2007_07_15_PONE); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_overLeapYears() { + OffsetDate t = TEST_2007_07_15_PONE.plusYears(-1).plusWeeks(104); + assertEquals(t, OffsetDate.of(LocalDate.of(2008, 7, 12), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_negative() { + OffsetDate t = TEST_2007_07_15_PONE.plusWeeks(-1); + assertEquals(t, OffsetDate.of(LocalDate.of(2007, 7, 8), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_negativeAcrossYear() { + OffsetDate t = TEST_2007_07_15_PONE.plusWeeks(-28); + assertEquals(t, OffsetDate.of(LocalDate.of(2006, 12, 31), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_negativeOverYears() { + OffsetDate t = TEST_2007_07_15_PONE.plusWeeks(-104); + assertEquals(t, OffsetDate.of(LocalDate.of(2005, 7, 17), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_noChange() { + OffsetDate t = TEST_2007_07_15_PONE.plusWeeks(0); + assertEquals(t, TEST_2007_07_15_PONE); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_maximum() { + OffsetDate t = OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 24), OFFSET_PONE).plusWeeks(1); + OffsetDate expected = OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 31), OFFSET_PONE); + assertEquals(t, expected); + } + + @Test(groups={"tck"}) + public void test_plusWeeks_minimum() { + OffsetDate t = OffsetDate.of(LocalDate.of(Year.MIN_VALUE, 1, 8), OFFSET_PONE).plusWeeks(-1); + OffsetDate expected = OffsetDate.of(LocalDate.of(Year.MIN_VALUE, 1, 1), OFFSET_PONE); + assertEquals(t, expected); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_plusWeeks_invalidTooLarge() { + OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 25), OFFSET_PONE).plusWeeks(1); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_plusWeeks_invalidTooSmall() { + OffsetDate.of(LocalDate.of(Year.MIN_VALUE, 1, 7), OFFSET_PONE).plusWeeks(-1); + } + + @Test(expectedExceptions={ArithmeticException.class}, groups={"tck"}) + public void test_plusWeeks_invalidMaxMinusMax() { + OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 25), OFFSET_PONE).plusWeeks(Long.MAX_VALUE); + } + + @Test(expectedExceptions={ArithmeticException.class}, groups={"tck"}) + public void test_plusWeeks_invalidMaxMinusMin() { + OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 25), OFFSET_PONE).plusWeeks(Long.MIN_VALUE); + } + + //----------------------------------------------------------------------- + // plusDays() + //----------------------------------------------------------------------- + @DataProvider(name="samplePlusDaysSymmetry") + Object[][] provider_samplePlusDaysSymmetry() { + return new Object[][] { + {OffsetDate.of(LocalDate.of(-1, 1, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(-1, 2, 28), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(-1, 3, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(-1, 12, 31), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(0, 1, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(0, 2, 28), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(0, 2, 29), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(0, 3, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(0, 12, 31), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2007, 1, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2007, 2, 28), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2007, 3, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2007, 12, 31), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2008, 1, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2008, 2, 28), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2008, 2, 29), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2008, 3, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2008, 12, 31), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2099, 1, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2099, 2, 28), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2099, 3, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2099, 12, 31), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2100, 1, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2100, 2, 28), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2100, 3, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2100, 12, 31), OFFSET_PTWO)}, + }; + } + + @Test(dataProvider="samplePlusDaysSymmetry", groups={"tck"}) + public void test_plusDays_symmetry(OffsetDate reference) { + for (int days = 0; days < 365 * 8; days++) { + OffsetDate t = reference.plusDays(days).plusDays(-days); + assertEquals(t, reference); + + t = reference.plusDays(-days).plusDays(days); + assertEquals(t, reference); + } + } + + @Test(groups={"tck"}) + public void test_plusDays_normal() { + OffsetDate t = TEST_2007_07_15_PONE.plusDays(1); + assertEquals(t, OffsetDate.of(LocalDate.of(2007, 7, 16), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plusDays_overMonths() { + OffsetDate t = TEST_2007_07_15_PONE.plusDays(62); + assertEquals(t, OffsetDate.of(LocalDate.of(2007, 9, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plusDays_overYears() { + OffsetDate t = OffsetDate.of(LocalDate.of(2006, 7, 14), OFFSET_PONE).plusDays(366); + assertEquals(t, TEST_2007_07_15_PONE); + } + + @Test(groups={"tck"}) + public void test_plusDays_overLeapYears() { + OffsetDate t = TEST_2007_07_15_PONE.plusYears(-1).plusDays(365 + 366); + assertEquals(t, OffsetDate.of(LocalDate.of(2008, 7, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plusDays_negative() { + OffsetDate t = TEST_2007_07_15_PONE.plusDays(-1); + assertEquals(t, OffsetDate.of(LocalDate.of(2007, 7, 14), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plusDays_negativeAcrossYear() { + OffsetDate t = TEST_2007_07_15_PONE.plusDays(-196); + assertEquals(t, OffsetDate.of(LocalDate.of(2006, 12, 31), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plusDays_negativeOverYears() { + OffsetDate t = TEST_2007_07_15_PONE.plusDays(-730); + assertEquals(t, OffsetDate.of(LocalDate.of(2005, 7, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plusDays_noChange() { + OffsetDate t = TEST_2007_07_15_PONE.plusDays(0); + assertEquals(t, TEST_2007_07_15_PONE); + } + + @Test(groups={"tck"}) + public void test_plusDays_maximum() { + OffsetDate t = OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 30), OFFSET_PONE).plusDays(1); + OffsetDate expected = OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 31), OFFSET_PONE); + assertEquals(t, expected); + } + + @Test(groups={"tck"}) + public void test_plusDays_minimum() { + OffsetDate t = OffsetDate.of(LocalDate.of(Year.MIN_VALUE, 1, 2), OFFSET_PONE).plusDays(-1); + OffsetDate expected = OffsetDate.of(LocalDate.of(Year.MIN_VALUE, 1, 1), OFFSET_PONE); + assertEquals(t, expected); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_plusDays_invalidTooLarge() { + OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 31), OFFSET_PONE).plusDays(1); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_plusDays_invalidTooSmall() { + OffsetDate.of(LocalDate.of(Year.MIN_VALUE, 1, 1), OFFSET_PONE).plusDays(-1); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void test_plusDays_overflowTooLarge() { + OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 31), OFFSET_PONE).plusDays(Long.MAX_VALUE); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void test_plusDays_overflowTooSmall() { + OffsetDate.of(LocalDate.of(Year.MIN_VALUE, 1, 1), OFFSET_PONE).plusDays(Long.MIN_VALUE); + } + + //----------------------------------------------------------------------- + // minus(MinusAdjuster) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minus_MinusAdjuster() { + MockSimplePeriod period = MockSimplePeriod.of(7, ChronoUnit.MONTHS); + OffsetDate t = TEST_2007_07_15_PONE.minus(period); + assertEquals(t, OffsetDate.of(LocalDate.of(2006, 12, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minus_MinusAdjuster_noChange() { + MockSimplePeriod period = MockSimplePeriod.of(0, ChronoUnit.MONTHS); + OffsetDate t = TEST_2007_07_15_PONE.minus(period); + assertEquals(t, TEST_2007_07_15_PONE); + } + + @Test(groups={"tck"}) + public void test_minus_MinusAdjuster_zero() { + OffsetDate t = TEST_2007_07_15_PONE.minus(Period.ZERO); + assertEquals(t, TEST_2007_07_15_PONE); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_plus_MinusAdjuster_null() { + TEST_2007_07_15_PONE.minus((TemporalSubtractor) null); + } + + //----------------------------------------------------------------------- + // minusYears() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusYears_long_normal() { + OffsetDate t = TEST_2007_07_15_PONE.minusYears(1); + assertEquals(t, OffsetDate.of(LocalDate.of(2006, 7, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minusYears_long_negative() { + OffsetDate t = TEST_2007_07_15_PONE.minusYears(-1); + assertEquals(t, OffsetDate.of(LocalDate.of(2008, 7, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minusYears_long_noChange() { + OffsetDate t = TEST_2007_07_15_PONE.minusYears(0); + assertEquals(t, TEST_2007_07_15_PONE); + } + + @Test(groups={"tck"}) + public void test_minusYears_long_adjustDay() { + OffsetDate t = OffsetDate.of(LocalDate.of(2008, 2, 29), OFFSET_PONE).minusYears(1); + OffsetDate expected = OffsetDate.of(LocalDate.of(2007, 2, 28), OFFSET_PONE); + assertEquals(t, expected); + } + + @Test(groups={"tck"}) + public void test_minusYears_long_big() { + long years = 20L + Year.MAX_VALUE; + OffsetDate test = OffsetDate.of(LocalDate.of(40, 6, 1), OFFSET_PONE).minusYears(years); + assertEquals(test, OffsetDate.of(LocalDate.of((int) (40L - years), 6, 1), OFFSET_PONE)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusYears_long_invalidTooLarge() { + OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 1, 1), OFFSET_PONE).minusYears(-1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusYears_long_invalidTooLargeMaxAddMax() { + OffsetDate test = OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 1), OFFSET_PONE); + test.minusYears(Long.MAX_VALUE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusYears_long_invalidTooLargeMaxAddMin() { + OffsetDate test = OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 1), OFFSET_PONE); + test.minusYears(Long.MIN_VALUE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusYears_long_invalidTooSmall() { + OffsetDate.of(LocalDate.of(Year.MIN_VALUE, 1, 1), OFFSET_PONE).minusYears(1); + } + + //----------------------------------------------------------------------- + // minusMonths() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusMonths_long_normal() { + OffsetDate t = TEST_2007_07_15_PONE.minusMonths(1); + assertEquals(t, OffsetDate.of(LocalDate.of(2007, 6, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minusMonths_long_overYears() { + OffsetDate t = TEST_2007_07_15_PONE.minusMonths(25); + assertEquals(t, OffsetDate.of(LocalDate.of(2005, 6, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minusMonths_long_negative() { + OffsetDate t = TEST_2007_07_15_PONE.minusMonths(-1); + assertEquals(t, OffsetDate.of(LocalDate.of(2007, 8, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minusMonths_long_negativeAcrossYear() { + OffsetDate t = TEST_2007_07_15_PONE.minusMonths(-7); + assertEquals(t, OffsetDate.of(LocalDate.of(2008, 2, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minusMonths_long_negativeOverYears() { + OffsetDate t = TEST_2007_07_15_PONE.minusMonths(-31); + assertEquals(t, OffsetDate.of(LocalDate.of(2010, 2, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minusMonths_long_noChange() { + OffsetDate t = TEST_2007_07_15_PONE.minusMonths(0); + assertEquals(t, TEST_2007_07_15_PONE); + } + + @Test(groups={"tck"}) + public void test_minusMonths_long_adjustDayFromLeapYear() { + OffsetDate t = OffsetDate.of(LocalDate.of(2008, 2, 29), OFFSET_PONE).minusMonths(12); + OffsetDate expected = OffsetDate.of(LocalDate.of(2007, 2, 28), OFFSET_PONE); + assertEquals(t, expected); + } + + @Test(groups={"tck"}) + public void test_minusMonths_long_adjustDayFromMonthLength() { + OffsetDate t = OffsetDate.of(LocalDate.of(2007, 3, 31), OFFSET_PONE).minusMonths(1); + OffsetDate expected = OffsetDate.of(LocalDate.of(2007, 2, 28), OFFSET_PONE); + assertEquals(t, expected); + } + + @Test(groups={"tck"}) + public void test_minusMonths_long_big() { + long months = 20L + Integer.MAX_VALUE; + OffsetDate test = OffsetDate.of(LocalDate.of(40, 6, 1), OFFSET_PONE).minusMonths(months); + assertEquals(test, OffsetDate.of(LocalDate.of((int) (40L - months / 12), 6 - (int) (months % 12), 1), OFFSET_PONE)); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_minusMonths_long_invalidTooLarge() { + OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 1), OFFSET_PONE).minusMonths(-1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusMonths_long_invalidTooLargeMaxAddMax() { + OffsetDate test = OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 1), OFFSET_PONE); + test.minusMonths(Long.MAX_VALUE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusMonths_long_invalidTooLargeMaxAddMin() { + OffsetDate test = OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 1), OFFSET_PONE); + test.minusMonths(Long.MIN_VALUE); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_minusMonths_long_invalidTooSmall() { + OffsetDate.of(LocalDate.of(Year.MIN_VALUE, 1, 1), OFFSET_PONE).minusMonths(1); + } + + //----------------------------------------------------------------------- + // minusWeeks() + //----------------------------------------------------------------------- + @DataProvider(name="sampleMinusWeeksSymmetry") + Object[][] provider_sampleMinusWeeksSymmetry() { + return new Object[][] { + {OffsetDate.of(LocalDate.of(-1, 1, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(-1, 2, 28), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(-1, 3, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(-1, 12, 31), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(0, 1, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(0, 2, 28), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(0, 2, 29), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(0, 3, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(0, 12, 31), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2007, 1, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2007, 2, 28), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2007, 3, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2007, 12, 31), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2008, 1, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2008, 2, 28), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2008, 2, 29), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2008, 3, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2008, 12, 31), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2099, 1, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2099, 2, 28), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2099, 3, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2099, 12, 31), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2100, 1, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2100, 2, 28), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2100, 3, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2100, 12, 31), OFFSET_PTWO)}, + }; + } + + @Test(dataProvider="sampleMinusWeeksSymmetry", groups={"tck"}) + public void test_minusWeeks_symmetry(OffsetDate reference) { + for (int weeks = 0; weeks < 365 * 8; weeks++) { + OffsetDate t = reference.minusWeeks(weeks).minusWeeks(-weeks); + assertEquals(t, reference); + + t = reference.minusWeeks(-weeks).minusWeeks(weeks); + assertEquals(t, reference); + } + } + + @Test(groups={"tck"}) + public void test_minusWeeks_normal() { + OffsetDate t = TEST_2007_07_15_PONE.minusWeeks(1); + assertEquals(t, OffsetDate.of(LocalDate.of(2007, 7, 8), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_overMonths() { + OffsetDate t = TEST_2007_07_15_PONE.minusWeeks(9); + assertEquals(t, OffsetDate.of(LocalDate.of(2007, 5, 13), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_overYears() { + OffsetDate t = OffsetDate.of(LocalDate.of(2008, 7, 13), OFFSET_PONE).minusWeeks(52); + assertEquals(t, TEST_2007_07_15_PONE); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_overLeapYears() { + OffsetDate t = TEST_2007_07_15_PONE.minusYears(-1).minusWeeks(104); + assertEquals(t, OffsetDate.of(LocalDate.of(2006, 7, 18), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_negative() { + OffsetDate t = TEST_2007_07_15_PONE.minusWeeks(-1); + assertEquals(t, OffsetDate.of(LocalDate.of(2007, 7, 22), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_negativeAcrossYear() { + OffsetDate t = TEST_2007_07_15_PONE.minusWeeks(-28); + assertEquals(t, OffsetDate.of(LocalDate.of(2008, 1, 27), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_negativeOverYears() { + OffsetDate t = TEST_2007_07_15_PONE.minusWeeks(-104); + assertEquals(t, OffsetDate.of(LocalDate.of(2009, 7, 12), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_noChange() { + OffsetDate t = TEST_2007_07_15_PONE.minusWeeks(0); + assertEquals(t, TEST_2007_07_15_PONE); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_maximum() { + OffsetDate t = OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 24), OFFSET_PONE).minusWeeks(-1); + OffsetDate expected = OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 31), OFFSET_PONE); + assertEquals(t, expected); + } + + @Test(groups={"tck"}) + public void test_minusWeeks_minimum() { + OffsetDate t = OffsetDate.of(LocalDate.of(Year.MIN_VALUE, 1, 8), OFFSET_PONE).minusWeeks(1); + OffsetDate expected = OffsetDate.of(LocalDate.of(Year.MIN_VALUE, 1, 1), OFFSET_PONE); + assertEquals(t, expected); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_minusWeeks_invalidTooLarge() { + OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 25), OFFSET_PONE).minusWeeks(-1); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_minusWeeks_invalidTooSmall() { + OffsetDate.of(LocalDate.of(Year.MIN_VALUE, 1, 7), OFFSET_PONE).minusWeeks(1); + } + + @Test(expectedExceptions={ArithmeticException.class}, groups={"tck"}) + public void test_minusWeeks_invalidMaxMinusMax() { + OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 25), OFFSET_PONE).minusWeeks(Long.MAX_VALUE); + } + + @Test(expectedExceptions={ArithmeticException.class}, groups={"tck"}) + public void test_minusWeeks_invalidMaxMinusMin() { + OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 25), OFFSET_PONE).minusWeeks(Long.MIN_VALUE); + } + + //----------------------------------------------------------------------- + // minusDays() + //----------------------------------------------------------------------- + @DataProvider(name="sampleMinusDaysSymmetry") + Object[][] provider_sampleMinusDaysSymmetry() { + return new Object[][] { + {OffsetDate.of(LocalDate.of(-1, 1, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(-1, 2, 28), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(-1, 3, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(-1, 12, 31), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(0, 1, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(0, 2, 28), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(0, 2, 29), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(0, 3, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(0, 12, 31), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2007, 1, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2007, 2, 28), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2007, 3, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2007, 12, 31), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2008, 1, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2008, 2, 28), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2008, 2, 29), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2008, 3, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2008, 12, 31), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2099, 1, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2099, 2, 28), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2099, 3, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2099, 12, 31), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2100, 1, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2100, 2, 28), OFFSET_PTWO)}, + {OffsetDate.of(LocalDate.of(2100, 3, 1), OFFSET_PONE)}, + {OffsetDate.of(LocalDate.of(2100, 12, 31), OFFSET_PTWO)}, + }; + } + + @Test(dataProvider="sampleMinusDaysSymmetry", groups={"tck"}) + public void test_minusDays_symmetry(OffsetDate reference) { + for (int days = 0; days < 365 * 8; days++) { + OffsetDate t = reference.minusDays(days).minusDays(-days); + assertEquals(t, reference); + + t = reference.minusDays(-days).minusDays(days); + assertEquals(t, reference); + } + } + + @Test(groups={"tck"}) + public void test_minusDays_normal() { + OffsetDate t = TEST_2007_07_15_PONE.minusDays(1); + assertEquals(t, OffsetDate.of(LocalDate.of(2007, 7, 14), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minusDays_overMonths() { + OffsetDate t = TEST_2007_07_15_PONE.minusDays(62); + assertEquals(t, OffsetDate.of(LocalDate.of(2007, 5, 14), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minusDays_overYears() { + OffsetDate t = OffsetDate.of(LocalDate.of(2008, 7, 16), OFFSET_PONE).minusDays(367); + assertEquals(t, TEST_2007_07_15_PONE); + } + + @Test(groups={"tck"}) + public void test_minusDays_overLeapYears() { + OffsetDate t = TEST_2007_07_15_PONE.plusYears(2).minusDays(365 + 366); + assertEquals(t, TEST_2007_07_15_PONE); + } + + @Test(groups={"tck"}) + public void test_minusDays_negative() { + OffsetDate t = TEST_2007_07_15_PONE.minusDays(-1); + assertEquals(t, OffsetDate.of(LocalDate.of(2007, 7, 16), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minusDays_negativeAcrossYear() { + OffsetDate t = TEST_2007_07_15_PONE.minusDays(-169); + assertEquals(t, OffsetDate.of(LocalDate.of(2007, 12, 31), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minusDays_negativeOverYears() { + OffsetDate t = TEST_2007_07_15_PONE.minusDays(-731); + assertEquals(t, OffsetDate.of(LocalDate.of(2009, 7, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minusDays_noChange() { + OffsetDate t = TEST_2007_07_15_PONE.minusDays(0); + assertEquals(t, TEST_2007_07_15_PONE); + } + + @Test(groups={"tck"}) + public void test_minusDays_maximum() { + OffsetDate t = OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 30), OFFSET_PONE).minusDays(-1); + OffsetDate expected = OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 31), OFFSET_PONE); + assertEquals(t, expected); + } + + @Test(groups={"tck"}) + public void test_minusDays_minimum() { + OffsetDate t = OffsetDate.of(LocalDate.of(Year.MIN_VALUE, 1, 2), OFFSET_PONE).minusDays(1); + OffsetDate expected = OffsetDate.of(LocalDate.of(Year.MIN_VALUE, 1, 1), OFFSET_PONE); + assertEquals(t, expected); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_minusDays_invalidTooLarge() { + OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 31), OFFSET_PONE).minusDays(-1); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_minusDays_invalidTooSmall() { + OffsetDate.of(LocalDate.of(Year.MIN_VALUE, 1, 1), OFFSET_PONE).minusDays(1); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void test_minusDays_overflowTooLarge() { + OffsetDate.of(LocalDate.of(Year.MAX_VALUE, 12, 31), OFFSET_PONE).minusDays(Long.MIN_VALUE); + } + + @Test(expectedExceptions=ArithmeticException.class, groups={"tck"}) + public void test_minusDays_overflowTooSmall() { + OffsetDate.of(LocalDate.of(Year.MIN_VALUE, 1, 1), OFFSET_PONE).minusDays(Long.MAX_VALUE); + } + + //----------------------------------------------------------------------- + // atTime() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_atTime_Local() { + OffsetDate t = OffsetDate.of(LocalDate.of(2008, 6, 30), OFFSET_PTWO); + assertEquals(t.atTime(LocalTime.of(11, 30)), + OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30), OFFSET_PTWO)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_atTime_Local_nullLocalTime() { + OffsetDate t = OffsetDate.of(LocalDate.of(2008, 6, 30), OFFSET_PTWO); + t.atTime((LocalTime) null); + } + + //----------------------------------------------------------------------- + // getDate() + //----------------------------------------------------------------------- + @Test(dataProvider="sampleDates", groups={"tck"}) + public void test_getDate(int year, int month, int day, ZoneOffset offset) { + LocalDate t = LocalDate.of(year, month, day); + assertEquals(OffsetDate.of(LocalDate.of(year, month, day), offset).getDate(), t); + } + + //----------------------------------------------------------------------- + // compareTo() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_compareTo_date() { + OffsetDate a = OffsetDate.of(LocalDate.of(2008, 6, 29), OFFSET_PONE); + OffsetDate b = OffsetDate.of(LocalDate.of(2008, 6, 30), OFFSET_PONE); // a is before b due to date + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + assertEquals(a.atTime(LocalTime.MIDNIGHT).toInstant().compareTo(b.atTime(LocalTime.MIDNIGHT).toInstant()) < 0, true); + } + + @Test(groups={"tck"}) + public void test_compareTo_offset() { + OffsetDate a = OffsetDate.of(LocalDate.of(2008, 6, 30), OFFSET_PTWO); + OffsetDate b = OffsetDate.of(LocalDate.of(2008, 6, 30), OFFSET_PONE); // a is before b due to offset + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + assertEquals(a.atTime(LocalTime.MIDNIGHT).toInstant().compareTo(b.atTime(LocalTime.MIDNIGHT).toInstant()) < 0, true); + } + + @Test(groups={"tck"}) + public void test_compareTo_both() { + OffsetDate a = OffsetDate.of(LocalDate.of(2008, 6, 29), OFFSET_PTWO); + OffsetDate b = OffsetDate.of(LocalDate.of(2008, 6, 30), OFFSET_PONE); // a is before b on instant scale + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + assertEquals(a.atTime(LocalTime.MIDNIGHT).toInstant().compareTo(b.atTime(LocalTime.MIDNIGHT).toInstant()) < 0, true); + } + + @Test(groups={"tck"}) + public void test_compareTo_24hourDifference() { + OffsetDate a = OffsetDate.of(LocalDate.of(2008, 6, 29), ZoneOffset.ofHours(-12)); + OffsetDate b = OffsetDate.of(LocalDate.of(2008, 6, 30), ZoneOffset.ofHours(12)); // a is before b despite being same time-line time + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + assertEquals(a.atTime(LocalTime.MIDNIGHT).toInstant().compareTo(b.atTime(LocalTime.MIDNIGHT).toInstant()) == 0, true); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_compareTo_null() { + OffsetDate a = OffsetDate.of(LocalDate.of(2008, 6, 30), OFFSET_PONE); + a.compareTo(null); + } + + @Test(expectedExceptions=ClassCastException.class, groups={"tck"}) + @SuppressWarnings({"unchecked", "rawtypes"}) + public void compareToNonOffsetDate() { + Comparable c = TEST_2007_07_15_PONE; + c.compareTo(new Object()); + } + + //----------------------------------------------------------------------- + // isAfter() / isBefore() / isEqual() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_isBeforeIsAfterIsEqual1() { + OffsetDate a = OffsetDate.of(LocalDate.of(2008, 6, 29), OFFSET_PONE); + OffsetDate b = OffsetDate.of(LocalDate.of(2008, 6, 30), OFFSET_PONE); // a is before b due to time + assertEquals(a.isBefore(b), true); + assertEquals(a.isEqual(b), false); + assertEquals(a.isAfter(b), false); + + assertEquals(b.isBefore(a), false); + assertEquals(b.isEqual(a), false); + assertEquals(b.isAfter(a), true); + + assertEquals(a.isBefore(a), false); + assertEquals(b.isBefore(b), false); + + assertEquals(a.isEqual(a), true); + assertEquals(b.isEqual(b), true); + + assertEquals(a.isAfter(a), false); + assertEquals(b.isAfter(b), false); + } + + @Test(groups={"tck"}) + public void test_isBeforeIsAfterIsEqual2() { + OffsetDate a = OffsetDate.of(LocalDate.of(2008, 6, 30), OFFSET_PTWO); + OffsetDate b = OffsetDate.of(LocalDate.of(2008, 6, 30), OFFSET_PONE); // a is before b due to offset + assertEquals(a.isBefore(b), true); + assertEquals(a.isEqual(b), false); + assertEquals(a.isAfter(b), false); + + assertEquals(b.isBefore(a), false); + assertEquals(b.isEqual(a), false); + assertEquals(b.isAfter(a), true); + + assertEquals(a.isBefore(a), false); + assertEquals(b.isBefore(b), false); + + assertEquals(a.isEqual(a), true); + assertEquals(b.isEqual(b), true); + + assertEquals(a.isAfter(a), false); + assertEquals(b.isAfter(b), false); + } + + @Test(groups={"tck"}) + public void test_isBeforeIsAfterIsEqual_instantComparison() { + OffsetDate a = OffsetDate.of(LocalDate.of(2008, 6, 30), ZoneOffset.ofHours(12)); + OffsetDate b = OffsetDate.of(LocalDate.of(2008, 6, 29), ZoneOffset.ofHours(-12)); // a is same instant as b + assertEquals(a.isBefore(b), false); + assertEquals(a.isEqual(b), true); + assertEquals(a.isAfter(b), false); + + assertEquals(b.isBefore(a), false); + assertEquals(b.isEqual(a), true); + assertEquals(b.isAfter(a), false); + + assertEquals(a.isBefore(a), false); + assertEquals(b.isBefore(b), false); + + assertEquals(a.isEqual(a), true); + assertEquals(b.isEqual(b), true); + + assertEquals(a.isAfter(a), false); + assertEquals(b.isAfter(b), false); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_isBefore_null() { + OffsetDate a = OffsetDate.of(LocalDate.of(2008, 6, 30), OFFSET_PONE); + a.isBefore(null); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_isAfter_null() { + OffsetDate a = OffsetDate.of(LocalDate.of(2008, 6, 30), OFFSET_PONE); + a.isAfter(null); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_isEqual_null() { + OffsetDate a = OffsetDate.of(LocalDate.of(2008, 6, 30), OFFSET_PONE); + a.isEqual(null); + } + + //----------------------------------------------------------------------- + // equals() / hashCode() + //----------------------------------------------------------------------- + @Test(dataProvider="sampleDates", groups={"tck"}) + public void test_equals_true(int y, int m, int d, ZoneOffset offset) { + OffsetDate a = OffsetDate.of(LocalDate.of(y, m, d), offset); + OffsetDate b = OffsetDate.of(LocalDate.of(y, m, d), offset); + assertEquals(a.equals(b), true); + assertEquals(a.hashCode() == b.hashCode(), true); + } + @Test(dataProvider="sampleDates", groups={"tck"}) + public void test_equals_false_year_differs(int y, int m, int d, ZoneOffset offset) { + OffsetDate a = OffsetDate.of(LocalDate.of(y, m, d), offset); + OffsetDate b = OffsetDate.of(LocalDate.of(y + 1, m, d), offset); + assertEquals(a.equals(b), false); + } + + @Test(dataProvider="sampleDates", groups={"tck"}) + public void test_equals_false_month_differs(int y, int m, int d, ZoneOffset offset) { + OffsetDate a = OffsetDate.of(LocalDate.of(y, m, d), offset); + OffsetDate b = OffsetDate.of(LocalDate.of(y, m + 1, d), offset); + assertEquals(a.equals(b), false); + } + + @Test(dataProvider="sampleDates", groups={"tck"}) + public void test_equals_false_day_differs(int y, int m, int d, ZoneOffset offset) { + OffsetDate a = OffsetDate.of(LocalDate.of(y, m, d), offset); + OffsetDate b = OffsetDate.of(LocalDate.of(y, m, d + 1), offset); + assertEquals(a.equals(b), false); + } + + @Test(dataProvider="sampleDates", groups={"tck"}) + public void test_equals_false_offset_differs(int y, int m, int d, ZoneOffset ignored) { + OffsetDate a = OffsetDate.of(LocalDate.of(y, m, d), OFFSET_PONE); + OffsetDate b = OffsetDate.of(LocalDate.of(y, m, d), OFFSET_PTWO); + assertEquals(a.equals(b), false); + } + + @Test(groups={"tck"}) + public void test_equals_itself_true() { + assertEquals(TEST_2007_07_15_PONE.equals(TEST_2007_07_15_PONE), true); + } + + @Test(groups={"tck"}) + public void test_equals_string_false() { + assertEquals(TEST_2007_07_15_PONE.equals("2007-07-15"), false); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @DataProvider(name="sampleToString") + Object[][] provider_sampleToString() { + return new Object[][] { + {2008, 7, 5, "Z", "2008-07-05Z"}, + {2008, 7, 5, "+00", "2008-07-05Z"}, + {2008, 7, 5, "+0000", "2008-07-05Z"}, + {2008, 7, 5, "+00:00", "2008-07-05Z"}, + {2008, 7, 5, "+000000", "2008-07-05Z"}, + {2008, 7, 5, "+00:00:00", "2008-07-05Z"}, + {2008, 7, 5, "-00", "2008-07-05Z"}, + {2008, 7, 5, "-0000", "2008-07-05Z"}, + {2008, 7, 5, "-00:00", "2008-07-05Z"}, + {2008, 7, 5, "-000000", "2008-07-05Z"}, + {2008, 7, 5, "-00:00:00", "2008-07-05Z"}, + {2008, 7, 5, "+01", "2008-07-05+01:00"}, + {2008, 7, 5, "+0100", "2008-07-05+01:00"}, + {2008, 7, 5, "+01:00", "2008-07-05+01:00"}, + {2008, 7, 5, "+010000", "2008-07-05+01:00"}, + {2008, 7, 5, "+01:00:00", "2008-07-05+01:00"}, + {2008, 7, 5, "+0130", "2008-07-05+01:30"}, + {2008, 7, 5, "+01:30", "2008-07-05+01:30"}, + {2008, 7, 5, "+013000", "2008-07-05+01:30"}, + {2008, 7, 5, "+01:30:00", "2008-07-05+01:30"}, + {2008, 7, 5, "+013040", "2008-07-05+01:30:40"}, + {2008, 7, 5, "+01:30:40", "2008-07-05+01:30:40"}, + }; + } + + @Test(dataProvider="sampleToString", groups={"tck"}) + public void test_toString(int y, int m, int d, String offsetId, String expected) { + OffsetDate t = OffsetDate.of(LocalDate.of(y, m, d), ZoneOffset.of(offsetId)); + String str = t.toString(); + assertEquals(str, expected); + } + + //----------------------------------------------------------------------- + // toString(DateTimeFormatter) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toString_formatter() { + DateTimeFormatter f = DateTimeFormatters.pattern("y M d"); + String t = OffsetDate.of(LocalDate.of(2010, 12, 3), OFFSET_PONE).toString(f); + assertEquals(t, "2010 12 3"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_toString_formatter_null() { + OffsetDate.of(LocalDate.of(2010, 12, 3), OFFSET_PONE).toString(null); + } + +} diff --git a/jdk/test/java/time/tck/java/time/temporal/TCKOffsetDateTime.java b/jdk/test/java/time/tck/java/time/temporal/TCKOffsetDateTime.java new file mode 100644 index 00000000000..01d72a4a661 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/temporal/TCKOffsetDateTime.java @@ -0,0 +1,1488 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.temporal; + +import static java.time.Month.DECEMBER; +import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; +import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; +import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; +import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; +import static java.time.temporal.ChronoField.AMPM_OF_DAY; +import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_AMPM; +import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_DAY; +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.DAY_OF_WEEK; +import static java.time.temporal.ChronoField.DAY_OF_YEAR; +import static java.time.temporal.ChronoField.EPOCH_DAY; +import static java.time.temporal.ChronoField.EPOCH_MONTH; +import static java.time.temporal.ChronoField.ERA; +import static java.time.temporal.ChronoField.HOUR_OF_AMPM; +import static java.time.temporal.ChronoField.HOUR_OF_DAY; +import static java.time.temporal.ChronoField.INSTANT_SECONDS; +import static java.time.temporal.ChronoField.MICRO_OF_DAY; +import static java.time.temporal.ChronoField.MICRO_OF_SECOND; +import static java.time.temporal.ChronoField.MILLI_OF_DAY; +import static java.time.temporal.ChronoField.MILLI_OF_SECOND; +import static java.time.temporal.ChronoField.MINUTE_OF_DAY; +import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.NANO_OF_DAY; +import static java.time.temporal.ChronoField.NANO_OF_SECOND; +import static java.time.temporal.ChronoField.OFFSET_SECONDS; +import static java.time.temporal.ChronoField.SECOND_OF_DAY; +import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; +import static java.time.temporal.ChronoField.YEAR; +import static java.time.temporal.ChronoField.YEAR_OF_ERA; +import static java.time.temporal.ChronoUnit.DAYS; +import static java.time.temporal.ChronoUnit.NANOS; +import static java.time.temporal.ChronoUnit.SECONDS; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import java.time.Clock; +import java.time.DateTimeException; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Month; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatters; +import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoUnit; +import java.time.temporal.ISOChrono; +import java.time.temporal.JulianFields; +import java.time.temporal.OffsetDate; +import java.time.temporal.OffsetDateTime; +import java.time.temporal.OffsetTime; +import java.time.temporal.Queries; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalAdjuster; +import java.time.temporal.TemporalField; +import java.time.temporal.TemporalQuery; +import java.time.temporal.Year; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import tck.java.time.AbstractDateTimeTest; +import test.java.time.MockSimplePeriod; + +/** + * Test OffsetDateTime. + */ +@Test +public class TCKOffsetDateTime extends AbstractDateTimeTest { + + private static final ZoneId ZONE_PARIS = ZoneId.of("Europe/Paris"); + private static final ZoneId ZONE_GAZA = ZoneId.of("Asia/Gaza"); + private static final ZoneOffset OFFSET_PONE = ZoneOffset.ofHours(1); + private static final ZoneOffset OFFSET_PTWO = ZoneOffset.ofHours(2); + private static final ZoneOffset OFFSET_MONE = ZoneOffset.ofHours(-1); + private static final ZoneOffset OFFSET_MTWO = ZoneOffset.ofHours(-2); + private OffsetDateTime TEST_2008_6_30_11_30_59_000000500; + + @BeforeMethod(groups={"tck","implementation"}) + public void setUp() { + TEST_2008_6_30_11_30_59_000000500 = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59, 500), OFFSET_PONE); + } + + //----------------------------------------------------------------------- + @Override + protected List samples() { + TemporalAccessor[] array = {TEST_2008_6_30_11_30_59_000000500, OffsetDateTime.MIN, OffsetDateTime.MAX}; + return Arrays.asList(array); + } + + @Override + protected List validFields() { + TemporalField[] array = { + NANO_OF_SECOND, + NANO_OF_DAY, + MICRO_OF_SECOND, + MICRO_OF_DAY, + MILLI_OF_SECOND, + MILLI_OF_DAY, + SECOND_OF_MINUTE, + SECOND_OF_DAY, + MINUTE_OF_HOUR, + MINUTE_OF_DAY, + CLOCK_HOUR_OF_AMPM, + HOUR_OF_AMPM, + CLOCK_HOUR_OF_DAY, + HOUR_OF_DAY, + AMPM_OF_DAY, + DAY_OF_WEEK, + ALIGNED_DAY_OF_WEEK_IN_MONTH, + ALIGNED_DAY_OF_WEEK_IN_YEAR, + DAY_OF_MONTH, + DAY_OF_YEAR, + EPOCH_DAY, + ALIGNED_WEEK_OF_MONTH, + ALIGNED_WEEK_OF_YEAR, + MONTH_OF_YEAR, + EPOCH_MONTH, + YEAR_OF_ERA, + YEAR, + ERA, + OFFSET_SECONDS, + INSTANT_SECONDS, + JulianFields.JULIAN_DAY, + JulianFields.MODIFIED_JULIAN_DAY, + JulianFields.RATA_DIE, + }; + return Arrays.asList(array); + } + + @Override + protected List invalidFields() { + List list = new ArrayList<>(Arrays.asList(ChronoField.values())); + list.removeAll(validFields()); + return list; + } + + //----------------------------------------------------------------------- + @Test + public void test_serialization() throws Exception { + assertSerializable(TEST_2008_6_30_11_30_59_000000500); + assertSerializable(OffsetDateTime.MIN); + assertSerializable(OffsetDateTime.MAX); + } + + @Test + public void test_serialization_format() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baos) ) { + dos.writeByte(3); + } + byte[] bytes = baos.toByteArray(); + ByteArrayOutputStream baosDateTime = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baosDateTime) ) { + dos.writeByte(5); + dos.writeInt(2012); + dos.writeByte(9); + dos.writeByte(16); + dos.writeByte(22); + dos.writeByte(17); + dos.writeByte(59); + dos.writeInt(464_000_000); + } + byte[] bytesDateTime = baosDateTime.toByteArray(); + ByteArrayOutputStream baosOffset = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baosOffset) ) { + dos.writeByte(8); + dos.writeByte(4); // quarter hours stored: 3600 / 900 + } + byte[] bytesOffset = baosOffset.toByteArray(); + LocalDateTime ldt = LocalDateTime.of(2012, 9, 16, 22, 17, 59, 464_000_000); + assertSerializedBySer(OffsetDateTime.of(ldt, ZoneOffset.ofHours(1)), bytes, bytesDateTime, bytesOffset); + } + + //----------------------------------------------------------------------- + // constants + //----------------------------------------------------------------------- + @Test + public void constant_MIN() { + check(OffsetDateTime.MIN, Year.MIN_VALUE, 1, 1, 0, 0, 0, 0, ZoneOffset.MAX); + } + + @Test + public void constant_MAX() { + check(OffsetDateTime.MAX, Year.MAX_VALUE, 12, 31, 23, 59, 59, 999999999, ZoneOffset.MIN); + } + + //----------------------------------------------------------------------- + // now() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void now() { + OffsetDateTime expected = OffsetDateTime.now(Clock.systemDefaultZone()); + OffsetDateTime test = OffsetDateTime.now(); + long diff = Math.abs(test.getTime().toNanoOfDay() - expected.getTime().toNanoOfDay()); + if (diff >= 100000000) { + // may be date change + expected = OffsetDateTime.now(Clock.systemDefaultZone()); + test = OffsetDateTime.now(); + diff = Math.abs(test.getTime().toNanoOfDay() - expected.getTime().toNanoOfDay()); + } + assertTrue(diff < 100000000); // less than 0.1 secs + } + + //----------------------------------------------------------------------- + // now(Clock) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void now_Clock_allSecsInDay_utc() { + for (int i = 0; i < (2 * 24 * 60 * 60); i++) { + Instant instant = Instant.ofEpochSecond(i).plusNanos(123456789L); + Clock clock = Clock.fixed(instant, ZoneOffset.UTC); + OffsetDateTime test = OffsetDateTime.now(clock); + assertEquals(test.getYear(), 1970); + assertEquals(test.getMonth(), Month.JANUARY); + assertEquals(test.getDayOfMonth(), (i < 24 * 60 * 60 ? 1 : 2)); + assertEquals(test.getHour(), (i / (60 * 60)) % 24); + assertEquals(test.getMinute(), (i / 60) % 60); + assertEquals(test.getSecond(), i % 60); + assertEquals(test.getNano(), 123456789); + assertEquals(test.getOffset(), ZoneOffset.UTC); + } + } + + @Test(groups={"tck"}) + public void now_Clock_allSecsInDay_offset() { + for (int i = 0; i < (2 * 24 * 60 * 60); i++) { + Instant instant = Instant.ofEpochSecond(i).plusNanos(123456789L); + Clock clock = Clock.fixed(instant.minusSeconds(OFFSET_PONE.getTotalSeconds()), OFFSET_PONE); + OffsetDateTime test = OffsetDateTime.now(clock); + assertEquals(test.getYear(), 1970); + assertEquals(test.getMonth(), Month.JANUARY); + assertEquals(test.getDayOfMonth(), (i < 24 * 60 * 60) ? 1 : 2); + assertEquals(test.getHour(), (i / (60 * 60)) % 24); + assertEquals(test.getMinute(), (i / 60) % 60); + assertEquals(test.getSecond(), i % 60); + assertEquals(test.getNano(), 123456789); + assertEquals(test.getOffset(), OFFSET_PONE); + } + } + + @Test(groups={"tck"}) + public void now_Clock_allSecsInDay_beforeEpoch() { + LocalTime expected = LocalTime.MIDNIGHT.plusNanos(123456789L); + for (int i =-1; i >= -(24 * 60 * 60); i--) { + Instant instant = Instant.ofEpochSecond(i).plusNanos(123456789L); + Clock clock = Clock.fixed(instant, ZoneOffset.UTC); + OffsetDateTime test = OffsetDateTime.now(clock); + assertEquals(test.getYear(), 1969); + assertEquals(test.getMonth(), Month.DECEMBER); + assertEquals(test.getDayOfMonth(), 31); + expected = expected.minusSeconds(1); + assertEquals(test.getTime(), expected); + assertEquals(test.getOffset(), ZoneOffset.UTC); + } + } + + @Test(groups={"tck"}) + public void now_Clock_offsets() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(1970, 1, 1), LocalTime.of(12, 0), ZoneOffset.UTC); + for (int i = -9; i < 15; i++) { + ZoneOffset offset = ZoneOffset.ofHours(i); + Clock clock = Clock.fixed(base.toInstant(), offset); + OffsetDateTime test = OffsetDateTime.now(clock); + assertEquals(test.getHour(), (12 + i) % 24); + assertEquals(test.getMinute(), 0); + assertEquals(test.getSecond(), 0); + assertEquals(test.getNano(), 0); + assertEquals(test.getOffset(), offset); + } + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void now_Clock_nullZoneId() { + OffsetDateTime.now((ZoneId) null); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void now_Clock_nullClock() { + OffsetDateTime.now((Clock) null); + } + + //----------------------------------------------------------------------- + private void check(OffsetDateTime test, int y, int mo, int d, int h, int m, int s, int n, ZoneOffset offset) { + assertEquals(test.getYear(), y); + assertEquals(test.getMonth().getValue(), mo); + assertEquals(test.getDayOfMonth(), d); + assertEquals(test.getHour(), h); + assertEquals(test.getMinute(), m); + assertEquals(test.getSecond(), s); + assertEquals(test.getNano(), n); + assertEquals(test.getOffset(), offset); + assertEquals(test, test); + assertEquals(test.hashCode(), test.hashCode()); + assertEquals(OffsetDateTime.of(LocalDateTime.of(y, mo, d, h, m, s, n), offset), test); + } + + //----------------------------------------------------------------------- + // dateTime factories + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_of_intMonthIntHM() { + OffsetDateTime test = OffsetDateTime.of(LocalDate.of(2008, Month.JUNE, 30), + LocalTime.of(11, 30), OFFSET_PONE); + check(test, 2008, 6, 30, 11, 30, 0, 0, OFFSET_PONE); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_of_intMonthIntHMS() { + OffsetDateTime test = OffsetDateTime.of(LocalDate.of(2008, Month.JUNE, 30), + LocalTime.of(11, 30, 10), OFFSET_PONE); + check(test, 2008, 6, 30, 11, 30, 10, 0, OFFSET_PONE); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_of_intMonthIntHMSN() { + OffsetDateTime test = OffsetDateTime.of(LocalDate.of(2008, Month.JUNE, 30), + LocalTime.of(11, 30, 10, 500), OFFSET_PONE); + check(test, 2008, 6, 30, 11, 30, 10, 500, OFFSET_PONE); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_of_intsHM() { + OffsetDateTime test = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30), OFFSET_PONE); + check(test, 2008, 6, 30, 11, 30, 0, 0, OFFSET_PONE); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_of_intsHMS() { + OffsetDateTime test = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 10), OFFSET_PONE); + check(test, 2008, 6, 30, 11, 30, 10, 0, OFFSET_PONE); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_of_intsHMSN() { + OffsetDateTime test = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 10, 500), OFFSET_PONE); + check(test, 2008, 6, 30, 11, 30, 10, 500, OFFSET_PONE); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_of_LocalDateLocalTimeZoneOffset() { + LocalDate date = LocalDate.of(2008, 6, 30); + LocalTime time = LocalTime.of(11, 30, 10, 500); + OffsetDateTime test = OffsetDateTime.of(date, time, OFFSET_PONE); + check(test, 2008, 6, 30, 11, 30, 10, 500, OFFSET_PONE); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_of_LocalDateLocalTimeZoneOffset_nullLocalDate() { + LocalTime time = LocalTime.of(11, 30, 10, 500); + OffsetDateTime.of((LocalDate) null, time, OFFSET_PONE); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_of_LocalDateLocalTimeZoneOffset_nullLocalTime() { + LocalDate date = LocalDate.of(2008, 6, 30); + OffsetDateTime.of(date, (LocalTime) null, OFFSET_PONE); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_of_LocalDateLocalTimeZoneOffset_nullOffset() { + LocalDate date = LocalDate.of(2008, 6, 30); + LocalTime time = LocalTime.of(11, 30, 10, 500); + OffsetDateTime.of(date, time, (ZoneOffset) null); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_of_LocalDateTimeZoneOffset() { + LocalDateTime dt = LocalDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 10, 500)); + OffsetDateTime test = OffsetDateTime.of(dt, OFFSET_PONE); + check(test, 2008, 6, 30, 11, 30, 10, 500, OFFSET_PONE); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_of_LocalDateTimeZoneOffset_nullProvider() { + OffsetDateTime.of((LocalDateTime) null, OFFSET_PONE); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_of_LocalDateTimeZoneOffset_nullOffset() { + LocalDateTime dt = LocalDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 10, 500)); + OffsetDateTime.of(dt, (ZoneOffset) null); + } + + //----------------------------------------------------------------------- + // from() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_factory_CalendricalObject() { + assertEquals(OffsetDateTime.from( + OffsetDateTime.of(LocalDate.of(2007, 7, 15), LocalTime.of(17, 30), OFFSET_PONE)), + OffsetDateTime.of(LocalDate.of(2007, 7, 15), LocalTime.of(17, 30), OFFSET_PONE)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_CalendricalObject_invalid_noDerive() { + OffsetDateTime.from(LocalTime.of(12, 30)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_factory_Calendricals_null() { + OffsetDateTime.from((TemporalAccessor) null); + } + + //----------------------------------------------------------------------- + // parse() + //----------------------------------------------------------------------- + @Test(dataProvider="sampleToString", groups={"tck"}) + public void test_parse(int y, int month, int d, int h, int m, int s, int n, String offsetId, String text) { + OffsetDateTime t = OffsetDateTime.parse(text); + assertEquals(t.getYear(), y); + assertEquals(t.getMonth().getValue(), month); + assertEquals(t.getDayOfMonth(), d); + assertEquals(t.getHour(), h); + assertEquals(t.getMinute(), m); + assertEquals(t.getSecond(), s); + assertEquals(t.getNano(), n); + assertEquals(t.getOffset().getId(), offsetId); + } + + @Test(expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_illegalValue() { + OffsetDateTime.parse("2008-06-32T11:15+01:00"); + } + + @Test(expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_invalidValue() { + OffsetDateTime.parse("2008-06-31T11:15+01:00"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_nullText() { + OffsetDateTime.parse((String) null); + } + + //----------------------------------------------------------------------- + // parse(DateTimeFormatter) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_parse_formatter() { + DateTimeFormatter f = DateTimeFormatters.pattern("y M d H m s XXX"); + OffsetDateTime test = OffsetDateTime.parse("2010 12 3 11 30 0 +01:00", f); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2010, 12, 3), LocalTime.of(11, 30), ZoneOffset.ofHours(1))); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_formatter_nullText() { + DateTimeFormatter f = DateTimeFormatters.pattern("y M d H m s"); + OffsetDateTime.parse((String) null, f); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_formatter_nullFormatter() { + OffsetDateTime.parse("ANY", null); + } + + //----------------------------------------------------------------------- + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void constructor_nullTime() throws Throwable { + Constructor con = OffsetDateTime.class.getDeclaredConstructor(LocalDateTime.class, ZoneOffset.class); + con.setAccessible(true); + try { + con.newInstance(null, OFFSET_PONE); + } catch (InvocationTargetException ex) { + throw ex.getCause(); + } + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void constructor_nullOffset() throws Throwable { + Constructor con = OffsetDateTime.class.getDeclaredConstructor(LocalDateTime.class, ZoneOffset.class); + con.setAccessible(true); + try { + con.newInstance(LocalDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30)), null); + } catch (InvocationTargetException ex) { + throw ex.getCause(); + } + } + + //----------------------------------------------------------------------- + // basics + //----------------------------------------------------------------------- + @DataProvider(name="sampleTimes") + Object[][] provider_sampleTimes() { + return new Object[][] { + {2008, 6, 30, 11, 30, 20, 500, OFFSET_PONE}, + {2008, 6, 30, 11, 0, 0, 0, OFFSET_PONE}, + {2008, 6, 30, 23, 59, 59, 999999999, OFFSET_PONE}, + {-1, 1, 1, 0, 0, 0, 0, OFFSET_PONE}, + }; + } + + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_get(int y, int o, int d, int h, int m, int s, int n, ZoneOffset offset) { + LocalDate localDate = LocalDate.of(y, o, d); + LocalTime localTime = LocalTime.of(h, m, s, n); + LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime); + OffsetDateTime a = OffsetDateTime.of(localDateTime, offset); + + assertEquals(a.getYear(), localDate.getYear()); + assertEquals(a.getMonth(), localDate.getMonth()); + assertEquals(a.getDayOfMonth(), localDate.getDayOfMonth()); + assertEquals(a.getDayOfYear(), localDate.getDayOfYear()); + assertEquals(a.getDayOfWeek(), localDate.getDayOfWeek()); + + assertEquals(a.getHour(), localDateTime.getHour()); + assertEquals(a.getMinute(), localDateTime.getMinute()); + assertEquals(a.getSecond(), localDateTime.getSecond()); + assertEquals(a.getNano(), localDateTime.getNano()); + + assertEquals(a.toOffsetDate(), OffsetDate.of(localDate, offset)); + assertEquals(a.toOffsetTime(), OffsetTime.of(localTime, offset)); + assertEquals(a.toString(), localDateTime.toString() + offset.toString()); + } + + //----------------------------------------------------------------------- + // get(TemporalField) + //----------------------------------------------------------------------- + @Test + public void test_get_TemporalField() { + OffsetDateTime test = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(12, 30, 40, 987654321), OFFSET_PONE); + assertEquals(test.get(ChronoField.YEAR), 2008); + assertEquals(test.get(ChronoField.MONTH_OF_YEAR), 6); + assertEquals(test.get(ChronoField.DAY_OF_MONTH), 30); + assertEquals(test.get(ChronoField.DAY_OF_WEEK), 1); + assertEquals(test.get(ChronoField.DAY_OF_YEAR), 182); + + assertEquals(test.get(ChronoField.HOUR_OF_DAY), 12); + assertEquals(test.get(ChronoField.MINUTE_OF_HOUR), 30); + assertEquals(test.get(ChronoField.SECOND_OF_MINUTE), 40); + assertEquals(test.get(ChronoField.NANO_OF_SECOND), 987654321); + assertEquals(test.get(ChronoField.HOUR_OF_AMPM), 0); + assertEquals(test.get(ChronoField.AMPM_OF_DAY), 1); + + assertEquals(test.get(ChronoField.OFFSET_SECONDS), 3600); + } + + @Test + public void test_getLong_TemporalField() { + OffsetDateTime test = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(12, 30, 40, 987654321), OFFSET_PONE); + assertEquals(test.getLong(ChronoField.YEAR), 2008); + assertEquals(test.getLong(ChronoField.MONTH_OF_YEAR), 6); + assertEquals(test.getLong(ChronoField.DAY_OF_MONTH), 30); + assertEquals(test.getLong(ChronoField.DAY_OF_WEEK), 1); + assertEquals(test.getLong(ChronoField.DAY_OF_YEAR), 182); + + assertEquals(test.getLong(ChronoField.HOUR_OF_DAY), 12); + assertEquals(test.getLong(ChronoField.MINUTE_OF_HOUR), 30); + assertEquals(test.getLong(ChronoField.SECOND_OF_MINUTE), 40); + assertEquals(test.getLong(ChronoField.NANO_OF_SECOND), 987654321); + assertEquals(test.getLong(ChronoField.HOUR_OF_AMPM), 0); + assertEquals(test.getLong(ChronoField.AMPM_OF_DAY), 1); + + assertEquals(test.getLong(ChronoField.INSTANT_SECONDS), test.toEpochSecond()); + assertEquals(test.getLong(ChronoField.OFFSET_SECONDS), 3600); + } + + //----------------------------------------------------------------------- + // query(TemporalQuery) + //----------------------------------------------------------------------- + @Test + public void test_query_chrono() { + assertEquals(TEST_2008_6_30_11_30_59_000000500.query(Queries.chrono()), ISOChrono.INSTANCE); + assertEquals(Queries.chrono().queryFrom(TEST_2008_6_30_11_30_59_000000500), ISOChrono.INSTANCE); + } + + @Test + public void test_query_zoneId() { + assertEquals(TEST_2008_6_30_11_30_59_000000500.query(Queries.zoneId()), null); + assertEquals(Queries.zoneId().queryFrom(TEST_2008_6_30_11_30_59_000000500), null); + } + + @Test + public void test_query_precision() { + assertEquals(TEST_2008_6_30_11_30_59_000000500.query(Queries.precision()), NANOS); + assertEquals(Queries.precision().queryFrom(TEST_2008_6_30_11_30_59_000000500), NANOS); + } + + @Test + public void test_query_offset() { + assertEquals(TEST_2008_6_30_11_30_59_000000500.query(Queries.offset()), OFFSET_PONE); + assertEquals(Queries.offset().queryFrom(TEST_2008_6_30_11_30_59_000000500), OFFSET_PONE); + } + + @Test + public void test_query_zone() { + assertEquals(TEST_2008_6_30_11_30_59_000000500.query(Queries.zone()), OFFSET_PONE); + assertEquals(Queries.zone().queryFrom(TEST_2008_6_30_11_30_59_000000500), OFFSET_PONE); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_query_null() { + TEST_2008_6_30_11_30_59_000000500.query(null); + } + + //----------------------------------------------------------------------- + // with(WithAdjuster) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_with_adjustment() { + final OffsetDateTime sample = OffsetDateTime.of(LocalDate.of(2012, 3, 4), LocalTime.of(23, 5), OFFSET_PONE); + TemporalAdjuster adjuster = new TemporalAdjuster() { + @Override + public Temporal adjustInto(Temporal dateTime) { + return sample; + } + }; + assertEquals(TEST_2008_6_30_11_30_59_000000500.with(adjuster), sample); + } + + @Test(groups={"tck"}) + public void test_with_adjustment_LocalDate() { + OffsetDateTime test = TEST_2008_6_30_11_30_59_000000500.with(LocalDate.of(2012, 9, 3)); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2012, 9, 3), LocalTime.of(11, 30, 59, 500), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_with_adjustment_LocalTime() { + OffsetDateTime test = TEST_2008_6_30_11_30_59_000000500.with(LocalTime.of(19, 15)); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(19, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_with_adjustment_LocalDateTime() { + OffsetDateTime test = TEST_2008_6_30_11_30_59_000000500.with(LocalDateTime.of(LocalDate.of(2012, 9, 3), LocalTime.of(19, 15))); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2012, 9, 3), LocalTime.of(19, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_with_adjustment_OffsetDate() { + OffsetDateTime test = TEST_2008_6_30_11_30_59_000000500.with(OffsetDate.of(LocalDate.of(2012, 9, 3), OFFSET_PTWO)); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2012, 9, 3), LocalTime.of(11, 30, 59, 500), OFFSET_PTWO)); + } + + @Test(groups={"tck"}) + public void test_with_adjustment_OffsetTime() { + OffsetDateTime test = TEST_2008_6_30_11_30_59_000000500.with(OffsetTime.of(LocalTime.of(19, 15), OFFSET_PTWO)); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(19, 15), OFFSET_PTWO)); + } + + @Test(groups={"tck"}) + public void test_with_adjustment_OffsetDateTime() { + OffsetDateTime test = TEST_2008_6_30_11_30_59_000000500.with(OffsetDateTime.of(LocalDate.of(2012, 9, 3), LocalTime.of(19, 15), OFFSET_PTWO)); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2012, 9, 3), LocalTime.of(19, 15), OFFSET_PTWO)); + } + + @Test(groups={"tck"}) + public void test_with_adjustment_Month() { + OffsetDateTime test = TEST_2008_6_30_11_30_59_000000500.with(DECEMBER); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2008, 12, 30),LocalTime.of(11, 30, 59, 500), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_with_adjustment_ZoneOffset() { + OffsetDateTime test = TEST_2008_6_30_11_30_59_000000500.with(OFFSET_PTWO); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59, 500), OFFSET_PTWO)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_with_adjustment_null() { + TEST_2008_6_30_11_30_59_000000500.with((TemporalAdjuster) null); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_withOffsetSameLocal_null() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + base.withOffsetSameLocal(null); + } + + //----------------------------------------------------------------------- + // withOffsetSameInstant() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withOffsetSameInstant() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.withOffsetSameInstant(OFFSET_PTWO); + OffsetDateTime expected = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(12, 30, 59), OFFSET_PTWO); + assertEquals(test, expected); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_withOffsetSameInstant_null() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + base.withOffsetSameInstant(null); + } + + //----------------------------------------------------------------------- + // withYear() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withYear_normal() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.withYear(2007); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2007, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // withMonth() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withMonth_normal() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.withMonth(1); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2008, 1, 30), LocalTime.of(11, 30, 59), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // withDayOfMonth() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withDayOfMonth_normal() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.withDayOfMonth(15); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2008, 6, 15), LocalTime.of(11, 30, 59), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // withDayOfYear(int) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withDayOfYear_normal() { + OffsetDateTime t = TEST_2008_6_30_11_30_59_000000500.withDayOfYear(33); + assertEquals(t, OffsetDateTime.of(LocalDate.of(2008, 2, 2), LocalTime.of(11, 30, 59, 500), OFFSET_PONE)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withDayOfYear_illegal() { + TEST_2008_6_30_11_30_59_000000500.withDayOfYear(367); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withDayOfYear_invalid() { + OffsetDateTime.of(LocalDate.of(2007, 2, 2), LocalTime.of(11, 30), OFFSET_PONE).withDayOfYear(366); + } + + //----------------------------------------------------------------------- + // withHour() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withHour_normal() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.withHour(15); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(15, 30, 59), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // withMinute() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withMinute_normal() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.withMinute(15); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 15, 59), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // withSecond() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withSecond_normal() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.withSecond(15); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 15), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // withNano() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withNanoOfSecond_normal() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59, 1), OFFSET_PONE); + OffsetDateTime test = base.withNano(15); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59, 15), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // truncatedTo(TemporalUnit) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_truncatedTo_normal() { + assertEquals(TEST_2008_6_30_11_30_59_000000500.truncatedTo(NANOS), TEST_2008_6_30_11_30_59_000000500); + assertEquals(TEST_2008_6_30_11_30_59_000000500.truncatedTo(SECONDS), TEST_2008_6_30_11_30_59_000000500.withNano(0)); + assertEquals(TEST_2008_6_30_11_30_59_000000500.truncatedTo(DAYS), TEST_2008_6_30_11_30_59_000000500.with(LocalTime.MIDNIGHT)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_truncatedTo_null() { + TEST_2008_6_30_11_30_59_000000500.truncatedTo(null); + } + + //----------------------------------------------------------------------- + // plus(Period) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plus_Period() { + MockSimplePeriod period = MockSimplePeriod.of(7, ChronoUnit.MONTHS); + OffsetDateTime t = TEST_2008_6_30_11_30_59_000000500.plus(period); + assertEquals(t, OffsetDateTime.of(LocalDate.of(2009, 1, 30), LocalTime.of(11, 30, 59, 500), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // plus(Duration) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plus_Duration() { + Duration dur = Duration.ofSeconds(62, 3); + OffsetDateTime t = TEST_2008_6_30_11_30_59_000000500.plus(dur); + assertEquals(t, OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 32, 1, 503), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plus_Duration_zero() { + OffsetDateTime t = TEST_2008_6_30_11_30_59_000000500.plus(Duration.ZERO); + assertEquals(t, TEST_2008_6_30_11_30_59_000000500); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_plus_Duration_null() { + TEST_2008_6_30_11_30_59_000000500.plus((Duration) null); + } + + //----------------------------------------------------------------------- + // plusYears() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusYears() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.plusYears(1); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2009, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // plusMonths() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusMonths() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.plusMonths(1); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2008, 7, 30), LocalTime.of(11, 30, 59), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // plusWeeks() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusWeeks() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.plusWeeks(1); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2008, 7, 7), LocalTime.of(11, 30, 59), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // plusDays() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusDays() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.plusDays(1); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2008, 7, 1), LocalTime.of(11, 30, 59), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // plusHours() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusHours() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.plusHours(13); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2008, 7, 1), LocalTime.of(0, 30, 59), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // plusMinutes() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusMinutes() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.plusMinutes(30); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(12, 0, 59), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // plusSeconds() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusSeconds() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.plusSeconds(1); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 31, 0), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // plusNanos() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusNanos() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59, 0), OFFSET_PONE); + OffsetDateTime test = base.plusNanos(1); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59, 1), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // minus(Period) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minus_Period() { + MockSimplePeriod period = MockSimplePeriod.of(7, ChronoUnit.MONTHS); + OffsetDateTime t = TEST_2008_6_30_11_30_59_000000500.minus(period); + assertEquals(t, OffsetDateTime.of(LocalDate.of(2007, 11, 30), LocalTime.of(11, 30, 59, 500), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // minus(Duration) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minus_Duration() { + Duration dur = Duration.ofSeconds(62, 3); + OffsetDateTime t = TEST_2008_6_30_11_30_59_000000500.minus(dur); + assertEquals(t, OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 29, 57, 497), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minus_Duration_zero() { + OffsetDateTime t = TEST_2008_6_30_11_30_59_000000500.minus(Duration.ZERO); + assertEquals(t, TEST_2008_6_30_11_30_59_000000500); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_minus_Duration_null() { + TEST_2008_6_30_11_30_59_000000500.minus((Duration) null); + } + + //----------------------------------------------------------------------- + // minusYears() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusYears() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.minusYears(1); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2007, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // minusMonths() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusMonths() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.minusMonths(1); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2008, 5, 30), LocalTime.of(11, 30, 59), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // minusWeeks() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusWeeks() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.minusWeeks(1); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2008, 6, 23), LocalTime.of(11, 30, 59), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // minusDays() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusDays() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.minusDays(1); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2008, 6, 29), LocalTime.of(11, 30, 59), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // minusHours() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusHours() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.minusHours(13); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2008, 6, 29), LocalTime.of(22, 30, 59), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // minusMinutes() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusMinutes() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.minusMinutes(30); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 0, 59), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // minusSeconds() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusSeconds() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.minusSeconds(1); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 58), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // minusNanos() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusNanos() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59, 0), OFFSET_PONE); + OffsetDateTime test = base.minusNanos(1); + assertEquals(test, OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 58, 999999999), OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // atZoneSameInstant() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_atZone() { + OffsetDateTime t = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30), OFFSET_MTWO); + assertEquals(t.atZoneSameInstant(ZONE_PARIS), + ZonedDateTime.of(LocalDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(15, 30)), ZONE_PARIS)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_atZone_nullTimeZone() { + OffsetDateTime t = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30), OFFSET_PTWO); + t.atZoneSameInstant((ZoneId) null); + } + + //----------------------------------------------------------------------- + // atZoneSimilarLocal() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_atZoneSimilarLocal() { + OffsetDateTime t = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30), OFFSET_MTWO); + assertEquals(t.atZoneSimilarLocal(ZONE_PARIS), + ZonedDateTime.of(LocalDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30)), ZONE_PARIS)); + } + + @Test(groups={"tck"}) + public void test_atZoneSimilarLocal_dstGap() { + OffsetDateTime t = OffsetDateTime.of(LocalDate.of(2007, 4, 1), LocalTime.of(0, 0), OFFSET_MTWO); + assertEquals(t.atZoneSimilarLocal(ZONE_GAZA), + ZonedDateTime.of(LocalDateTime.of(LocalDate.of(2007, 4, 1), LocalTime.of(1, 0)), ZONE_GAZA)); + } + + @Test(groups={"tck"}) + public void test_atZone_dstOverlapSummer() { + OffsetDateTime t = OffsetDateTime.of(LocalDate.of(2007, 10, 28), LocalTime.of(2, 30), OFFSET_PTWO); + assertEquals(t.atZoneSimilarLocal(ZONE_PARIS).getDateTime(), t.getDateTime()); + assertEquals(t.atZoneSimilarLocal(ZONE_PARIS).getOffset(), OFFSET_PTWO); + assertEquals(t.atZoneSimilarLocal(ZONE_PARIS).getZone(), ZONE_PARIS); + } + + @Test(groups={"tck"}) + public void test_atZone_dstOverlapWinter() { + OffsetDateTime t = OffsetDateTime.of(LocalDate.of(2007, 10, 28), LocalTime.of(2, 30), OFFSET_PONE); + assertEquals(t.atZoneSimilarLocal(ZONE_PARIS).getDateTime(), t.getDateTime()); + assertEquals(t.atZoneSimilarLocal(ZONE_PARIS).getOffset(), OFFSET_PONE); + assertEquals(t.atZoneSimilarLocal(ZONE_PARIS).getZone(), ZONE_PARIS); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_atZoneSimilarLocal_nullTimeZone() { + OffsetDateTime t = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30), OFFSET_PTWO); + t.atZoneSimilarLocal((ZoneId) null); + } + + //----------------------------------------------------------------------- + // toEpochSecond() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toEpochSecond_afterEpoch() { + for (int i = 0; i < 100000; i++) { + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(1970, 1, 1), LocalTime.of(0, 0), ZoneOffset.UTC).plusSeconds(i); + assertEquals(a.toEpochSecond(), i); + } + } + + @Test(groups={"tck"}) + public void test_toEpochSecond_beforeEpoch() { + for (int i = 0; i < 100000; i++) { + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(1970, 1, 1), LocalTime.of(0, 0), ZoneOffset.UTC).minusSeconds(i); + assertEquals(a.toEpochSecond(), -i); + } + } + + //----------------------------------------------------------------------- + // compareTo() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_compareTo_timeMins() { + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 29, 3), OFFSET_PONE); + OffsetDateTime b = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 2), OFFSET_PONE); // a is before b due to time + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + assertEquals(a.toInstant().compareTo(b.toInstant()) < 0, true); + } + + @Test(groups={"tck"}) + public void test_compareTo_timeSecs() { + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 29, 2), OFFSET_PONE); + OffsetDateTime b = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 29, 3), OFFSET_PONE); // a is before b due to time + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + assertEquals(a.toInstant().compareTo(b.toInstant()) < 0, true); + } + + @Test(groups={"tck"}) + public void test_compareTo_timeNanos() { + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 29, 40, 4), OFFSET_PONE); + OffsetDateTime b = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 29, 40, 5), OFFSET_PONE); // a is before b due to time + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + assertEquals(a.toInstant().compareTo(b.toInstant()) < 0, true); + } + + @Test(groups={"tck"}) + public void test_compareTo_offset() { + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30), OFFSET_PTWO); + OffsetDateTime b = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30), OFFSET_PONE); // a is before b due to offset + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + assertEquals(a.toInstant().compareTo(b.toInstant()) < 0, true); + } + + @Test(groups={"tck"}) + public void test_compareTo_offsetNanos() { + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 40, 6), OFFSET_PTWO); + OffsetDateTime b = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 40, 5), OFFSET_PONE); // a is before b due to offset + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + assertEquals(a.toInstant().compareTo(b.toInstant()) < 0, true); + } + + @Test(groups={"tck"}) + public void test_compareTo_both() { + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 50), OFFSET_PTWO); + OffsetDateTime b = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 20), OFFSET_PONE); // a is before b on instant scale + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + assertEquals(a.toInstant().compareTo(b.toInstant()) < 0, true); + } + + @Test(groups={"tck"}) + public void test_compareTo_bothNanos() { + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 20, 40, 4), OFFSET_PTWO); + OffsetDateTime b = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(10, 20, 40, 5), OFFSET_PONE); // a is before b on instant scale + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + assertEquals(a.toInstant().compareTo(b.toInstant()) < 0, true); + } + + @Test(groups={"tck"}) + public void test_compareTo_hourDifference() { + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(10, 0), OFFSET_PONE); + OffsetDateTime b = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 0), OFFSET_PTWO); // a is before b despite being same time-line time + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + assertEquals(a.toInstant().compareTo(b.toInstant()) == 0, true); + } + + @Test(groups={"tck"}) + public void test_compareTo_max() { + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(Year.MAX_VALUE, 12, 31), LocalTime.of(23, 59), OFFSET_MONE); + OffsetDateTime b = OffsetDateTime.of(LocalDate.of(Year.MAX_VALUE, 12, 31), LocalTime.of(23, 59), OFFSET_MTWO); // a is before b due to offset + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + } + + @Test(groups={"tck"}) + public void test_compareTo_min() { + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(Year.MIN_VALUE, 1, 1), LocalTime.of(0, 0), OFFSET_PTWO); + OffsetDateTime b = OffsetDateTime.of(LocalDate.of(Year.MIN_VALUE, 1, 1), LocalTime.of(0, 0), OFFSET_PONE); // a is before b due to offset + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_compareTo_null() { + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + a.compareTo(null); + } + + @Test(expectedExceptions=ClassCastException.class, groups={"tck"}) + @SuppressWarnings({"unchecked", "rawtypes"}) + public void compareToNonOffsetDateTime() { + Comparable c = TEST_2008_6_30_11_30_59_000000500; + c.compareTo(new Object()); + } + + //----------------------------------------------------------------------- + // isAfter() / isBefore() / isEqual() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_isBeforeIsAfterIsEqual1() { + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 58, 3), OFFSET_PONE); + OffsetDateTime b = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59, 2), OFFSET_PONE); // a is before b due to time + assertEquals(a.isBefore(b), true); + assertEquals(a.isEqual(b), false); + assertEquals(a.isAfter(b), false); + + assertEquals(b.isBefore(a), false); + assertEquals(b.isEqual(a), false); + assertEquals(b.isAfter(a), true); + + assertEquals(a.isBefore(a), false); + assertEquals(b.isBefore(b), false); + + assertEquals(a.isEqual(a), true); + assertEquals(b.isEqual(b), true); + + assertEquals(a.isAfter(a), false); + assertEquals(b.isAfter(b), false); + } + + @Test(groups={"tck"}) + public void test_isBeforeIsAfterIsEqual2() { + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59, 2), OFFSET_PONE); + OffsetDateTime b = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59, 3), OFFSET_PONE); // a is before b due to time + assertEquals(a.isBefore(b), true); + assertEquals(a.isEqual(b), false); + assertEquals(a.isAfter(b), false); + + assertEquals(b.isBefore(a), false); + assertEquals(b.isEqual(a), false); + assertEquals(b.isAfter(a), true); + + assertEquals(a.isBefore(a), false); + assertEquals(b.isBefore(b), false); + + assertEquals(a.isEqual(a), true); + assertEquals(b.isEqual(b), true); + + assertEquals(a.isAfter(a), false); + assertEquals(b.isAfter(b), false); + } + + @Test(groups={"tck"}) + public void test_isBeforeIsAfterIsEqual_instantComparison() { + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(10, 0), OFFSET_PONE); + OffsetDateTime b = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 0), OFFSET_PTWO); // a is same instant as b + assertEquals(a.isBefore(b), false); + assertEquals(a.isEqual(b), true); + assertEquals(a.isAfter(b), false); + + assertEquals(b.isBefore(a), false); + assertEquals(b.isEqual(a), true); + assertEquals(b.isAfter(a), false); + + assertEquals(a.isBefore(a), false); + assertEquals(b.isBefore(b), false); + + assertEquals(a.isEqual(a), true); + assertEquals(b.isEqual(b), true); + + assertEquals(a.isAfter(a), false); + assertEquals(b.isAfter(b), false); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_isBefore_null() { + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + a.isBefore(null); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_isEqual_null() { + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + a.isEqual(null); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_isAfter_null() { + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + a.isAfter(null); + } + + //----------------------------------------------------------------------- + // equals() / hashCode() + //----------------------------------------------------------------------- + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_true(int y, int o, int d, int h, int m, int s, int n, ZoneOffset ignored) { + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(y, o, d), LocalTime.of(h, m, s, n), OFFSET_PONE); + OffsetDateTime b = OffsetDateTime.of(LocalDate.of(y, o, d), LocalTime.of(h, m, s, n), OFFSET_PONE); + assertEquals(a.equals(b), true); + assertEquals(a.hashCode() == b.hashCode(), true); + } + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_false_year_differs(int y, int o, int d, int h, int m, int s, int n, ZoneOffset ignored) { + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(y, o, d), LocalTime.of(h, m, s, n), OFFSET_PONE); + OffsetDateTime b = OffsetDateTime.of(LocalDate.of(y + 1, o, d), LocalTime.of(h, m, s, n), OFFSET_PONE); + assertEquals(a.equals(b), false); + } + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_false_hour_differs(int y, int o, int d, int h, int m, int s, int n, ZoneOffset ignored) { + h = (h == 23 ? 22 : h); + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(y, o, d), LocalTime.of(h, m, s, n), OFFSET_PONE); + OffsetDateTime b = OffsetDateTime.of(LocalDate.of(y, o, d), LocalTime.of(h + 1, m, s, n), OFFSET_PONE); + assertEquals(a.equals(b), false); + } + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_false_minute_differs(int y, int o, int d, int h, int m, int s, int n, ZoneOffset ignored) { + m = (m == 59 ? 58 : m); + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(y, o, d), LocalTime.of(h, m, s, n), OFFSET_PONE); + OffsetDateTime b = OffsetDateTime.of(LocalDate.of(y, o, d), LocalTime.of(h, m + 1, s, n), OFFSET_PONE); + assertEquals(a.equals(b), false); + } + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_false_second_differs(int y, int o, int d, int h, int m, int s, int n, ZoneOffset ignored) { + s = (s == 59 ? 58 : s); + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(y, o, d), LocalTime.of(h, m, s, n), OFFSET_PONE); + OffsetDateTime b = OffsetDateTime.of(LocalDate.of(y, o, d), LocalTime.of(h, m, s + 1, n), OFFSET_PONE); + assertEquals(a.equals(b), false); + } + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_false_nano_differs(int y, int o, int d, int h, int m, int s, int n, ZoneOffset ignored) { + n = (n == 999999999 ? 999999998 : n); + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(y, o, d), LocalTime.of(h, m, s, n), OFFSET_PONE); + OffsetDateTime b = OffsetDateTime.of(LocalDate.of(y, o, d), LocalTime.of(h, m, s, n + 1), OFFSET_PONE); + assertEquals(a.equals(b), false); + } + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_false_offset_differs(int y, int o, int d, int h, int m, int s, int n, ZoneOffset ignored) { + OffsetDateTime a = OffsetDateTime.of(LocalDate.of(y, o, d), LocalTime.of(h, m, s, n), OFFSET_PONE); + OffsetDateTime b = OffsetDateTime.of(LocalDate.of(y, o, d), LocalTime.of(h, m, s, n), OFFSET_PTWO); + assertEquals(a.equals(b), false); + } + + @Test(groups={"tck"}) + public void test_equals_itself_true() { + assertEquals(TEST_2008_6_30_11_30_59_000000500.equals(TEST_2008_6_30_11_30_59_000000500), true); + } + + @Test(groups={"tck"}) + public void test_equals_string_false() { + assertEquals(TEST_2008_6_30_11_30_59_000000500.equals("2007-07-15"), false); + } + + @Test(groups={"tck"}) + public void test_equals_null_false() { + assertEquals(TEST_2008_6_30_11_30_59_000000500.equals(null), false); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @DataProvider(name="sampleToString") + Object[][] provider_sampleToString() { + return new Object[][] { + {2008, 6, 30, 11, 30, 59, 0, "Z", "2008-06-30T11:30:59Z"}, + {2008, 6, 30, 11, 30, 59, 0, "+01:00", "2008-06-30T11:30:59+01:00"}, + {2008, 6, 30, 11, 30, 59, 999000000, "Z", "2008-06-30T11:30:59.999Z"}, + {2008, 6, 30, 11, 30, 59, 999000000, "+01:00", "2008-06-30T11:30:59.999+01:00"}, + {2008, 6, 30, 11, 30, 59, 999000, "Z", "2008-06-30T11:30:59.000999Z"}, + {2008, 6, 30, 11, 30, 59, 999000, "+01:00", "2008-06-30T11:30:59.000999+01:00"}, + {2008, 6, 30, 11, 30, 59, 999, "Z", "2008-06-30T11:30:59.000000999Z"}, + {2008, 6, 30, 11, 30, 59, 999, "+01:00", "2008-06-30T11:30:59.000000999+01:00"}, + }; + } + + @Test(dataProvider="sampleToString", groups={"tck"}) + public void test_toString(int y, int o, int d, int h, int m, int s, int n, String offsetId, String expected) { + OffsetDateTime t = OffsetDateTime.of(LocalDate.of(y, o, d), LocalTime.of(h, m, s, n), ZoneOffset.of(offsetId)); + String str = t.toString(); + assertEquals(str, expected); + } + + //----------------------------------------------------------------------- + // toString(DateTimeFormatter) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toString_formatter() { + DateTimeFormatter f = DateTimeFormatters.pattern("y M d H m s"); + String t = OffsetDateTime.of(LocalDate.of(2010, 12, 3), LocalTime.of(11, 30), OFFSET_PONE).toString(f); + assertEquals(t, "2010 12 3 11 30 0"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_toString_formatter_null() { + OffsetDateTime.of(LocalDate.of(2010, 12, 3), LocalTime.of(11, 30), OFFSET_PONE).toString(null); + } + +} diff --git a/jdk/test/java/time/tck/java/time/temporal/TCKOffsetTime.java b/jdk/test/java/time/tck/java/time/temporal/TCKOffsetTime.java new file mode 100644 index 00000000000..374268589ad --- /dev/null +++ b/jdk/test/java/time/tck/java/time/temporal/TCKOffsetTime.java @@ -0,0 +1,1325 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.temporal; + +import static java.time.temporal.ChronoField.AMPM_OF_DAY; +import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_AMPM; +import static java.time.temporal.ChronoField.CLOCK_HOUR_OF_DAY; +import static java.time.temporal.ChronoField.HOUR_OF_AMPM; +import static java.time.temporal.ChronoField.HOUR_OF_DAY; +import static java.time.temporal.ChronoField.MICRO_OF_DAY; +import static java.time.temporal.ChronoField.MICRO_OF_SECOND; +import static java.time.temporal.ChronoField.MILLI_OF_DAY; +import static java.time.temporal.ChronoField.MILLI_OF_SECOND; +import static java.time.temporal.ChronoField.MINUTE_OF_DAY; +import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; +import static java.time.temporal.ChronoField.NANO_OF_DAY; +import static java.time.temporal.ChronoField.NANO_OF_SECOND; +import static java.time.temporal.ChronoField.OFFSET_SECONDS; +import static java.time.temporal.ChronoField.SECOND_OF_DAY; +import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; +import static java.time.temporal.ChronoUnit.DAYS; +import static java.time.temporal.ChronoUnit.NANOS; +import static java.time.temporal.ChronoUnit.SECONDS; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import java.time.Clock; +import java.time.DateTimeException; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Period; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatters; +import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoUnit; +import java.time.temporal.JulianFields; +import java.time.temporal.OffsetDate; +import java.time.temporal.OffsetDateTime; +import java.time.temporal.OffsetTime; +import java.time.temporal.Queries; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalAdder; +import java.time.temporal.TemporalAdjuster; +import java.time.temporal.TemporalField; +import java.time.temporal.TemporalQuery; +import java.time.temporal.TemporalSubtractor; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import tck.java.time.AbstractDateTimeTest; +import test.java.time.MockSimplePeriod; + +/** + * Test OffsetTime. + */ +@Test +public class TCKOffsetTime extends AbstractDateTimeTest { + + private static final ZoneOffset OFFSET_PONE = ZoneOffset.ofHours(1); + private static final ZoneOffset OFFSET_PTWO = ZoneOffset.ofHours(2); + private static final LocalDate DATE = LocalDate.of(2008, 12, 3); + private OffsetTime TEST_11_30_59_500_PONE; + + @BeforeMethod(groups={"tck","implementation"}) + public void setUp() { + TEST_11_30_59_500_PONE = OffsetTime.of(LocalTime.of(11, 30, 59, 500), OFFSET_PONE); + } + + //----------------------------------------------------------------------- + @Override + protected List samples() { + TemporalAccessor[] array = {TEST_11_30_59_500_PONE, OffsetTime.MIN, OffsetTime.MAX}; + return Arrays.asList(array); + } + + @Override + protected List validFields() { + TemporalField[] array = { + NANO_OF_SECOND, + NANO_OF_DAY, + MICRO_OF_SECOND, + MICRO_OF_DAY, + MILLI_OF_SECOND, + MILLI_OF_DAY, + SECOND_OF_MINUTE, + SECOND_OF_DAY, + MINUTE_OF_HOUR, + MINUTE_OF_DAY, + CLOCK_HOUR_OF_AMPM, + HOUR_OF_AMPM, + CLOCK_HOUR_OF_DAY, + HOUR_OF_DAY, + AMPM_OF_DAY, + OFFSET_SECONDS, + }; + return Arrays.asList(array); + } + + @Override + protected List invalidFields() { + List list = new ArrayList<>(Arrays.asList(ChronoField.values())); + list.removeAll(validFields()); + list.add(JulianFields.JULIAN_DAY); + list.add(JulianFields.MODIFIED_JULIAN_DAY); + list.add(JulianFields.RATA_DIE); + return list; + } + + //----------------------------------------------------------------------- + @Test + public void test_serialization() throws Exception { + assertSerializable(TEST_11_30_59_500_PONE); + assertSerializable(OffsetTime.MIN); + assertSerializable(OffsetTime.MAX); + } + + @Test + public void test_serialization_format() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baos) ) { + dos.writeByte(2); + } + byte[] bytes = baos.toByteArray(); + ByteArrayOutputStream baosTime = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baosTime) ) { + dos.writeByte(4); + dos.writeByte(22); + dos.writeByte(17); + dos.writeByte(59); + dos.writeInt(464_000_000); + } + byte[] bytesTime = baosTime.toByteArray(); + ByteArrayOutputStream baosOffset = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baosOffset) ) { + dos.writeByte(8); + dos.writeByte(4); // quarter hours stored: 3600 / 900 + } + byte[] bytesOffset = baosOffset.toByteArray(); + assertSerializedBySer(OffsetTime.of(LocalTime.of(22, 17, 59, 464_000_000), ZoneOffset.ofHours(1)), bytes, + bytesTime, bytesOffset); + } + + //----------------------------------------------------------------------- + // constants + //----------------------------------------------------------------------- + @Test + public void constant_MIN() { + check(OffsetTime.MIN, 0, 0, 0, 0, ZoneOffset.MAX); + } + + @Test + public void constant_MAX() { + check(OffsetTime.MAX, 23, 59, 59, 999999999, ZoneOffset.MIN); + } + + //----------------------------------------------------------------------- + // now() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void now() { + ZonedDateTime nowDT = ZonedDateTime.now(); + + OffsetTime expected = OffsetTime.now(Clock.systemDefaultZone()); + OffsetTime test = OffsetTime.now(); + long diff = Math.abs(test.getTime().toNanoOfDay() - expected.getTime().toNanoOfDay()); + assertTrue(diff < 100000000); // less than 0.1 secs + assertEquals(test.getOffset(), nowDT.getOffset()); + } + + //----------------------------------------------------------------------- + // now(Clock) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void now_Clock_allSecsInDay() { + for (int i = 0; i < (2 * 24 * 60 * 60); i++) { + Instant instant = Instant.ofEpochSecond(i, 8); + Clock clock = Clock.fixed(instant, ZoneOffset.UTC); + OffsetTime test = OffsetTime.now(clock); + assertEquals(test.getHour(), (i / (60 * 60)) % 24); + assertEquals(test.getMinute(), (i / 60) % 60); + assertEquals(test.getSecond(), i % 60); + assertEquals(test.getNano(), 8); + assertEquals(test.getOffset(), ZoneOffset.UTC); + } + } + + @Test(groups={"tck"}) + public void now_Clock_beforeEpoch() { + for (int i =-1; i >= -(24 * 60 * 60); i--) { + Instant instant = Instant.ofEpochSecond(i, 8); + Clock clock = Clock.fixed(instant, ZoneOffset.UTC); + OffsetTime test = OffsetTime.now(clock); + assertEquals(test.getHour(), ((i + 24 * 60 * 60) / (60 * 60)) % 24); + assertEquals(test.getMinute(), ((i + 24 * 60 * 60) / 60) % 60); + assertEquals(test.getSecond(), (i + 24 * 60 * 60) % 60); + assertEquals(test.getNano(), 8); + assertEquals(test.getOffset(), ZoneOffset.UTC); + } + } + + @Test(groups={"tck"}) + public void now_Clock_offsets() { + Instant base = LocalDateTime.of(1970, 1, 1, 12, 0).toInstant(ZoneOffset.UTC); + for (int i = -9; i < 15; i++) { + ZoneOffset offset = ZoneOffset.ofHours(i); + Clock clock = Clock.fixed(base, offset); + OffsetTime test = OffsetTime.now(clock); + assertEquals(test.getHour(), (12 + i) % 24); + assertEquals(test.getMinute(), 0); + assertEquals(test.getSecond(), 0); + assertEquals(test.getNano(), 0); + assertEquals(test.getOffset(), offset); + } + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void now_Clock_nullZoneId() { + OffsetTime.now((ZoneId) null); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void now_Clock_nullClock() { + OffsetTime.now((Clock) null); + } + + //----------------------------------------------------------------------- + // factories + //----------------------------------------------------------------------- + private void check(OffsetTime test, int h, int m, int s, int n, ZoneOffset offset) { + assertEquals(test.getTime(), LocalTime.of(h, m, s, n)); + assertEquals(test.getOffset(), offset); + + assertEquals(test.getHour(), h); + assertEquals(test.getMinute(), m); + assertEquals(test.getSecond(), s); + assertEquals(test.getNano(), n); + + assertEquals(test, test); + assertEquals(test.hashCode(), test.hashCode()); + assertEquals(OffsetTime.of(LocalTime.of(h, m, s, n), offset), test); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_intsHM() { + OffsetTime test = OffsetTime.of(LocalTime.of(11, 30), OFFSET_PONE); + check(test, 11, 30, 0, 0, OFFSET_PONE); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_intsHMS() { + OffsetTime test = OffsetTime.of(LocalTime.of(11, 30, 10), OFFSET_PONE); + check(test, 11, 30, 10, 0, OFFSET_PONE); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_intsHMSN() { + OffsetTime test = OffsetTime.of(LocalTime.of(11, 30, 10, 500), OFFSET_PONE); + check(test, 11, 30, 10, 500, OFFSET_PONE); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_LocalTimeZoneOffset() { + LocalTime localTime = LocalTime.of(11, 30, 10, 500); + OffsetTime test = OffsetTime.of(localTime, OFFSET_PONE); + check(test, 11, 30, 10, 500, OFFSET_PONE); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_LocalTimeZoneOffset_nullTime() { + OffsetTime.of((LocalTime) null, OFFSET_PONE); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_LocalTimeZoneOffset_nullOffset() { + LocalTime localTime = LocalTime.of(11, 30, 10, 500); + OffsetTime.of(localTime, (ZoneOffset) null); + } + + //----------------------------------------------------------------------- + // ofInstant() + //----------------------------------------------------------------------- + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_ofInstant_nullInstant() { + OffsetTime.ofInstant((Instant) null, ZoneOffset.UTC); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_ofInstant_nullOffset() { + Instant instant = Instant.ofEpochSecond(0L); + OffsetTime.ofInstant(instant, (ZoneOffset) null); + } + + @Test(groups={"tck"}) + public void factory_ofInstant_allSecsInDay() { + for (int i = 0; i < (2 * 24 * 60 * 60); i++) { + Instant instant = Instant.ofEpochSecond(i, 8); + OffsetTime test = OffsetTime.ofInstant(instant, ZoneOffset.UTC); + assertEquals(test.getHour(), (i / (60 * 60)) % 24); + assertEquals(test.getMinute(), (i / 60) % 60); + assertEquals(test.getSecond(), i % 60); + assertEquals(test.getNano(), 8); + } + } + + @Test(groups={"tck"}) + public void factory_ofInstant_beforeEpoch() { + for (int i =-1; i >= -(24 * 60 * 60); i--) { + Instant instant = Instant.ofEpochSecond(i, 8); + OffsetTime test = OffsetTime.ofInstant(instant, ZoneOffset.UTC); + assertEquals(test.getHour(), ((i + 24 * 60 * 60) / (60 * 60)) % 24); + assertEquals(test.getMinute(), ((i + 24 * 60 * 60) / 60) % 60); + assertEquals(test.getSecond(), (i + 24 * 60 * 60) % 60); + assertEquals(test.getNano(), 8); + } + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_ofInstant_maxYear() { + OffsetTime test = OffsetTime.ofInstant(Instant.MAX, ZoneOffset.UTC); + assertEquals(test.getHour(), 23); + assertEquals(test.getMinute(), 59); + assertEquals(test.getSecond(), 59); + assertEquals(test.getNano(), 999_999_999); + } + + @Test(groups={"tck"}) + public void factory_ofInstant_minYear() { + OffsetTime test = OffsetTime.ofInstant(Instant.MIN, ZoneOffset.UTC); + assertEquals(test.getHour(), 0); + assertEquals(test.getMinute(), 0); + assertEquals(test.getSecond(), 0); + assertEquals(test.getNano(), 0); + } + + //----------------------------------------------------------------------- + // from(TemporalAccessor) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_from_TemporalAccessor_OT() { + assertEquals(OffsetTime.from(OffsetTime.of(LocalTime.of(17, 30), OFFSET_PONE)), OffsetTime.of(LocalTime.of(17, 30), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_from_TemporalAccessor_ZDT() { + ZonedDateTime base = LocalDateTime.of(2007, 7, 15, 11, 30, 59, 500).atZone(OFFSET_PONE); + assertEquals(OffsetTime.from(base), TEST_11_30_59_500_PONE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void factory_from_TemporalAccessor_invalid_noDerive() { + OffsetTime.from(LocalDate.of(2007, 7, 15)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_from_TemporalAccessor_null() { + OffsetTime.from((TemporalAccessor) null); + } + + //----------------------------------------------------------------------- + // parse() + //----------------------------------------------------------------------- + @Test(dataProvider = "sampleToString", groups={"tck"}) + public void factory_parse_validText(int h, int m, int s, int n, String offsetId, String parsable) { + OffsetTime t = OffsetTime.parse(parsable); + assertNotNull(t, parsable); + check(t, h, m, s, n, ZoneOffset.of(offsetId)); + } + + @DataProvider(name="sampleBadParse") + Object[][] provider_sampleBadParse() { + return new Object[][]{ + {"00;00"}, + {"12-00"}, + {"-01:00"}, + {"00:00:00-09"}, + {"00:00:00,09"}, + {"00:00:abs"}, + {"11"}, + {"11:30"}, + {"11:30+01:00[Europe/Paris]"}, + }; + } + + @Test(dataProvider = "sampleBadParse", expectedExceptions={DateTimeParseException.class}, groups={"tck"}) + public void factory_parse_invalidText(String unparsable) { + OffsetTime.parse(unparsable); + } + + //-----------------------------------------------------------------------s + @Test(expectedExceptions={DateTimeParseException.class}, groups={"tck"}) + public void factory_parse_illegalHour() { + OffsetTime.parse("25:00+01:00"); + } + + @Test(expectedExceptions={DateTimeParseException.class}, groups={"tck"}) + public void factory_parse_illegalMinute() { + OffsetTime.parse("12:60+01:00"); + } + + @Test(expectedExceptions={DateTimeParseException.class}, groups={"tck"}) + public void factory_parse_illegalSecond() { + OffsetTime.parse("12:12:60+01:00"); + } + + //----------------------------------------------------------------------- + // parse(DateTimeFormatter) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_parse_formatter() { + DateTimeFormatter f = DateTimeFormatters.pattern("H m s XXX"); + OffsetTime test = OffsetTime.parse("11 30 0 +01:00", f); + assertEquals(test, OffsetTime.of(LocalTime.of(11, 30), ZoneOffset.ofHours(1))); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_formatter_nullText() { + DateTimeFormatter f = DateTimeFormatters.pattern("y M d H m s"); + OffsetTime.parse((String) null, f); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_formatter_nullFormatter() { + OffsetTime.parse("ANY", null); + } + + //----------------------------------------------------------------------- + // constructor + //----------------------------------------------------------------------- + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void constructor_nullTime() throws Throwable { + Constructor con = OffsetTime.class.getDeclaredConstructor(LocalTime.class, ZoneOffset.class); + con.setAccessible(true); + try { + con.newInstance(null, OFFSET_PONE); + } catch (InvocationTargetException ex) { + throw ex.getCause(); + } + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void constructor_nullOffset() throws Throwable { + Constructor con = OffsetTime.class.getDeclaredConstructor(LocalTime.class, ZoneOffset.class); + con.setAccessible(true); + try { + con.newInstance(LocalTime.of(11, 30), null); + } catch (InvocationTargetException ex) { + throw ex.getCause(); + } + } + + //----------------------------------------------------------------------- + // basics + //----------------------------------------------------------------------- + @DataProvider(name="sampleTimes") + Object[][] provider_sampleTimes() { + return new Object[][] { + {11, 30, 20, 500, OFFSET_PONE}, + {11, 0, 0, 0, OFFSET_PONE}, + {23, 59, 59, 999999999, OFFSET_PONE}, + }; + } + + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_get(int h, int m, int s, int n, ZoneOffset offset) { + LocalTime localTime = LocalTime.of(h, m, s, n); + OffsetTime a = OffsetTime.of(localTime, offset); + + assertEquals(a.getTime(), localTime); + assertEquals(a.getOffset(), offset); + assertEquals(a.toString(), localTime.toString() + offset.toString()); + assertEquals(a.getHour(), localTime.getHour()); + assertEquals(a.getMinute(), localTime.getMinute()); + assertEquals(a.getSecond(), localTime.getSecond()); + assertEquals(a.getNano(), localTime.getNano()); + } + + //----------------------------------------------------------------------- + // get(TemporalField) + //----------------------------------------------------------------------- + @Test + public void test_get_TemporalField() { + OffsetTime test = OffsetTime.of(LocalTime.of(12, 30, 40, 987654321), OFFSET_PONE); + assertEquals(test.get(ChronoField.HOUR_OF_DAY), 12); + assertEquals(test.get(ChronoField.MINUTE_OF_HOUR), 30); + assertEquals(test.get(ChronoField.SECOND_OF_MINUTE), 40); + assertEquals(test.get(ChronoField.NANO_OF_SECOND), 987654321); + assertEquals(test.get(ChronoField.HOUR_OF_AMPM), 0); + assertEquals(test.get(ChronoField.AMPM_OF_DAY), 1); + + assertEquals(test.get(ChronoField.OFFSET_SECONDS), 3600); + } + + @Test + public void test_getLong_TemporalField() { + OffsetTime test = OffsetTime.of(LocalTime.of(12, 30, 40, 987654321), OFFSET_PONE); + assertEquals(test.getLong(ChronoField.HOUR_OF_DAY), 12); + assertEquals(test.getLong(ChronoField.MINUTE_OF_HOUR), 30); + assertEquals(test.getLong(ChronoField.SECOND_OF_MINUTE), 40); + assertEquals(test.getLong(ChronoField.NANO_OF_SECOND), 987654321); + assertEquals(test.getLong(ChronoField.HOUR_OF_AMPM), 0); + assertEquals(test.getLong(ChronoField.AMPM_OF_DAY), 1); + + assertEquals(test.getLong(ChronoField.OFFSET_SECONDS), 3600); + } + + //----------------------------------------------------------------------- + // query(TemporalQuery) + //----------------------------------------------------------------------- + @Test + public void test_query_chrono() { + assertEquals(TEST_11_30_59_500_PONE.query(Queries.chrono()), null); + assertEquals(Queries.chrono().queryFrom(TEST_11_30_59_500_PONE), null); + } + + @Test + public void test_query_zoneId() { + assertEquals(TEST_11_30_59_500_PONE.query(Queries.zoneId()), null); + assertEquals(Queries.zoneId().queryFrom(TEST_11_30_59_500_PONE), null); + } + + @Test + public void test_query_precision() { + assertEquals(TEST_11_30_59_500_PONE.query(Queries.precision()), NANOS); + assertEquals(Queries.precision().queryFrom(TEST_11_30_59_500_PONE), NANOS); + } + + @Test + public void test_query_offset() { + assertEquals(TEST_11_30_59_500_PONE.query(Queries.offset()), OFFSET_PONE); + assertEquals(Queries.offset().queryFrom(TEST_11_30_59_500_PONE), OFFSET_PONE); + } + + @Test + public void test_query_zone() { + assertEquals(TEST_11_30_59_500_PONE.query(Queries.zone()), OFFSET_PONE); + assertEquals(Queries.zone().queryFrom(TEST_11_30_59_500_PONE), OFFSET_PONE); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_query_null() { + TEST_11_30_59_500_PONE.query(null); + } + + //----------------------------------------------------------------------- + // withOffsetSameLocal() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withOffsetSameLocal() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetTime test = base.withOffsetSameLocal(OFFSET_PTWO); + assertEquals(test.getTime(), base.getTime()); + assertEquals(test.getOffset(), OFFSET_PTWO); + } + + @Test(groups={"tck"}) + public void test_withOffsetSameLocal_noChange() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetTime test = base.withOffsetSameLocal(OFFSET_PONE); + assertEquals(test, base); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_withOffsetSameLocal_null() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + base.withOffsetSameLocal(null); + } + + //----------------------------------------------------------------------- + // withOffsetSameInstant() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withOffsetSameInstant() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetTime test = base.withOffsetSameInstant(OFFSET_PTWO); + OffsetTime expected = OffsetTime.of(LocalTime.of(12, 30, 59), OFFSET_PTWO); + assertEquals(test, expected); + } + + @Test(groups={"tck"}) + public void test_withOffsetSameInstant_noChange() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetTime test = base.withOffsetSameInstant(OFFSET_PONE); + assertEquals(test, base); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_withOffsetSameInstant_null() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + base.withOffsetSameInstant(null); + } + + //----------------------------------------------------------------------- + // with(WithAdjuster) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_with_adjustment() { + final OffsetTime sample = OffsetTime.of(LocalTime.of(23, 5), OFFSET_PONE); + TemporalAdjuster adjuster = new TemporalAdjuster() { + @Override + public Temporal adjustInto(Temporal dateTime) { + return sample; + } + }; + assertEquals(TEST_11_30_59_500_PONE.with(adjuster), sample); + } + + @Test(groups={"tck"}) + public void test_with_adjustment_LocalTime() { + OffsetTime test = TEST_11_30_59_500_PONE.with(LocalTime.of(13, 30)); + assertEquals(test, OffsetTime.of(LocalTime.of(13, 30), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_with_adjustment_OffsetTime() { + OffsetTime test = TEST_11_30_59_500_PONE.with(OffsetTime.of(LocalTime.of(13, 35), OFFSET_PTWO)); + assertEquals(test, OffsetTime.of(LocalTime.of(13, 35), OFFSET_PTWO)); + } + + @Test(groups={"tck"}) + public void test_with_adjustment_ZoneOffset() { + OffsetTime test = TEST_11_30_59_500_PONE.with(OFFSET_PTWO); + assertEquals(test, OffsetTime.of(LocalTime.of(11, 30, 59, 500), OFFSET_PTWO)); + } + + @Test(groups={"tck"}) + public void test_with_adjustment_AmPm() { + OffsetTime test = TEST_11_30_59_500_PONE.with(new TemporalAdjuster() { + @Override + public Temporal adjustInto(Temporal dateTime) { + return dateTime.with(HOUR_OF_DAY, 23); + } + }); + assertEquals(test, OffsetTime.of(LocalTime.of(23, 30, 59, 500), OFFSET_PONE)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_with_adjustment_null() { + TEST_11_30_59_500_PONE.with((TemporalAdjuster) null); + } + + //----------------------------------------------------------------------- + // with(TemporalField, long) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_with_TemporalField() { + OffsetTime test = OffsetTime.of(LocalTime.of(12, 30, 40, 987654321), OFFSET_PONE); + assertEquals(test.with(ChronoField.HOUR_OF_DAY, 15), OffsetTime.of(LocalTime.of(15, 30, 40, 987654321), OFFSET_PONE)); + assertEquals(test.with(ChronoField.MINUTE_OF_HOUR, 50), OffsetTime.of(LocalTime.of(12, 50, 40, 987654321), OFFSET_PONE)); + assertEquals(test.with(ChronoField.SECOND_OF_MINUTE, 50), OffsetTime.of(LocalTime.of(12, 30, 50, 987654321), OFFSET_PONE)); + assertEquals(test.with(ChronoField.NANO_OF_SECOND, 12345), OffsetTime.of(LocalTime.of(12, 30, 40, 12345), OFFSET_PONE)); + assertEquals(test.with(ChronoField.HOUR_OF_AMPM, 6), OffsetTime.of(LocalTime.of(18, 30, 40, 987654321), OFFSET_PONE)); + assertEquals(test.with(ChronoField.AMPM_OF_DAY, 0), OffsetTime.of(LocalTime.of(0, 30, 40, 987654321), OFFSET_PONE)); + + assertEquals(test.with(ChronoField.OFFSET_SECONDS, 7205), OffsetTime.of(LocalTime.of(12, 30, 40, 987654321), ZoneOffset.ofHoursMinutesSeconds(2, 0, 5))); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"} ) + public void test_with_TemporalField_null() { + TEST_11_30_59_500_PONE.with((TemporalField) null, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"} ) + public void test_with_TemporalField_invalidField() { + TEST_11_30_59_500_PONE.with(ChronoField.YEAR, 0); + } + + //----------------------------------------------------------------------- + // withHour() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withHour_normal() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetTime test = base.withHour(15); + assertEquals(test, OffsetTime.of(LocalTime.of(15, 30, 59), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_withHour_noChange() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetTime test = base.withHour(11); + assertEquals(test, base); + } + + //----------------------------------------------------------------------- + // withMinute() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withMinute_normal() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetTime test = base.withMinute(15); + assertEquals(test, OffsetTime.of(LocalTime.of(11, 15, 59), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_withMinute_noChange() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetTime test = base.withMinute(30); + assertEquals(test, base); + } + + //----------------------------------------------------------------------- + // withSecond() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withSecond_normal() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetTime test = base.withSecond(15); + assertEquals(test, OffsetTime.of(LocalTime.of(11, 30, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_withSecond_noChange() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetTime test = base.withSecond(59); + assertEquals(test, base); + } + + //----------------------------------------------------------------------- + // withNano() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withNanoOfSecond_normal() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59, 1), OFFSET_PONE); + OffsetTime test = base.withNano(15); + assertEquals(test, OffsetTime.of(LocalTime.of(11, 30, 59, 15), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_withNanoOfSecond_noChange() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59, 1), OFFSET_PONE); + OffsetTime test = base.withNano(1); + assertEquals(test, base); + } + + //----------------------------------------------------------------------- + // truncatedTo(TemporalUnit) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_truncatedTo_normal() { + assertEquals(TEST_11_30_59_500_PONE.truncatedTo(NANOS), TEST_11_30_59_500_PONE); + assertEquals(TEST_11_30_59_500_PONE.truncatedTo(SECONDS), TEST_11_30_59_500_PONE.withNano(0)); + assertEquals(TEST_11_30_59_500_PONE.truncatedTo(DAYS), TEST_11_30_59_500_PONE.with(LocalTime.MIDNIGHT)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_truncatedTo_null() { + TEST_11_30_59_500_PONE.truncatedTo(null); + } + + //----------------------------------------------------------------------- + // plus(PlusAdjuster) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plus_PlusAdjuster() { + MockSimplePeriod period = MockSimplePeriod.of(7, ChronoUnit.MINUTES); + OffsetTime t = TEST_11_30_59_500_PONE.plus(period); + assertEquals(t, OffsetTime.of(LocalTime.of(11, 37, 59, 500), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plus_PlusAdjuster_noChange() { + OffsetTime t = TEST_11_30_59_500_PONE.plus(MockSimplePeriod.of(0, SECONDS)); + assertEquals(t, TEST_11_30_59_500_PONE); + } + + @Test(groups={"tck"}) + public void test_plus_PlusAdjuster_zero() { + OffsetTime t = TEST_11_30_59_500_PONE.plus(Period.ZERO); + assertEquals(t, TEST_11_30_59_500_PONE); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_plus_PlusAdjuster_null() { + TEST_11_30_59_500_PONE.plus((TemporalAdder) null); + } + + //----------------------------------------------------------------------- + // plusHours() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusHours() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetTime test = base.plusHours(13); + assertEquals(test, OffsetTime.of(LocalTime.of(0, 30, 59), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plusHours_zero() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetTime test = base.plusHours(0); + assertEquals(test, base); + } + + //----------------------------------------------------------------------- + // plusMinutes() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusMinutes() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetTime test = base.plusMinutes(30); + assertEquals(test, OffsetTime.of(LocalTime.of(12, 0, 59), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plusMinutes_zero() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetTime test = base.plusMinutes(0); + assertEquals(test, base); + } + + //----------------------------------------------------------------------- + // plusSeconds() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusSeconds() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetTime test = base.plusSeconds(1); + assertEquals(test, OffsetTime.of(LocalTime.of(11, 31, 0), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plusSeconds_zero() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetTime test = base.plusSeconds(0); + assertEquals(test, base); + } + + //----------------------------------------------------------------------- + // plusNanos() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusNanos() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59, 0), OFFSET_PONE); + OffsetTime test = base.plusNanos(1); + assertEquals(test, OffsetTime.of(LocalTime.of(11, 30, 59, 1), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_plusNanos_zero() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetTime test = base.plusNanos(0); + assertEquals(test, base); + } + + //----------------------------------------------------------------------- + // minus(MinusAdjuster) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minus_MinusAdjuster() { + MockSimplePeriod period = MockSimplePeriod.of(7, ChronoUnit.MINUTES); + OffsetTime t = TEST_11_30_59_500_PONE.minus(period); + assertEquals(t, OffsetTime.of(LocalTime.of(11, 23, 59, 500), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minus_MinusAdjuster_noChange() { + OffsetTime t = TEST_11_30_59_500_PONE.minus(MockSimplePeriod.of(0, SECONDS)); + assertEquals(t, TEST_11_30_59_500_PONE); + } + + @Test(groups={"tck"}) + public void test_minus_MinusAdjuster_zero() { + OffsetTime t = TEST_11_30_59_500_PONE.minus(Period.ZERO); + assertEquals(t, TEST_11_30_59_500_PONE); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_minus_MinusAdjuster_null() { + TEST_11_30_59_500_PONE.minus((TemporalSubtractor) null); + } + + //----------------------------------------------------------------------- + // minusHours() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusHours() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetTime test = base.minusHours(-13); + assertEquals(test, OffsetTime.of(LocalTime.of(0, 30, 59), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minusHours_zero() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetTime test = base.minusHours(0); + assertEquals(test, base); + } + + //----------------------------------------------------------------------- + // minusMinutes() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusMinutes() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetTime test = base.minusMinutes(50); + assertEquals(test, OffsetTime.of(LocalTime.of(10, 40, 59), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minusMinutes_zero() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetTime test = base.minusMinutes(0); + assertEquals(test, base); + } + + //----------------------------------------------------------------------- + // minusSeconds() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusSeconds() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetTime test = base.minusSeconds(60); + assertEquals(test, OffsetTime.of(LocalTime.of(11, 29, 59), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minusSeconds_zero() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetTime test = base.minusSeconds(0); + assertEquals(test, base); + } + + //----------------------------------------------------------------------- + // minusNanos() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusNanos() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59, 0), OFFSET_PONE); + OffsetTime test = base.minusNanos(1); + assertEquals(test, OffsetTime.of(LocalTime.of(11, 30, 58, 999999999), OFFSET_PONE)); + } + + @Test(groups={"tck"}) + public void test_minusNanos_zero() { + OffsetTime base = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetTime test = base.minusNanos(0); + assertEquals(test, base); + } + + //----------------------------------------------------------------------- + // compareTo() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_compareTo_time() { + OffsetTime a = OffsetTime.of(LocalTime.of(11, 29), OFFSET_PONE); + OffsetTime b = OffsetTime.of(LocalTime.of(11, 30), OFFSET_PONE); // a is before b due to time + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + assertEquals(convertInstant(a).compareTo(convertInstant(b)) < 0, true); + } + + @Test(groups={"tck"}) + public void test_compareTo_offset() { + OffsetTime a = OffsetTime.of(LocalTime.of(11, 30), OFFSET_PTWO); + OffsetTime b = OffsetTime.of(LocalTime.of(11, 30), OFFSET_PONE); // a is before b due to offset + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + assertEquals(convertInstant(a).compareTo(convertInstant(b)) < 0, true); + } + + @Test(groups={"tck"}) + public void test_compareTo_both() { + OffsetTime a = OffsetTime.of(LocalTime.of(11, 50), OFFSET_PTWO); + OffsetTime b = OffsetTime.of(LocalTime.of(11, 20), OFFSET_PONE); // a is before b on instant scale + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + assertEquals(convertInstant(a).compareTo(convertInstant(b)) < 0, true); + } + + @Test(groups={"tck"}) + public void test_compareTo_bothNearStartOfDay() { + OffsetTime a = OffsetTime.of(LocalTime.of(0, 10), OFFSET_PONE); + OffsetTime b = OffsetTime.of(LocalTime.of(2, 30), OFFSET_PTWO); // a is before b on instant scale + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + assertEquals(convertInstant(a).compareTo(convertInstant(b)) < 0, true); + } + + @Test(groups={"tck"}) + public void test_compareTo_hourDifference() { + OffsetTime a = OffsetTime.of(LocalTime.of(10, 0), OFFSET_PONE); + OffsetTime b = OffsetTime.of(LocalTime.of(11, 0), OFFSET_PTWO); // a is before b despite being same time-line time + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + assertEquals(convertInstant(a).compareTo(convertInstant(b)) == 0, true); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_compareTo_null() { + OffsetTime a = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + a.compareTo(null); + } + + @Test(expectedExceptions=ClassCastException.class, groups={"tck"}) + @SuppressWarnings({"unchecked", "rawtypes"}) + public void compareToNonOffsetTime() { + Comparable c = TEST_11_30_59_500_PONE; + c.compareTo(new Object()); + } + + private Instant convertInstant(OffsetTime ot) { + return DATE.atTime(ot.getTime()).toInstant(ot.getOffset()); + } + + //----------------------------------------------------------------------- + // isAfter() / isBefore() / isEqual() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_isBeforeIsAfterIsEqual1() { + OffsetTime a = OffsetTime.of(LocalTime.of(11, 30, 58), OFFSET_PONE); + OffsetTime b = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); // a is before b due to time + assertEquals(a.isBefore(b), true); + assertEquals(a.isEqual(b), false); + assertEquals(a.isAfter(b), false); + + assertEquals(b.isBefore(a), false); + assertEquals(b.isEqual(a), false); + assertEquals(b.isAfter(a), true); + + assertEquals(a.isBefore(a), false); + assertEquals(b.isBefore(b), false); + + assertEquals(a.isEqual(a), true); + assertEquals(b.isEqual(b), true); + + assertEquals(a.isAfter(a), false); + assertEquals(b.isAfter(b), false); + } + + @Test(groups={"tck"}) + public void test_isBeforeIsAfterIsEqual1nanos() { + OffsetTime a = OffsetTime.of(LocalTime.of(11, 30, 59, 3), OFFSET_PONE); + OffsetTime b = OffsetTime.of(LocalTime.of(11, 30, 59, 4), OFFSET_PONE); // a is before b due to time + assertEquals(a.isBefore(b), true); + assertEquals(a.isEqual(b), false); + assertEquals(a.isAfter(b), false); + + assertEquals(b.isBefore(a), false); + assertEquals(b.isEqual(a), false); + assertEquals(b.isAfter(a), true); + + assertEquals(a.isBefore(a), false); + assertEquals(b.isBefore(b), false); + + assertEquals(a.isEqual(a), true); + assertEquals(b.isEqual(b), true); + + assertEquals(a.isAfter(a), false); + assertEquals(b.isAfter(b), false); + } + + @Test(groups={"tck"}) + public void test_isBeforeIsAfterIsEqual2() { + OffsetTime a = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PTWO); + OffsetTime b = OffsetTime.of(LocalTime.of(11, 30, 58), OFFSET_PONE); // a is before b due to offset + assertEquals(a.isBefore(b), true); + assertEquals(a.isEqual(b), false); + assertEquals(a.isAfter(b), false); + + assertEquals(b.isBefore(a), false); + assertEquals(b.isEqual(a), false); + assertEquals(b.isAfter(a), true); + + assertEquals(a.isBefore(a), false); + assertEquals(b.isBefore(b), false); + + assertEquals(a.isEqual(a), true); + assertEquals(b.isEqual(b), true); + + assertEquals(a.isAfter(a), false); + assertEquals(b.isAfter(b), false); + } + + @Test(groups={"tck"}) + public void test_isBeforeIsAfterIsEqual2nanos() { + OffsetTime a = OffsetTime.of(LocalTime.of(11, 30, 59, 4), ZoneOffset.ofTotalSeconds(OFFSET_PONE.getTotalSeconds() + 1)); + OffsetTime b = OffsetTime.of(LocalTime.of(11, 30, 59, 3), OFFSET_PONE); // a is before b due to offset + assertEquals(a.isBefore(b), true); + assertEquals(a.isEqual(b), false); + assertEquals(a.isAfter(b), false); + + assertEquals(b.isBefore(a), false); + assertEquals(b.isEqual(a), false); + assertEquals(b.isAfter(a), true); + + assertEquals(a.isBefore(a), false); + assertEquals(b.isBefore(b), false); + + assertEquals(a.isEqual(a), true); + assertEquals(b.isEqual(b), true); + + assertEquals(a.isAfter(a), false); + assertEquals(b.isAfter(b), false); + } + + @Test(groups={"tck"}) + public void test_isBeforeIsAfterIsEqual_instantComparison() { + OffsetTime a = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PTWO); + OffsetTime b = OffsetTime.of(LocalTime.of(10, 30, 59), OFFSET_PONE); // a is same instant as b + assertEquals(a.isBefore(b), false); + assertEquals(a.isEqual(b), true); + assertEquals(a.isAfter(b), false); + + assertEquals(b.isBefore(a), false); + assertEquals(b.isEqual(a), true); + assertEquals(b.isAfter(a), false); + + assertEquals(a.isBefore(a), false); + assertEquals(b.isBefore(b), false); + + assertEquals(a.isEqual(a), true); + assertEquals(b.isEqual(b), true); + + assertEquals(a.isAfter(a), false); + assertEquals(b.isAfter(b), false); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_isBefore_null() { + OffsetTime a = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + a.isBefore(null); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_isAfter_null() { + OffsetTime a = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + a.isAfter(null); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_isEqual_null() { + OffsetTime a = OffsetTime.of(LocalTime.of(11, 30, 59), OFFSET_PONE); + a.isEqual(null); + } + + //----------------------------------------------------------------------- + // equals() / hashCode() + //----------------------------------------------------------------------- + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_true(int h, int m, int s, int n, ZoneOffset ignored) { + OffsetTime a = OffsetTime.of(LocalTime.of(h, m, s, n), OFFSET_PONE); + OffsetTime b = OffsetTime.of(LocalTime.of(h, m, s, n), OFFSET_PONE); + assertEquals(a.equals(b), true); + assertEquals(a.hashCode() == b.hashCode(), true); + } + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_false_hour_differs(int h, int m, int s, int n, ZoneOffset ignored) { + h = (h == 23 ? 22 : h); + OffsetTime a = OffsetTime.of(LocalTime.of(h, m, s, n), OFFSET_PONE); + OffsetTime b = OffsetTime.of(LocalTime.of(h + 1, m, s, n), OFFSET_PONE); + assertEquals(a.equals(b), false); + } + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_false_minute_differs(int h, int m, int s, int n, ZoneOffset ignored) { + m = (m == 59 ? 58 : m); + OffsetTime a = OffsetTime.of(LocalTime.of(h, m, s, n), OFFSET_PONE); + OffsetTime b = OffsetTime.of(LocalTime.of(h, m + 1, s, n), OFFSET_PONE); + assertEquals(a.equals(b), false); + } + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_false_second_differs(int h, int m, int s, int n, ZoneOffset ignored) { + s = (s == 59 ? 58 : s); + OffsetTime a = OffsetTime.of(LocalTime.of(h, m, s, n), OFFSET_PONE); + OffsetTime b = OffsetTime.of(LocalTime.of(h, m, s + 1, n), OFFSET_PONE); + assertEquals(a.equals(b), false); + } + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_false_nano_differs(int h, int m, int s, int n, ZoneOffset ignored) { + n = (n == 999999999 ? 999999998 : n); + OffsetTime a = OffsetTime.of(LocalTime.of(h, m, s, n), OFFSET_PONE); + OffsetTime b = OffsetTime.of(LocalTime.of(h, m, s, n + 1), OFFSET_PONE); + assertEquals(a.equals(b), false); + } + @Test(dataProvider="sampleTimes", groups={"tck"}) + public void test_equals_false_offset_differs(int h, int m, int s, int n, ZoneOffset ignored) { + OffsetTime a = OffsetTime.of(LocalTime.of(h, m, s, n), OFFSET_PONE); + OffsetTime b = OffsetTime.of(LocalTime.of(h, m, s, n), OFFSET_PTWO); + assertEquals(a.equals(b), false); + } + + @Test(groups={"tck"}) + public void test_equals_itself_true() { + assertEquals(TEST_11_30_59_500_PONE.equals(TEST_11_30_59_500_PONE), true); + } + + @Test(groups={"tck"}) + public void test_equals_string_false() { + assertEquals(TEST_11_30_59_500_PONE.equals("2007-07-15"), false); + } + + @Test(groups={"tck"}) + public void test_equals_null_false() { + assertEquals(TEST_11_30_59_500_PONE.equals(null), false); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @DataProvider(name="sampleToString") + Object[][] provider_sampleToString() { + return new Object[][] { + {11, 30, 59, 0, "Z", "11:30:59Z"}, + {11, 30, 59, 0, "+01:00", "11:30:59+01:00"}, + {11, 30, 59, 999000000, "Z", "11:30:59.999Z"}, + {11, 30, 59, 999000000, "+01:00", "11:30:59.999+01:00"}, + {11, 30, 59, 999000, "Z", "11:30:59.000999Z"}, + {11, 30, 59, 999000, "+01:00", "11:30:59.000999+01:00"}, + {11, 30, 59, 999, "Z", "11:30:59.000000999Z"}, + {11, 30, 59, 999, "+01:00", "11:30:59.000000999+01:00"}, + }; + } + + @Test(dataProvider="sampleToString", groups={"tck"}) + public void test_toString(int h, int m, int s, int n, String offsetId, String expected) { + OffsetTime t = OffsetTime.of(LocalTime.of(h, m, s, n), ZoneOffset.of(offsetId)); + String str = t.toString(); + assertEquals(str, expected); + } + + //----------------------------------------------------------------------- + // toString(DateTimeFormatter) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toString_formatter() { + DateTimeFormatter f = DateTimeFormatters.pattern("H m s"); + String t = OffsetTime.of(LocalTime.of(11, 30), OFFSET_PONE).toString(f); + assertEquals(t, "11 30 0"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_toString_formatter_null() { + OffsetTime.of(LocalTime.of(11, 30), OFFSET_PONE).toString(null); + } + +} diff --git a/jdk/test/java/time/tck/java/time/temporal/TCKSimplePeriod.java b/jdk/test/java/time/tck/java/time/temporal/TCKSimplePeriod.java new file mode 100644 index 00000000000..d67e6aae297 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/temporal/TCKSimplePeriod.java @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.temporal; + +import static java.time.temporal.ChronoUnit.DAYS; +import static java.time.temporal.ChronoUnit.MONTHS; +import static java.time.temporal.ChronoUnit.NANOS; +import static java.time.temporal.ChronoUnit.SECONDS; +import static java.time.temporal.ChronoUnit.YEARS; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertSame; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import java.time.LocalDate; +import java.time.Period; +import java.time.temporal.ISOFields; +import java.time.temporal.SimplePeriod; +import java.time.temporal.TemporalUnit; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import tck.java.time.AbstractTCKTest; + +/** + * Test. + */ +@Test +public class TCKSimplePeriod extends AbstractTCKTest { + + private static final SimplePeriod TEST_12_MONTHS = SimplePeriod.of(12, MONTHS); + + //----------------------------------------------------------------------- + @Test(dataProvider="samples") + public void test_serialization(long amount, TemporalUnit unit) throws ClassNotFoundException, IOException { + SimplePeriod test = SimplePeriod.of(amount, unit); + assertSerializable(test); + } + + @Test + public void test_serialization_format_zoneOffset() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baos) ) { + dos.writeByte(10); + dos.writeLong(12); + } + byte[] bytes = baos.toByteArray(); + assertSerializedBySer(TEST_12_MONTHS, bytes, new byte[0]); + } + + //----------------------------------------------------------------------- + // of(long, TenmporalUnit) + //----------------------------------------------------------------------- + @DataProvider(name="samples") + Object[][] data_samples() { + return new Object[][] { + {0, YEARS}, + {1, YEARS}, + {-1, YEARS}, + {2, MONTHS}, + {-2, MONTHS}, + {43, ISOFields.WEEK_BASED_YEARS}, + {Long.MAX_VALUE, NANOS}, + {Long.MIN_VALUE, NANOS}, + }; + } + + @Test(dataProvider="samples") + public void factory_of(long amount, TemporalUnit unit) { + SimplePeriod test = SimplePeriod.of(amount, unit); + assertEquals(test.getAmount(), amount); + assertEquals(test.getUnit(), unit); + } + + //----------------------------------------------------------------------- + // addTo() + //----------------------------------------------------------------------- + @DataProvider(name="addTo") + Object[][] data_addTo() { + return new Object[][] { + {SimplePeriod.of(0, DAYS), date(2012, 6, 30), date(2012, 6, 30)}, + + {SimplePeriod.of(1, DAYS), date(2012, 6, 30), date(2012, 7, 1)}, + {SimplePeriod.of(-1, DAYS), date(2012, 6, 30), date(2012, 6, 29)}, + + {SimplePeriod.of(2, DAYS), date(2012, 6, 30), date(2012, 7, 2)}, + {SimplePeriod.of(-2, DAYS), date(2012, 6, 30), date(2012, 6, 28)}, + + {SimplePeriod.of(3, MONTHS), date(2012, 5, 31), date(2012, 8, 31)}, + {SimplePeriod.of(4, MONTHS), date(2012, 5, 31), date(2012, 9, 30)}, + {SimplePeriod.of(-3, MONTHS), date(2012, 5, 31), date(2012, 2, 29)}, + {SimplePeriod.of(-4, MONTHS), date(2012, 5, 31), date(2012, 1, 31)}, + }; + } + + @Test(dataProvider="addTo") + public void test_addTo(SimplePeriod period, LocalDate baseDate, LocalDate expected) { + assertEquals(period.addTo(baseDate), expected); + } + + @Test(dataProvider="addTo") + public void test_addTo_usingLocalDatePlus(SimplePeriod period, LocalDate baseDate, LocalDate expected) { + assertEquals(baseDate.plus(period), expected); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_addTo_nullZero() { + SimplePeriod.of(0, DAYS).addTo(null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_addTo_nullNonZero() { + SimplePeriod.of(2, DAYS).addTo(null); + } + + //----------------------------------------------------------------------- + // subtractFrom() + //----------------------------------------------------------------------- + @DataProvider(name="subtractFrom") + Object[][] data_subtractFrom() { + return new Object[][] { + {SimplePeriod.of(0, DAYS), date(2012, 6, 30), date(2012, 6, 30)}, + + {SimplePeriod.of(1, DAYS), date(2012, 6, 30), date(2012, 6, 29)}, + {SimplePeriod.of(-1, DAYS), date(2012, 6, 30), date(2012, 7, 1)}, + + {SimplePeriod.of(2, DAYS), date(2012, 6, 30), date(2012, 6, 28)}, + {SimplePeriod.of(-2, DAYS), date(2012, 6, 30), date(2012, 7, 2)}, + + {SimplePeriod.of(3, MONTHS), date(2012, 5, 31), date(2012, 2, 29)}, + {SimplePeriod.of(4, MONTHS), date(2012, 5, 31), date(2012, 1, 31)}, + {SimplePeriod.of(-3, MONTHS), date(2012, 5, 31), date(2012, 8, 31)}, + {SimplePeriod.of(-4, MONTHS), date(2012, 5, 31), date(2012, 9, 30)}, + }; + } + + @Test(dataProvider="subtractFrom") + public void test_subtractFrom(SimplePeriod period, LocalDate baseDate, LocalDate expected) { + assertEquals(period.subtractFrom(baseDate), expected); + } + + @Test(dataProvider="subtractFrom") + public void test_subtractFrom_usingLocalDateMinus(SimplePeriod period, LocalDate baseDate, LocalDate expected) { + assertEquals(baseDate.minus(period), expected); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_subtractFrom_nullZero() { + SimplePeriod.of(0, DAYS).subtractFrom(null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_subtractFrom_nullNonZero() { + SimplePeriod.of(2, DAYS).subtractFrom(null); + } + + //----------------------------------------------------------------------- + // abs() + //----------------------------------------------------------------------- + @Test(dataProvider="samples") + public void test_abs(long amount, TemporalUnit unit) { + SimplePeriod test = SimplePeriod.of(amount, unit); + if (amount >= 0) { + assertSame(test.abs(), test); // spec requires assertSame + } else if (amount == Long.MIN_VALUE) { + // ignore, separately tested + } else { + assertEquals(test.abs(), SimplePeriod.of(-amount, unit)); + } + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_abs_minValue() { + SimplePeriod.of(Long.MIN_VALUE, SECONDS).abs(); + } + + //----------------------------------------------------------------------- + // equals() / hashCode() + //----------------------------------------------------------------------- + @Test(dataProvider="samples") + public void test_equals(long amount, TemporalUnit unit) { + SimplePeriod test1 = SimplePeriod.of(amount, unit); + SimplePeriod test2 = SimplePeriod.of(amount, unit); + assertEquals(test1, test2); + } + + @Test(dataProvider="samples") + public void test_equals_self(long amount, TemporalUnit unit) { + SimplePeriod test = SimplePeriod.of(amount, unit); + assertEquals(test.equals(test), true); + } + + public void test_equals_null() { + assertEquals(TEST_12_MONTHS.equals(null), false); + } + + public void test_equals_otherClass() { + Period test = Period.of(1, 2, 3, 4, 5, 6); + assertEquals(test.equals(""), false); + } + + //----------------------------------------------------------------------- + public void test_hashCode() { + SimplePeriod test5 = SimplePeriod.of(5, DAYS); + SimplePeriod test6 = SimplePeriod.of(6, DAYS); + SimplePeriod test5M = SimplePeriod.of(5, MONTHS); + SimplePeriod test5Y = SimplePeriod.of(5, YEARS); + assertEquals(test5.hashCode() == test5.hashCode(), true); + assertEquals(test5.hashCode() == test6.hashCode(), false); + assertEquals(test5.hashCode() == test5M.hashCode(), false); + assertEquals(test5.hashCode() == test5Y.hashCode(), false); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @Test(dataProvider="samples") + public void test_toString(long amount, TemporalUnit unit) { + SimplePeriod test = SimplePeriod.of(amount, unit); + assertEquals(test.toString(), amount + " " + unit.getName()); + } + + private static LocalDate date(int y, int m, int d) { + return LocalDate.of(y, m, d); + } + +} diff --git a/jdk/test/java/time/tck/java/time/temporal/TCKWeekFields.java b/jdk/test/java/time/tck/java/time/temporal/TCKWeekFields.java new file mode 100644 index 00000000000..9119a70fa65 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/temporal/TCKWeekFields.java @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.temporal; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.fail; + +import static java.time.temporal.ChronoField.DAY_OF_WEEK; +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.DAY_OF_YEAR; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.YEAR; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.temporal.TemporalField; +import java.time.format.DateTimeBuilder; +import java.time.temporal.ValueRange; +import java.time.temporal.WeekFields; + +import org.testng.annotations.Test; + +/** + * Test WeekFields. + */ +@Test +public class TCKWeekFields { + + @Test(groups={"tck"}) + public void test_WeekFieldsOf() { + for (DayOfWeek dow : DayOfWeek.values()) { + for (int minDays = 1; minDays <= 7; minDays++) { + WeekFields week = WeekFields.of(dow, minDays); + assertEquals(week.getFirstDayOfWeek(), dow, "Incorrect firstDayOfWeek"); + assertEquals(week.getMinimalDaysInFirstWeek(), minDays, "Incorrect MinimalDaysInFirstWeek"); + } + } + } + + @Test(groups={"tck"}) + public void test_DayOfWeek() { + LocalDate day = LocalDate.of(2000, 1, 10); // Known to be ISO Monday + for (DayOfWeek firstDayOfWeek : DayOfWeek.values()) { + for (int minDays = 1; minDays <= 7; minDays++) { + WeekFields week = WeekFields.of(firstDayOfWeek, minDays); + TemporalField f = week.dayOfWeek(); + //System.out.printf(" Week: %s; field: %s%n", week, f); + + for (int i = 1; i <= 7; i++) { + //System.out.printf(" ISO Dow: %s, WeekDOW ordinal: %s%n", day.getDayOfWeek(), day.get(f)); + assertEquals(day.get(f), (7 + day.getDayOfWeek().getValue() - firstDayOfWeek.getValue()) % 7 + 1); + day = day.plusDays(1); + } + } + } + } + + @Test(groups={"tck"}) + public void test_WeekOfMonth() { + for (DayOfWeek firstDayOfWeek : DayOfWeek.values()) { + for (int minDays = 1; minDays <= 7; minDays++) { + LocalDate day = LocalDate.of(2012, 12, 31); // Known to be ISO Monday + WeekFields week = WeekFields.of(firstDayOfWeek, minDays); + TemporalField dowField = week.dayOfWeek(); + TemporalField womField = week.weekOfMonth(); + //System.err.printf("%n Week: %s; dowField: %s, domField: %s%n", week, dowField, womField); + + DayOfWeek isoDOW = day.getDayOfWeek(); + int dow = (7 + isoDOW.getValue() - firstDayOfWeek.getValue()) % 7 + 1; + + for (int i = 1; i <= 15; i++) { + int actualDOW = day.get(dowField); + int actualWOM = day.get(womField); + + // Verify that the combination of day of week and week of month can be used + // to reconstruct the same date. + LocalDate day1 = day.withDayOfMonth(1); + int offset = - (day1.get(dowField) - 1); + //System.err.printf(" refDay: %s%n", day1.plusDays(offset)); + int week1 = day1.get(womField); + if (week1 == 0) { + // week of the 1st is partial; start with first full week + offset += 7; + } + //System.err.printf(" refDay2: %s, offset: %d, week1: %d%n", day1.plusDays(offset), offset, week1); + offset += actualDOW - 1; + //System.err.printf(" refDay3: %s%n", day1.plusDays(offset)); + offset += (actualWOM - 1) * 7; + //System.err.printf(" refDay4: %s%n", day1.plusDays(offset)); + LocalDate result = day1.plusDays(offset); + + if (!day.equals(result)) { + System.err.printf("FAIL ISO Dow: %s, offset: %s, actualDOW: %s, actualWOM: %s, expected: %s, result: %s%n", + day.getDayOfWeek(), offset, actualDOW, actualWOM, day, result); + } + assertEquals(result, day, "Incorrect dayOfWeek or weekOfMonth: " + + String.format("%s, ISO Dow: %s, offset: %s, actualDOW: %s, actualWOM: %s, expected: %s, result: %s%n", + week, day.getDayOfWeek(), offset, actualDOW, actualWOM, day, result)); + day = day.plusDays(1); + } + } + } + } + + @Test(groups={"tck"}) + public void test_WeekOfYear() { + for (DayOfWeek firstDayOfWeek : DayOfWeek.values()) { + for (int minDays = 1; minDays <= 7; minDays++) { + LocalDate day = LocalDate.of(2012, 12, 31); // Known to be ISO Monday + WeekFields week = WeekFields.of(firstDayOfWeek, minDays); + TemporalField dowField = week.dayOfWeek(); + TemporalField woyField = week.weekOfYear(); + //System.err.printf("%n Year: %s; dowField: %s, woyField: %s%n", week, dowField, woyField); + + DayOfWeek isoDOW = day.getDayOfWeek(); + int dow = (7 + isoDOW.getValue() - firstDayOfWeek.getValue()) % 7 + 1; + + for (int i = 1; i <= 15; i++) { + int actualDOW = day.get(dowField); + int actualWOY = day.get(woyField); + + // Verify that the combination of day of week and week of month can be used + // to reconstruct the same date. + LocalDate day1 = day.withDayOfYear(1); + int offset = - (day1.get(dowField) - 1); + //System.err.printf(" refDay: %s%n", day1.plusDays(offset)); + int week1 = day1.get(woyField); + if (week1 == 0) { + // week of the 1st is partial; start with first full week + offset += 7; + } + //System.err.printf(" refDay2: %s, offset: %d, week1: %d%n", day1.plusDays(offset), offset, week1); + offset += actualDOW - 1; + //System.err.printf(" refDay3: %s%n", day1.plusDays(offset)); + offset += (actualWOY - 1) * 7; + //System.err.printf(" refDay4: %s%n", day1.plusDays(offset)); + LocalDate result = day1.plusDays(offset); + + + if (!day.equals(result)) { + System.err.printf("FAIL ISO Dow: %s, offset: %s, actualDOW: %s, actualWOY: %s, expected: %s, result: %s%n", + day.getDayOfWeek(), offset, actualDOW, actualWOY, day, result); + } + assertEquals(result, day, "Incorrect dayOfWeek or weekOfYear " + + String.format("%s, ISO Dow: %s, offset: %s, actualDOW: %s, actualWOM: %s, expected: %s, result: %s%n", + week, day.getDayOfWeek(), offset, actualDOW, actualWOY, day, result)); + day = day.plusDays(1); + } + } + } + } + + @Test(groups={"tck"}) + public void test_fieldRanges() { + for (DayOfWeek firstDayOfWeek : DayOfWeek.values()) { + for (int minDays = 1; minDays <= 7; minDays++) { + WeekFields weekDef = WeekFields.of(firstDayOfWeek, minDays); + TemporalField dowField = weekDef.dayOfWeek(); + TemporalField womField = weekDef.weekOfMonth(); + TemporalField woyField = weekDef.weekOfYear(); + + LocalDate day = LocalDate.of(2012, 11, 30); + LocalDate endDay = LocalDate.of(2013, 1, 2); + while (day.isBefore(endDay)) { + LocalDate last = day.with(DAY_OF_MONTH, day.lengthOfMonth()); + int lastWOM = last.get(womField); + LocalDate first = day.with(DAY_OF_MONTH, 1); + int firstWOM = first.get(womField); + ValueRange rangeWOM = day.range(womField); + assertEquals(rangeWOM.getMinimum(), firstWOM, + "Range min should be same as WeekOfMonth for first day of month: " + + first + ", " + weekDef); + assertEquals(rangeWOM.getMaximum(), lastWOM, + "Range max should be same as WeekOfMonth for last day of month: " + + last + ", " + weekDef); + + last = day.with(DAY_OF_YEAR, day.lengthOfYear()); + int lastWOY = last.get(woyField); + first = day.with(DAY_OF_YEAR, 1); + int firstWOY = first.get(woyField); + ValueRange rangeWOY = day.range(woyField); + assertEquals(rangeWOY.getMinimum(), firstWOY, + "Range min should be same as WeekOfYear for first day of Year: " + + day + ", " + weekDef); + assertEquals(rangeWOY.getMaximum(), lastWOY, + "Range max should be same as WeekOfYear for last day of Year: " + + day + ", " + weekDef); + + day = day.plusDays(1); + } + } + } + } + + //----------------------------------------------------------------------- + // withDayOfWeek() + //----------------------------------------------------------------------- + @Test(groups = {"tck"}) + public void test_withDayOfWeek() { + for (DayOfWeek firstDayOfWeek : DayOfWeek.values()) { + for (int minDays = 1; minDays <= 7; minDays++) { + LocalDate day = LocalDate.of(2012, 12, 15); // Safely in the middle of a month + WeekFields week = WeekFields.of(firstDayOfWeek, minDays); + + TemporalField dowField = week.dayOfWeek(); + TemporalField womField = week.weekOfMonth(); + TemporalField woyField = week.weekOfYear(); + int wom = day.get(womField); + int woy = day.get(woyField); + for (int dow = 1; dow <= 7; dow++) { + LocalDate result = day.with(dowField, dow); + if (result.get(dowField) != dow) { + System.err.printf(" DOW actual: %d, expected: %d, week:%s%n", + result.get(dowField), dow, week); + } + if (result.get(womField) != wom) { + System.err.printf(" WOM actual: %d, expected: %d, week:%s%n", + result.get(womField), wom, week); + } + if (result.get(woyField) != woy) { + System.err.printf(" WOY actual: %d, expected: %d, week:%s%n", + result.get(woyField), woy, week); + } + assertEquals(result.get(dowField), dow, String.format("Incorrect new Day of week: %s", result)); + assertEquals(result.get(womField), wom, "Week of Month should not change"); + assertEquals(result.get(woyField), woy, "Week of Year should not change"); + } + } + } + } + + @Test() + public void test_computedFieldResolver() { + // For all starting days of week, for all minDays, for two weeks in Dec 2012 + // Test that when supplied with partial values, that the resolver + // fills in the month + for (DayOfWeek firstDayOfWeek : DayOfWeek.values()) { + for (int minDays = 1; minDays <= 7; minDays++) { + LocalDate day = LocalDate.of(2012, 12, 15); // Safely in the middle of a month + WeekFields week = WeekFields.of(firstDayOfWeek, minDays); + + TemporalField dowField = week.dayOfWeek(); + TemporalField womField = week.weekOfMonth(); + TemporalField woyField = week.weekOfYear(); + int wom = day.get(womField); + int woy = day.get(woyField); + for (int dow = 1; dow <= 15; dow++) { + // Test that with dayOfWeek and Week of month it computes the day of month + DateTimeBuilder builder = new DateTimeBuilder(); + builder.addFieldValue(YEAR, day.get(YEAR)); + builder.addFieldValue(MONTH_OF_YEAR, day.get(MONTH_OF_YEAR)); + builder.addFieldValue(DAY_OF_WEEK, day.get(DAY_OF_WEEK)); + builder.addFieldValue(dowField, day.get(dowField)); + builder.addFieldValue(womField, day.get(womField)); + + boolean res1 = dowField.resolve(builder, day.get(dowField)); + boolean res2 = womField.resolve(builder, day.get(womField)); + assertEquals(day.get(DAY_OF_MONTH), day.get(DAY_OF_MONTH)); + assertEquals(day.get(DAY_OF_YEAR), day.get(DAY_OF_YEAR)); + + day = day.plusDays(1); + } + day = LocalDate.of(2012, 12, 15); // Safely in the middle of a month + for (int dow = 1; dow <= 15; dow++) { + // Test that with dayOfWeek and Week of year it computes the day of year + DateTimeBuilder builder = new DateTimeBuilder(); + builder.addFieldValue(YEAR, day.get(YEAR)); + builder.addFieldValue(DAY_OF_WEEK, day.get(DAY_OF_WEEK)); + builder.addFieldValue(dowField, day.get(dowField)); + builder.addFieldValue(woyField, day.get(woyField)); + + boolean res1 = dowField.resolve(builder, day.get(dowField)); + boolean res2 = woyField.resolve(builder, day.get(woyField)); + + assertEquals(day.get(DAY_OF_MONTH), day.get(DAY_OF_MONTH)); + assertEquals(day.get(DAY_OF_YEAR), day.get(DAY_OF_YEAR)); + + day = day.plusDays(1); + } + } + } + } + + @Test(groups = {"tck"}) + public void test_WeekFieldsSingleton() throws IOException, ClassNotFoundException { + for (DayOfWeek firstDayOfWeek : DayOfWeek.values()) { + for (int minDays = 1; minDays <= 7; minDays++) { + WeekFields weekDef = WeekFields.of(firstDayOfWeek, minDays); + WeekFields weekDef2 = WeekFields.of(firstDayOfWeek, minDays); + assertSame(weekDef2, weekDef, "WeekFields same parameters should be same instance"); + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos)) { + oos.writeObject(weekDef); + + ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream( + baos.toByteArray())); + WeekFields result = (WeekFields)ois.readObject(); + assertSame(result, weekDef, "Deserialized object same as serialized."); + } + // Exceptions will be handled as failures by TestNG + } + } + } +} diff --git a/jdk/test/java/time/tck/java/time/temporal/TCKYear.java b/jdk/test/java/time/tck/java/time/temporal/TCKYear.java new file mode 100644 index 00000000000..5464c6b4241 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/temporal/TCKYear.java @@ -0,0 +1,781 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.temporal; + +import static java.time.temporal.ChronoField.ERA; +import static java.time.temporal.ChronoField.YEAR; +import static java.time.temporal.ChronoField.YEAR_OF_ERA; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import java.time.Clock; +import java.time.DateTimeException; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.Month; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatters; +import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoField; +import java.time.temporal.JulianFields; +import java.time.temporal.MonthDay; +import java.time.temporal.OffsetDateTime; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalField; +import java.time.temporal.Year; +import java.time.temporal.YearMonth; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import tck.java.time.AbstractDateTimeTest; + +/** + * Test Year. + */ +@Test +public class TCKYear extends AbstractDateTimeTest { + + private static final Year TEST_2008 = Year.of(2008); + + @BeforeMethod + public void setUp() { + } + + //----------------------------------------------------------------------- + @Override + protected List samples() { + TemporalAccessor[] array = {TEST_2008, }; + return Arrays.asList(array); + } + + @Override + protected List validFields() { + TemporalField[] array = { + YEAR_OF_ERA, + YEAR, + ERA, + }; + return Arrays.asList(array); + } + + @Override + protected List invalidFields() { + List list = new ArrayList<>(Arrays.asList(ChronoField.values())); + list.removeAll(validFields()); + list.add(JulianFields.JULIAN_DAY); + list.add(JulianFields.MODIFIED_JULIAN_DAY); + list.add(JulianFields.RATA_DIE); + return list; + } + + //----------------------------------------------------------------------- + @Test + public void test_serialization() throws Exception { + assertSerializable(Year.of(2)); + assertSerializable(Year.of(0)); + assertSerializable(Year.of(-2)); + } + + @Test + public void test_serialization_format() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baos) ) { + dos.writeByte(4); + dos.writeInt(2012); + } + byte[] bytes = baos.toByteArray(); + assertSerializedBySer(Year.of(2012), bytes); + } + + //----------------------------------------------------------------------- + // now() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void now() { + Year expected = Year.now(Clock.systemDefaultZone()); + Year test = Year.now(); + for (int i = 0; i < 100; i++) { + if (expected.equals(test)) { + return; + } + expected = Year.now(Clock.systemDefaultZone()); + test = Year.now(); + } + assertEquals(test, expected); + } + + //----------------------------------------------------------------------- + // now(ZoneId) + //----------------------------------------------------------------------- + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void now_ZoneId_nullZoneId() { + Year.now((ZoneId) null); + } + + @Test(groups={"tck"}) + public void now_ZoneId() { + ZoneId zone = ZoneId.of("UTC+01:02:03"); + Year expected = Year.now(Clock.system(zone)); + Year test = Year.now(zone); + for (int i = 0; i < 100; i++) { + if (expected.equals(test)) { + return; + } + expected = Year.now(Clock.system(zone)); + test = Year.now(zone); + } + assertEquals(test, expected); + } + + //----------------------------------------------------------------------- + // now(Clock) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void now_Clock() { + Instant instant = OffsetDateTime.of(LocalDate.of(2010, 12, 31), LocalTime.of(0, 0), ZoneOffset.UTC).toInstant(); + Clock clock = Clock.fixed(instant, ZoneOffset.UTC); + Year test = Year.now(clock); + assertEquals(test.getValue(), 2010); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void now_Clock_nullClock() { + Year.now((Clock) null); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_factory_int_singleton() { + for (int i = -4; i <= 2104; i++) { + Year test = Year.of(i); + assertEquals(test.getValue(), i); + assertEquals(Year.of(i), test); + } + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_int_tooLow() { + Year.of(Year.MIN_VALUE - 1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_int_tooHigh() { + Year.of(Year.MAX_VALUE + 1); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_factory_CalendricalObject() { + assertEquals(Year.from(LocalDate.of(2007, 7, 15)), Year.of(2007)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_CalendricalObject_invalid_noDerive() { + Year.from(LocalTime.of(12, 30)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_factory_CalendricalObject_null() { + Year.from((TemporalAccessor) null); + } + + //----------------------------------------------------------------------- + // parse() + //----------------------------------------------------------------------- + @DataProvider(name="goodParseData") + Object[][] provider_goodParseData() { + return new Object[][] { + {"0000", Year.of(0)}, + {"9999", Year.of(9999)}, + {"2000", Year.of(2000)}, + + {"+12345678", Year.of(12345678)}, + {"+123456", Year.of(123456)}, + {"-1234", Year.of(-1234)}, + {"-12345678", Year.of(-12345678)}, + + {"+" + Year.MAX_VALUE, Year.of(Year.MAX_VALUE)}, + {"" + Year.MIN_VALUE, Year.of(Year.MIN_VALUE)}, + }; + } + + @Test(dataProvider="goodParseData", groups={"tck"}) + public void factory_parse_success(String text, Year expected) { + Year year = Year.parse(text); + assertEquals(year, expected); + } + + @DataProvider(name="badParseData") + Object[][] provider_badParseData() { + return new Object[][] { + {"", 0}, + {"-00", 1}, + {"--01-0", 1}, + {"A01", 0}, + {"200", 0}, + {"2009/12", 4}, + + {"-0000-10", 0}, + {"-12345678901-10", 11}, + {"+1-10", 1}, + {"+12-10", 1}, + {"+123-10", 1}, + {"+1234-10", 0}, + {"12345-10", 0}, + {"+12345678901-10", 11}, + }; + } + + @Test(dataProvider="badParseData", expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_fail(String text, int pos) { + try { + Year.parse(text); + fail(String.format("Parse should have failed for %s at position %d", text, pos)); + } catch (DateTimeParseException ex) { + assertEquals(ex.getParsedString(), text); + assertEquals(ex.getErrorIndex(), pos); + throw ex; + } + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_nullText() { + Year.parse(null); + } + + //----------------------------------------------------------------------- + // parse(DateTimeFormatter) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_parse_formatter() { + DateTimeFormatter f = DateTimeFormatters.pattern("y"); + Year test = Year.parse("2010", f); + assertEquals(test, Year.of(2010)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_formatter_nullText() { + DateTimeFormatter f = DateTimeFormatters.pattern("y"); + Year.parse((String) null, f); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_formatter_nullFormatter() { + Year.parse("ANY", null); + } + + //----------------------------------------------------------------------- + // get(TemporalField) + //----------------------------------------------------------------------- + @Test + public void test_get_TemporalField() { + assertEquals(TEST_2008.get(ChronoField.YEAR), 2008); + assertEquals(TEST_2008.get(ChronoField.YEAR_OF_ERA), 2008); + assertEquals(TEST_2008.get(ChronoField.ERA), 1); + } + + @Test + public void test_getLong_TemporalField() { + assertEquals(TEST_2008.getLong(ChronoField.YEAR), 2008); + assertEquals(TEST_2008.getLong(ChronoField.YEAR_OF_ERA), 2008); + assertEquals(TEST_2008.getLong(ChronoField.ERA), 1); + } + + //----------------------------------------------------------------------- + // isLeap() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_isLeap() { + assertEquals(Year.of(1999).isLeap(), false); + assertEquals(Year.of(2000).isLeap(), true); + assertEquals(Year.of(2001).isLeap(), false); + + assertEquals(Year.of(2007).isLeap(), false); + assertEquals(Year.of(2008).isLeap(), true); + assertEquals(Year.of(2009).isLeap(), false); + assertEquals(Year.of(2010).isLeap(), false); + assertEquals(Year.of(2011).isLeap(), false); + assertEquals(Year.of(2012).isLeap(), true); + + assertEquals(Year.of(2095).isLeap(), false); + assertEquals(Year.of(2096).isLeap(), true); + assertEquals(Year.of(2097).isLeap(), false); + assertEquals(Year.of(2098).isLeap(), false); + assertEquals(Year.of(2099).isLeap(), false); + assertEquals(Year.of(2100).isLeap(), false); + assertEquals(Year.of(2101).isLeap(), false); + assertEquals(Year.of(2102).isLeap(), false); + assertEquals(Year.of(2103).isLeap(), false); + assertEquals(Year.of(2104).isLeap(), true); + assertEquals(Year.of(2105).isLeap(), false); + + assertEquals(Year.of(-500).isLeap(), false); + assertEquals(Year.of(-400).isLeap(), true); + assertEquals(Year.of(-300).isLeap(), false); + assertEquals(Year.of(-200).isLeap(), false); + assertEquals(Year.of(-100).isLeap(), false); + assertEquals(Year.of(0).isLeap(), true); + assertEquals(Year.of(100).isLeap(), false); + assertEquals(Year.of(200).isLeap(), false); + assertEquals(Year.of(300).isLeap(), false); + assertEquals(Year.of(400).isLeap(), true); + assertEquals(Year.of(500).isLeap(), false); + } + + //----------------------------------------------------------------------- + // plusYears() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusYears() { + assertEquals(Year.of(2007).plusYears(-1), Year.of(2006)); + assertEquals(Year.of(2007).plusYears(0), Year.of(2007)); + assertEquals(Year.of(2007).plusYears(1), Year.of(2008)); + assertEquals(Year.of(2007).plusYears(2), Year.of(2009)); + + assertEquals(Year.of(Year.MAX_VALUE - 1).plusYears(1), Year.of(Year.MAX_VALUE)); + assertEquals(Year.of(Year.MAX_VALUE).plusYears(0), Year.of(Year.MAX_VALUE)); + + assertEquals(Year.of(Year.MIN_VALUE + 1).plusYears(-1), Year.of(Year.MIN_VALUE)); + assertEquals(Year.of(Year.MIN_VALUE).plusYears(0), Year.of(Year.MIN_VALUE)); + } + + @Test(groups={"tck"}) + public void test_plusYear_zero_equals() { + Year base = Year.of(2007); + assertEquals(base.plusYears(0), base); + } + + @Test(groups={"tck"}) + public void test_plusYears_big() { + long years = 20L + Year.MAX_VALUE; + assertEquals(Year.of(-40).plusYears(years), Year.of((int) (-40L + years))); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusYears_max() { + Year.of(Year.MAX_VALUE).plusYears(1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusYears_maxLots() { + Year.of(Year.MAX_VALUE).plusYears(1000); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusYears_min() { + Year.of(Year.MIN_VALUE).plusYears(-1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusYears_minLots() { + Year.of(Year.MIN_VALUE).plusYears(-1000); + } + + //----------------------------------------------------------------------- + // minusYears() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusYears() { + assertEquals(Year.of(2007).minusYears(-1), Year.of(2008)); + assertEquals(Year.of(2007).minusYears(0), Year.of(2007)); + assertEquals(Year.of(2007).minusYears(1), Year.of(2006)); + assertEquals(Year.of(2007).minusYears(2), Year.of(2005)); + + assertEquals(Year.of(Year.MAX_VALUE - 1).minusYears(-1), Year.of(Year.MAX_VALUE)); + assertEquals(Year.of(Year.MAX_VALUE).minusYears(0), Year.of(Year.MAX_VALUE)); + + assertEquals(Year.of(Year.MIN_VALUE + 1).minusYears(1), Year.of(Year.MIN_VALUE)); + assertEquals(Year.of(Year.MIN_VALUE).minusYears(0), Year.of(Year.MIN_VALUE)); + } + + @Test(groups={"tck"}) + public void test_minusYear_zero_equals() { + Year base = Year.of(2007); + assertEquals(base.minusYears(0), base); + } + + @Test(groups={"tck"}) + public void test_minusYears_big() { + long years = 20L + Year.MAX_VALUE; + assertEquals(Year.of(40).minusYears(years), Year.of((int) (40L - years))); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusYears_max() { + Year.of(Year.MAX_VALUE).minusYears(-1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusYears_maxLots() { + Year.of(Year.MAX_VALUE).minusYears(-1000); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusYears_min() { + Year.of(Year.MIN_VALUE).minusYears(1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusYears_minLots() { + Year.of(Year.MIN_VALUE).minusYears(1000); + } + + //----------------------------------------------------------------------- + // adjustInto() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_adjustDate() { + LocalDate base = LocalDate.of(2007, 2, 12); + for (int i = -4; i <= 2104; i++) { + Temporal result = Year.of(i).adjustInto(base); + assertEquals(result, LocalDate.of(i, 2, 12)); + } + } + + @Test(groups={"tck"}) + public void test_adjustDate_resolve() { + Year test = Year.of(2011); + assertEquals(test.adjustInto(LocalDate.of(2012, 2, 29)), LocalDate.of(2011, 2, 28)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_adjustDate_nullLocalDate() { + Year test = Year.of(1); + test.adjustInto((LocalDate) null); + } + + //----------------------------------------------------------------------- + // length() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_length() { + assertEquals(Year.of(1999).length(), 365); + assertEquals(Year.of(2000).length(), 366); + assertEquals(Year.of(2001).length(), 365); + + assertEquals(Year.of(2007).length(), 365); + assertEquals(Year.of(2008).length(), 366); + assertEquals(Year.of(2009).length(), 365); + assertEquals(Year.of(2010).length(), 365); + assertEquals(Year.of(2011).length(), 365); + assertEquals(Year.of(2012).length(), 366); + + assertEquals(Year.of(2095).length(), 365); + assertEquals(Year.of(2096).length(), 366); + assertEquals(Year.of(2097).length(), 365); + assertEquals(Year.of(2098).length(), 365); + assertEquals(Year.of(2099).length(), 365); + assertEquals(Year.of(2100).length(), 365); + assertEquals(Year.of(2101).length(), 365); + assertEquals(Year.of(2102).length(), 365); + assertEquals(Year.of(2103).length(), 365); + assertEquals(Year.of(2104).length(), 366); + assertEquals(Year.of(2105).length(), 365); + + assertEquals(Year.of(-500).length(), 365); + assertEquals(Year.of(-400).length(), 366); + assertEquals(Year.of(-300).length(), 365); + assertEquals(Year.of(-200).length(), 365); + assertEquals(Year.of(-100).length(), 365); + assertEquals(Year.of(0).length(), 366); + assertEquals(Year.of(100).length(), 365); + assertEquals(Year.of(200).length(), 365); + assertEquals(Year.of(300).length(), 365); + assertEquals(Year.of(400).length(), 366); + assertEquals(Year.of(500).length(), 365); + } + + //----------------------------------------------------------------------- + // isValidMonthDay(Month) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_isValidMonthDay_june() { + Year test = Year.of(2007); + MonthDay monthDay = MonthDay.of(6, 30); + assertEquals(test.isValidMonthDay(monthDay), true); + } + + @Test(groups={"tck"}) + public void test_isValidMonthDay_febNonLeap() { + Year test = Year.of(2007); + MonthDay monthDay = MonthDay.of(2, 29); + assertEquals(test.isValidMonthDay(monthDay), false); + } + + @Test(groups={"tck"}) + public void test_isValidMonthDay_febLeap() { + Year test = Year.of(2008); + MonthDay monthDay = MonthDay.of(2, 29); + assertEquals(test.isValidMonthDay(monthDay), true); + } + + @Test(groups={"tck"}) + public void test_isValidMonthDay_null() { + Year test = Year.of(2008); + assertEquals(test.isValidMonthDay(null), false); + } + + //----------------------------------------------------------------------- + // atMonth(Month) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_atMonth() { + Year test = Year.of(2008); + assertEquals(test.atMonth(Month.JUNE), YearMonth.of(2008, 6)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_atMonth_nullMonth() { + Year test = Year.of(2008); + test.atMonth((Month) null); + } + + //----------------------------------------------------------------------- + // atMonth(int) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_atMonth_int() { + Year test = Year.of(2008); + assertEquals(test.atMonth(6), YearMonth.of(2008, 6)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atMonth_int_invalidMonth() { + Year test = Year.of(2008); + test.atMonth(13); + } + + //----------------------------------------------------------------------- + // atMonthDay(Month) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_atMonthDay() { + Year test = Year.of(2008); + assertEquals(test.atMonthDay(MonthDay.of(6, 30)), LocalDate.of(2008, 6, 30)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_atMonthDay_nullMonthDay() { + Year test = Year.of(2008); + test.atMonthDay((MonthDay) null); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atMonthDay_invalidMonthDay() { + Year test = Year.of(2008); + test.atMonthDay(MonthDay.of(6, 31)); + } + + //----------------------------------------------------------------------- + // atDay(int) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_atDay_notLeapYear() { + Year test = Year.of(2007); + LocalDate expected = LocalDate.of(2007, 1, 1); + for (int i = 1; i <= 365; i++) { + assertEquals(test.atDay(i), expected); + expected = expected.plusDays(1); + } + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atDay_notLeapYear_day366() { + Year test = Year.of(2007); + test.atDay(366); + } + + @Test(groups={"tck"}) + public void test_atDay_leapYear() { + Year test = Year.of(2008); + LocalDate expected = LocalDate.of(2008, 1, 1); + for (int i = 1; i <= 366; i++) { + assertEquals(test.atDay(i), expected); + expected = expected.plusDays(1); + } + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atDay_day0() { + Year test = Year.of(2007); + test.atDay(0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atDay_day367() { + Year test = Year.of(2007); + test.atDay(367); + } + + //----------------------------------------------------------------------- + // compareTo() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_compareTo() { + for (int i = -4; i <= 2104; i++) { + Year a = Year.of(i); + for (int j = -4; j <= 2104; j++) { + Year b = Year.of(j); + if (i < j) { + assertEquals(a.compareTo(b) < 0, true); + assertEquals(b.compareTo(a) > 0, true); + assertEquals(a.isAfter(b), false); + assertEquals(a.isBefore(b), true); + assertEquals(b.isAfter(a), true); + assertEquals(b.isBefore(a), false); + } else if (i > j) { + assertEquals(a.compareTo(b) > 0, true); + assertEquals(b.compareTo(a) < 0, true); + assertEquals(a.isAfter(b), true); + assertEquals(a.isBefore(b), false); + assertEquals(b.isAfter(a), false); + assertEquals(b.isBefore(a), true); + } else { + assertEquals(a.compareTo(b), 0); + assertEquals(b.compareTo(a), 0); + assertEquals(a.isAfter(b), false); + assertEquals(a.isBefore(b), false); + assertEquals(b.isAfter(a), false); + assertEquals(b.isBefore(a), false); + } + } + } + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_compareTo_nullYear() { + Year doy = null; + Year test = Year.of(1); + test.compareTo(doy); + } + + //----------------------------------------------------------------------- + // equals() / hashCode() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_equals() { + for (int i = -4; i <= 2104; i++) { + Year a = Year.of(i); + for (int j = -4; j <= 2104; j++) { + Year b = Year.of(j); + assertEquals(a.equals(b), i == j); + assertEquals(a.hashCode() == b.hashCode(), i == j); + } + } + } + + @Test(groups={"tck"}) + public void test_equals_same() { + Year test = Year.of(2011); + assertEquals(test.equals(test), true); + } + + @Test(groups={"tck"}) + public void test_equals_nullYear() { + Year doy = null; + Year test = Year.of(1); + assertEquals(test.equals(doy), false); + } + + @Test(groups={"tck"}) + public void test_equals_incorrectType() { + Year test = Year.of(1); + assertEquals(test.equals("Incorrect type"), false); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toString() { + for (int i = -4; i <= 2104; i++) { + Year a = Year.of(i); + assertEquals(a.toString(), "" + i); + } + } + + //----------------------------------------------------------------------- + // toString(DateTimeFormatter) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toString_formatter() { + DateTimeFormatter f = DateTimeFormatters.pattern("y"); + String t = Year.of(2010).toString(f); + assertEquals(t, "2010"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_toString_formatter_null() { + Year.of(2010).toString(null); + } + +} diff --git a/jdk/test/java/time/tck/java/time/temporal/TCKYearMonth.java b/jdk/test/java/time/tck/java/time/temporal/TCKYearMonth.java new file mode 100644 index 00000000000..25d57f1a22b --- /dev/null +++ b/jdk/test/java/time/tck/java/time/temporal/TCKYearMonth.java @@ -0,0 +1,1046 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.temporal; + +import static java.time.temporal.ChronoField.EPOCH_MONTH; +import static java.time.temporal.ChronoField.ERA; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.YEAR; +import static java.time.temporal.ChronoField.YEAR_OF_ERA; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import java.time.Clock; +import java.time.DateTimeException; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Month; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatters; +import java.time.format.DateTimeParseException; +import java.time.temporal.ChronoField; +import java.time.temporal.JulianFields; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalField; +import java.time.temporal.Year; +import java.time.temporal.YearMonth; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import tck.java.time.AbstractDateTimeTest; + +/** + * Test YearMonth. + */ +@Test +public class TCKYearMonth extends AbstractDateTimeTest { + + private YearMonth TEST_2008_06; + + @BeforeMethod(groups={"tck", "implementation"}) + public void setUp() { + TEST_2008_06 = YearMonth.of(2008, 6); + } + + //----------------------------------------------------------------------- + @Override + protected List samples() { + TemporalAccessor[] array = {TEST_2008_06, }; + return Arrays.asList(array); + } + + @Override + protected List validFields() { + TemporalField[] array = { + MONTH_OF_YEAR, + EPOCH_MONTH, + YEAR_OF_ERA, + YEAR, + ERA, + }; + return Arrays.asList(array); + } + + @Override + protected List invalidFields() { + List list = new ArrayList<>(Arrays.asList(ChronoField.values())); + list.removeAll(validFields()); + list.add(JulianFields.JULIAN_DAY); + list.add(JulianFields.MODIFIED_JULIAN_DAY); + list.add(JulianFields.RATA_DIE); + return list; + } + + //----------------------------------------------------------------------- + @Test + public void test_serialization() throws IOException, ClassNotFoundException { + assertSerializable(TEST_2008_06); + } + + @Test + public void test_serialization_format() throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (DataOutputStream dos = new DataOutputStream(baos) ) { + dos.writeByte(5); + dos.writeInt(2012); + dos.writeByte(9); + } + byte[] bytes = baos.toByteArray(); + assertSerializedBySer(YearMonth.of(2012, 9), bytes); + } + + //----------------------------------------------------------------------- + void check(YearMonth test, int y, int m) { + assertEquals(test.getYear(), y); + assertEquals(test.getMonth().getValue(), m); + } + + //----------------------------------------------------------------------- + // now() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void now() { + YearMonth expected = YearMonth.now(Clock.systemDefaultZone()); + YearMonth test = YearMonth.now(); + for (int i = 0; i < 100; i++) { + if (expected.equals(test)) { + return; + } + expected = YearMonth.now(Clock.systemDefaultZone()); + test = YearMonth.now(); + } + assertEquals(test, expected); + } + + //----------------------------------------------------------------------- + // now(ZoneId) + //----------------------------------------------------------------------- + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void now_ZoneId_nullZoneId() { + YearMonth.now((ZoneId) null); + } + + @Test(groups={"tck"}) + public void now_ZoneId() { + ZoneId zone = ZoneId.of("UTC+01:02:03"); + YearMonth expected = YearMonth.now(Clock.system(zone)); + YearMonth test = YearMonth.now(zone); + for (int i = 0; i < 100; i++) { + if (expected.equals(test)) { + return; + } + expected = YearMonth.now(Clock.system(zone)); + test = YearMonth.now(zone); + } + assertEquals(test, expected); + } + + //----------------------------------------------------------------------- + // now(Clock) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void now_Clock() { + Instant instant = LocalDateTime.of(2010, 12, 31, 0, 0).toInstant(ZoneOffset.UTC); + Clock clock = Clock.fixed(instant, ZoneOffset.UTC); + YearMonth test = YearMonth.now(clock); + assertEquals(test.getYear(), 2010); + assertEquals(test.getMonth(), Month.DECEMBER); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void now_Clock_nullClock() { + YearMonth.now((Clock) null); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_intsMonth() { + YearMonth test = YearMonth.of(2008, Month.FEBRUARY); + check(test, 2008, 2); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_intsMonth_yearTooLow() { + YearMonth.of(Year.MIN_VALUE - 1, Month.JANUARY); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_intsMonth_dayTooHigh() { + YearMonth.of(Year.MAX_VALUE + 1, Month.JANUARY); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_intsMonth_nullMonth() { + YearMonth.of(2008, null); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_ints() { + YearMonth test = YearMonth.of(2008, 2); + check(test, 2008, 2); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_ints_yearTooLow() { + YearMonth.of(Year.MIN_VALUE - 1, 2); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_ints_dayTooHigh() { + YearMonth.of(Year.MAX_VALUE + 1, 2); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_ints_monthTooLow() { + YearMonth.of(2008, 0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_ints_monthTooHigh() { + YearMonth.of(2008, 13); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_factory_CalendricalObject() { + assertEquals(YearMonth.from(LocalDate.of(2007, 7, 15)), YearMonth.of(2007, 7)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_factory_CalendricalObject_invalid_noDerive() { + YearMonth.from(LocalTime.of(12, 30)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_factory_CalendricalObject_null() { + YearMonth.from((TemporalAccessor) null); + } + + //----------------------------------------------------------------------- + // parse() + //----------------------------------------------------------------------- + @DataProvider(name="goodParseData") + Object[][] provider_goodParseData() { + return new Object[][] { + {"0000-01", YearMonth.of(0, 1)}, + {"0000-12", YearMonth.of(0, 12)}, + {"9999-12", YearMonth.of(9999, 12)}, + {"2000-01", YearMonth.of(2000, 1)}, + {"2000-02", YearMonth.of(2000, 2)}, + {"2000-03", YearMonth.of(2000, 3)}, + {"2000-04", YearMonth.of(2000, 4)}, + {"2000-05", YearMonth.of(2000, 5)}, + {"2000-06", YearMonth.of(2000, 6)}, + {"2000-07", YearMonth.of(2000, 7)}, + {"2000-08", YearMonth.of(2000, 8)}, + {"2000-09", YearMonth.of(2000, 9)}, + {"2000-10", YearMonth.of(2000, 10)}, + {"2000-11", YearMonth.of(2000, 11)}, + {"2000-12", YearMonth.of(2000, 12)}, + + {"+12345678-03", YearMonth.of(12345678, 3)}, + {"+123456-03", YearMonth.of(123456, 3)}, + {"0000-03", YearMonth.of(0, 3)}, + {"-1234-03", YearMonth.of(-1234, 3)}, + {"-12345678-03", YearMonth.of(-12345678, 3)}, + + {"+" + Year.MAX_VALUE + "-03", YearMonth.of(Year.MAX_VALUE, 3)}, + {Year.MIN_VALUE + "-03", YearMonth.of(Year.MIN_VALUE, 3)}, + }; + } + + @Test(dataProvider="goodParseData", groups={"tck"}) + public void factory_parse_success(String text, YearMonth expected) { + YearMonth yearMonth = YearMonth.parse(text); + assertEquals(yearMonth, expected); + } + + //----------------------------------------------------------------------- + @DataProvider(name="badParseData") + Object[][] provider_badParseData() { + return new Object[][] { + {"", 0}, + {"-00", 1}, + {"--01-0", 1}, + {"A01-3", 0}, + {"200-01", 0}, + {"2009/12", 4}, + + {"-0000-10", 0}, + {"-12345678901-10", 11}, + {"+1-10", 1}, + {"+12-10", 1}, + {"+123-10", 1}, + {"+1234-10", 0}, + {"12345-10", 0}, + {"+12345678901-10", 11}, + }; + } + + @Test(dataProvider="badParseData", expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_fail(String text, int pos) { + try { + YearMonth.parse(text); + fail(String.format("Parse should have failed for %s at position %d", text, pos)); + } catch (DateTimeParseException ex) { + assertEquals(ex.getParsedString(), text); + assertEquals(ex.getErrorIndex(), pos); + throw ex; + } + } + + //----------------------------------------------------------------------- + @Test(expectedExceptions=DateTimeParseException.class, groups={"tck"}) + public void factory_parse_illegalValue_Month() { + YearMonth.parse("2008-13"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_nullText() { + YearMonth.parse(null); + } + + //----------------------------------------------------------------------- + // parse(DateTimeFormatter) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void factory_parse_formatter() { + DateTimeFormatter f = DateTimeFormatters.pattern("y M"); + YearMonth test = YearMonth.parse("2010 12", f); + assertEquals(test, YearMonth.of(2010, 12)); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_formatter_nullText() { + DateTimeFormatter f = DateTimeFormatters.pattern("y M"); + YearMonth.parse((String) null, f); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void factory_parse_formatter_nullFormatter() { + YearMonth.parse("ANY", null); + } + + //----------------------------------------------------------------------- + // get(TemporalField) + //----------------------------------------------------------------------- + @Test + public void test_get_TemporalField() { + assertEquals(TEST_2008_06.get(ChronoField.YEAR), 2008); + assertEquals(TEST_2008_06.get(ChronoField.MONTH_OF_YEAR), 6); + assertEquals(TEST_2008_06.get(ChronoField.YEAR_OF_ERA), 2008); + assertEquals(TEST_2008_06.get(ChronoField.ERA), 1); + } + + @Test + public void test_getLong_TemporalField() { + assertEquals(TEST_2008_06.getLong(ChronoField.YEAR), 2008); + assertEquals(TEST_2008_06.getLong(ChronoField.MONTH_OF_YEAR), 6); + assertEquals(TEST_2008_06.getLong(ChronoField.YEAR_OF_ERA), 2008); + assertEquals(TEST_2008_06.getLong(ChronoField.ERA), 1); + assertEquals(TEST_2008_06.getLong(ChronoField.EPOCH_MONTH), (2008 - 1970) * 12 + 6 - 1); + } + + //----------------------------------------------------------------------- + // get*() + //----------------------------------------------------------------------- + @DataProvider(name="sampleDates") + Object[][] provider_sampleDates() { + return new Object[][] { + {2008, 1}, + {2008, 2}, + {-1, 3}, + {0, 12}, + }; + } + + //----------------------------------------------------------------------- + // with(Year) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_with_Year() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.with(Year.of(2000)), YearMonth.of(2000, 6)); + } + + @Test(groups={"tck"}) + public void test_with_Year_noChange_equal() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.with(Year.of(2008)), test); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_with_Year_null() { + YearMonth test = YearMonth.of(2008, 6); + test.with((Year) null); + } + + //----------------------------------------------------------------------- + // with(Month) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_with_Month() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.with(Month.JANUARY), YearMonth.of(2008, 1)); + } + + @Test(groups={"tck"}) + public void test_with_Month_noChange_equal() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.with(Month.JUNE), test); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_with_Month_null() { + YearMonth test = YearMonth.of(2008, 6); + test.with((Month) null); + } + + //----------------------------------------------------------------------- + // withYear() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withYear() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.withYear(1999), YearMonth.of(1999, 6)); + } + + @Test(groups={"tck"}) + public void test_withYear_int_noChange_equal() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.withYear(2008), test); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withYear_tooLow() { + YearMonth test = YearMonth.of(2008, 6); + test.withYear(Year.MIN_VALUE - 1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withYear_tooHigh() { + YearMonth test = YearMonth.of(2008, 6); + test.withYear(Year.MAX_VALUE + 1); + } + + //----------------------------------------------------------------------- + // withMonth() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_withMonth() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.withMonth(1), YearMonth.of(2008, 1)); + } + + @Test(groups={"tck"}) + public void test_withMonth_int_noChange_equal() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.withMonth(6), test); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withMonth_tooLow() { + YearMonth test = YearMonth.of(2008, 6); + test.withMonth(0); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_withMonth_tooHigh() { + YearMonth test = YearMonth.of(2008, 6); + test.withMonth(13); + } + + //----------------------------------------------------------------------- + // plusYears() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusYears_long() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.plusYears(1), YearMonth.of(2009, 6)); + } + + @Test(groups={"tck"}) + public void test_plusYears_long_noChange_equal() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.plusYears(0), test); + } + + @Test(groups={"tck"}) + public void test_plusYears_long_negative() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.plusYears(-1), YearMonth.of(2007, 6)); + } + + @Test(groups={"tck"}) + public void test_plusYears_long_big() { + YearMonth test = YearMonth.of(-40, 6); + assertEquals(test.plusYears(20L + Year.MAX_VALUE), YearMonth.of((int) (-40L + 20L + Year.MAX_VALUE), 6)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusYears_long_invalidTooLarge() { + YearMonth test = YearMonth.of(Year.MAX_VALUE, 6); + test.plusYears(1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusYears_long_invalidTooLargeMaxAddMax() { + YearMonth test = YearMonth.of(Year.MAX_VALUE, 12); + test.plusYears(Long.MAX_VALUE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusYears_long_invalidTooLargeMaxAddMin() { + YearMonth test = YearMonth.of(Year.MAX_VALUE, 12); + test.plusYears(Long.MIN_VALUE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusYears_long_invalidTooSmall() { + YearMonth test = YearMonth.of(Year.MIN_VALUE, 6); + test.plusYears(-1); + } + + //----------------------------------------------------------------------- + // plusMonths() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_plusMonths_long() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.plusMonths(1), YearMonth.of(2008, 7)); + } + + @Test(groups={"tck"}) + public void test_plusMonths_long_noChange_equal() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.plusMonths(0), test); + } + + @Test(groups={"tck"}) + public void test_plusMonths_long_overYears() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.plusMonths(7), YearMonth.of(2009, 1)); + } + + @Test(groups={"tck"}) + public void test_plusMonths_long_negative() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.plusMonths(-1), YearMonth.of(2008, 5)); + } + + @Test(groups={"tck"}) + public void test_plusMonths_long_negativeOverYear() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.plusMonths(-6), YearMonth.of(2007, 12)); + } + + @Test(groups={"tck"}) + public void test_plusMonths_long_big() { + YearMonth test = YearMonth.of(-40, 6); + long months = 20L + Integer.MAX_VALUE; + assertEquals(test.plusMonths(months), YearMonth.of((int) (-40L + months / 12), 6 + (int) (months % 12))); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_plusMonths_long_invalidTooLarge() { + YearMonth test = YearMonth.of(Year.MAX_VALUE, 12); + test.plusMonths(1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusMonths_long_invalidTooLargeMaxAddMax() { + YearMonth test = YearMonth.of(Year.MAX_VALUE, 12); + test.plusMonths(Long.MAX_VALUE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_plusMonths_long_invalidTooLargeMaxAddMin() { + YearMonth test = YearMonth.of(Year.MAX_VALUE, 12); + test.plusMonths(Long.MIN_VALUE); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_plusMonths_long_invalidTooSmall() { + YearMonth test = YearMonth.of(Year.MIN_VALUE, 1); + test.plusMonths(-1); + } + + //----------------------------------------------------------------------- + // minusYears() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusYears_long() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.minusYears(1), YearMonth.of(2007, 6)); + } + + @Test(groups={"tck"}) + public void test_minusYears_long_noChange_equal() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.minusYears(0), test); + } + + @Test(groups={"tck"}) + public void test_minusYears_long_negative() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.minusYears(-1), YearMonth.of(2009, 6)); + } + + @Test(groups={"tck"}) + public void test_minusYears_long_big() { + YearMonth test = YearMonth.of(40, 6); + assertEquals(test.minusYears(20L + Year.MAX_VALUE), YearMonth.of((int) (40L - 20L - Year.MAX_VALUE), 6)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusYears_long_invalidTooLarge() { + YearMonth test = YearMonth.of(Year.MAX_VALUE, 6); + test.minusYears(-1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusYears_long_invalidTooLargeMaxSubtractMax() { + YearMonth test = YearMonth.of(Year.MIN_VALUE, 12); + test.minusYears(Long.MAX_VALUE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusYears_long_invalidTooLargeMaxSubtractMin() { + YearMonth test = YearMonth.of(Year.MIN_VALUE, 12); + test.minusYears(Long.MIN_VALUE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusYears_long_invalidTooSmall() { + YearMonth test = YearMonth.of(Year.MIN_VALUE, 6); + test.minusYears(1); + } + + //----------------------------------------------------------------------- + // minusMonths() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_minusMonths_long() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.minusMonths(1), YearMonth.of(2008, 5)); + } + + @Test(groups={"tck"}) + public void test_minusMonths_long_noChange_equal() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.minusMonths(0), test); + } + + @Test(groups={"tck"}) + public void test_minusMonths_long_overYears() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.minusMonths(6), YearMonth.of(2007, 12)); + } + + @Test(groups={"tck"}) + public void test_minusMonths_long_negative() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.minusMonths(-1), YearMonth.of(2008, 7)); + } + + @Test(groups={"tck"}) + public void test_minusMonths_long_negativeOverYear() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.minusMonths(-7), YearMonth.of(2009, 1)); + } + + @Test(groups={"tck"}) + public void test_minusMonths_long_big() { + YearMonth test = YearMonth.of(40, 6); + long months = 20L + Integer.MAX_VALUE; + assertEquals(test.minusMonths(months), YearMonth.of((int) (40L - months / 12), 6 - (int) (months % 12))); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_minusMonths_long_invalidTooLarge() { + YearMonth test = YearMonth.of(Year.MAX_VALUE, 12); + test.minusMonths(-1); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusMonths_long_invalidTooLargeMaxSubtractMax() { + YearMonth test = YearMonth.of(Year.MAX_VALUE, 12); + test.minusMonths(Long.MAX_VALUE); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_minusMonths_long_invalidTooLargeMaxSubtractMin() { + YearMonth test = YearMonth.of(Year.MAX_VALUE, 12); + test.minusMonths(Long.MIN_VALUE); + } + + @Test(expectedExceptions={DateTimeException.class}, groups={"tck"}) + public void test_minusMonths_long_invalidTooSmall() { + YearMonth test = YearMonth.of(Year.MIN_VALUE, 1); + test.minusMonths(1); + } + + //----------------------------------------------------------------------- + // adjustInto() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_adjustDate() { + YearMonth test = YearMonth.of(2008, 6); + LocalDate date = LocalDate.of(2007, 1, 1); + assertEquals(test.adjustInto(date), LocalDate.of(2008, 6, 1)); + } + + @Test(groups={"tck"}) + public void test_adjustDate_preserveDoM() { + YearMonth test = YearMonth.of(2011, 3); + LocalDate date = LocalDate.of(2008, 2, 29); + assertEquals(test.adjustInto(date), LocalDate.of(2011, 3, 29)); + } + + @Test(groups={"tck"}) + public void test_adjustDate_resolve() { + YearMonth test = YearMonth.of(2007, 2); + LocalDate date = LocalDate.of(2008, 3, 31); + assertEquals(test.adjustInto(date), LocalDate.of(2007, 2, 28)); + } + + @Test(groups={"tck"}) + public void test_adjustDate_equal() { + YearMonth test = YearMonth.of(2008, 6); + LocalDate date = LocalDate.of(2008, 6, 30); + assertEquals(test.adjustInto(date), date); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_adjustDate_null() { + TEST_2008_06.adjustInto((LocalDate) null); + } + + //----------------------------------------------------------------------- + // isLeapYear() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_isLeapYear() { + assertEquals(YearMonth.of(2007, 6).isLeapYear(), false); + assertEquals(YearMonth.of(2008, 6).isLeapYear(), true); + } + + //----------------------------------------------------------------------- + // lengthOfMonth() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_lengthOfMonth_june() { + YearMonth test = YearMonth.of(2007, 6); + assertEquals(test.lengthOfMonth(), 30); + } + + @Test(groups={"tck"}) + public void test_lengthOfMonth_febNonLeap() { + YearMonth test = YearMonth.of(2007, 2); + assertEquals(test.lengthOfMonth(), 28); + } + + @Test(groups={"tck"}) + public void test_lengthOfMonth_febLeap() { + YearMonth test = YearMonth.of(2008, 2); + assertEquals(test.lengthOfMonth(), 29); + } + + //----------------------------------------------------------------------- + // lengthOfYear() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_lengthOfYear() { + assertEquals(YearMonth.of(2007, 6).lengthOfYear(), 365); + assertEquals(YearMonth.of(2008, 6).lengthOfYear(), 366); + } + + //----------------------------------------------------------------------- + // isValidDay(int) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_isValidDay_int_june() { + YearMonth test = YearMonth.of(2007, 6); + assertEquals(test.isValidDay(1), true); + assertEquals(test.isValidDay(30), true); + + assertEquals(test.isValidDay(-1), false); + assertEquals(test.isValidDay(0), false); + assertEquals(test.isValidDay(31), false); + assertEquals(test.isValidDay(32), false); + } + + @Test(groups={"tck"}) + public void test_isValidDay_int_febNonLeap() { + YearMonth test = YearMonth.of(2007, 2); + assertEquals(test.isValidDay(1), true); + assertEquals(test.isValidDay(28), true); + + assertEquals(test.isValidDay(-1), false); + assertEquals(test.isValidDay(0), false); + assertEquals(test.isValidDay(29), false); + assertEquals(test.isValidDay(32), false); + } + + @Test(groups={"tck"}) + public void test_isValidDay_int_febLeap() { + YearMonth test = YearMonth.of(2008, 2); + assertEquals(test.isValidDay(1), true); + assertEquals(test.isValidDay(29), true); + + assertEquals(test.isValidDay(-1), false); + assertEquals(test.isValidDay(0), false); + assertEquals(test.isValidDay(30), false); + assertEquals(test.isValidDay(32), false); + } + + //----------------------------------------------------------------------- + // atDay(int) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_atDay_int() { + YearMonth test = YearMonth.of(2008, 6); + assertEquals(test.atDay(30), LocalDate.of(2008, 6, 30)); + } + + @Test(expectedExceptions=DateTimeException.class, groups={"tck"}) + public void test_atDay_int_invalidDay() { + YearMonth test = YearMonth.of(2008, 6); + test.atDay(31); + } + + //----------------------------------------------------------------------- + // compareTo() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_comparisons() { + doTest_comparisons_YearMonth( + YearMonth.of(-1, 1), + YearMonth.of(0, 1), + YearMonth.of(0, 12), + YearMonth.of(1, 1), + YearMonth.of(1, 2), + YearMonth.of(1, 12), + YearMonth.of(2008, 1), + YearMonth.of(2008, 6), + YearMonth.of(2008, 12) + ); + } + + void doTest_comparisons_YearMonth(YearMonth... localDates) { + for (int i = 0; i < localDates.length; i++) { + YearMonth a = localDates[i]; + for (int j = 0; j < localDates.length; j++) { + YearMonth b = localDates[j]; + if (i < j) { + assertTrue(a.compareTo(b) < 0, a + " <=> " + b); + assertEquals(a.isBefore(b), true, a + " <=> " + b); + assertEquals(a.isAfter(b), false, a + " <=> " + b); + assertEquals(a.equals(b), false, a + " <=> " + b); + } else if (i > j) { + assertTrue(a.compareTo(b) > 0, a + " <=> " + b); + assertEquals(a.isBefore(b), false, a + " <=> " + b); + assertEquals(a.isAfter(b), true, a + " <=> " + b); + assertEquals(a.equals(b), false, a + " <=> " + b); + } else { + assertEquals(a.compareTo(b), 0, a + " <=> " + b); + assertEquals(a.isBefore(b), false, a + " <=> " + b); + assertEquals(a.isAfter(b), false, a + " <=> " + b); + assertEquals(a.equals(b), true, a + " <=> " + b); + } + } + } + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_compareTo_ObjectNull() { + TEST_2008_06.compareTo(null); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_isBefore_ObjectNull() { + TEST_2008_06.isBefore(null); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_isAfter_ObjectNull() { + TEST_2008_06.isAfter(null); + } + + //----------------------------------------------------------------------- + // equals() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_equals() { + YearMonth a = YearMonth.of(2008, 6); + YearMonth b = YearMonth.of(2008, 6); + YearMonth c = YearMonth.of(2007, 6); + YearMonth d = YearMonth.of(2008, 5); + + assertEquals(a.equals(a), true); + assertEquals(a.equals(b), true); + assertEquals(a.equals(c), false); + assertEquals(a.equals(d), false); + + assertEquals(b.equals(a), true); + assertEquals(b.equals(b), true); + assertEquals(b.equals(c), false); + assertEquals(b.equals(d), false); + + assertEquals(c.equals(a), false); + assertEquals(c.equals(b), false); + assertEquals(c.equals(c), true); + assertEquals(c.equals(d), false); + + assertEquals(d.equals(a), false); + assertEquals(d.equals(b), false); + assertEquals(d.equals(c), false); + assertEquals(d.equals(d), true); + } + + @Test(groups={"tck"}) + public void test_equals_itself_true() { + assertEquals(TEST_2008_06.equals(TEST_2008_06), true); + } + + @Test(groups={"tck"}) + public void test_equals_string_false() { + assertEquals(TEST_2008_06.equals("2007-07-15"), false); + } + + @Test(groups={"tck"}) + public void test_equals_null_false() { + assertEquals(TEST_2008_06.equals(null), false); + } + + //----------------------------------------------------------------------- + // hashCode() + //----------------------------------------------------------------------- + @Test(dataProvider="sampleDates", groups={"tck"}) + public void test_hashCode(int y, int m) { + YearMonth a = YearMonth.of(y, m); + assertEquals(a.hashCode(), a.hashCode()); + YearMonth b = YearMonth.of(y, m); + assertEquals(a.hashCode(), b.hashCode()); + } + + @Test(groups={"tck"}) + public void test_hashCode_unique() { + Set uniques = new HashSet(201 * 12); + for (int i = 1900; i <= 2100; i++) { + for (int j = 1; j <= 12; j++) { + assertTrue(uniques.add(YearMonth.of(i, j).hashCode())); + } + } + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @DataProvider(name="sampleToString") + Object[][] provider_sampleToString() { + return new Object[][] { + {2008, 1, "2008-01"}, + {2008, 12, "2008-12"}, + {7, 5, "0007-05"}, + {0, 5, "0000-05"}, + {-1, 1, "-0001-01"}, + }; + } + + @Test(dataProvider="sampleToString", groups={"tck"}) + public void test_toString(int y, int m, String expected) { + YearMonth test = YearMonth.of(y, m); + String str = test.toString(); + assertEquals(str, expected); + } + + //----------------------------------------------------------------------- + // toString(DateTimeFormatter) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toString_formatter() { + DateTimeFormatter f = DateTimeFormatters.pattern("y M"); + String t = YearMonth.of(2010, 12).toString(f); + assertEquals(t, "2010 12"); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_toString_formatter_null() { + YearMonth.of(2010, 12).toString(null); + } + +} diff --git a/jdk/test/java/time/tck/java/time/temporal/TestChrono.java b/jdk/test/java/time/tck/java/time/temporal/TestChrono.java new file mode 100644 index 00000000000..cd30519dd04 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/temporal/TestChrono.java @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.temporal; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Locale; +import java.util.Set; + +import java.time.temporal.Chrono; +import java.time.temporal.ChronoField; +import java.time.calendar.HijrahChrono; +import java.time.calendar.JapaneseChrono; +import java.time.calendar.MinguoChrono; +import java.time.calendar.ThaiBuddhistChrono; +import java.time.temporal.ChronoLocalDate; +import java.time.temporal.ISOChrono; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test Chrono class. + */ +@Test +public class TestChrono { + + @BeforeMethod(groups="tck") + public void setUp() { + // Ensure each of the classes are initialized (until initialization is fixed) + Chrono c; + c = HijrahChrono.INSTANCE; + c = ISOChrono.INSTANCE; + c = JapaneseChrono.INSTANCE; + c = MinguoChrono.INSTANCE; + c = ThaiBuddhistChrono.INSTANCE; + c.toString(); // avoids variable being marked as unused + } + + //----------------------------------------------------------------------- + // regular data factory for names and descriptions of available calendars + //----------------------------------------------------------------------- + @DataProvider(name = "calendars") + Object[][] data_of_calendars() { + return new Object[][] { + {"Hijrah", "islamicc", "Hijrah calendar"}, + {"ISO", "iso8601", "ISO calendar"}, + {"Japanese", "japanese", "Japanese calendar"}, + {"Minguo", "roc", "Minguo Calendar"}, + {"ThaiBuddhist", "buddhist", "ThaiBuddhist calendar"}, + }; + } + + @Test(dataProvider = "calendars") + public void test_getters(String chronoId, String calendarSystemType, String description) { + Chrono chrono = Chrono.of(chronoId); + assertNotNull(chrono, "Required calendar not found by ID: " + chronoId); + assertEquals(chrono.getId(), chronoId); + assertEquals(chrono.getCalendarType(), calendarSystemType); + } + + @Test(dataProvider = "calendars") + public void test_required_calendars(String chronoId, String calendarSystemType, String description) { + Chrono chrono = Chrono.of(chronoId); + assertNotNull(chrono, "Required calendar not found by ID: " + chronoId); + chrono = Chrono.of(calendarSystemType); + assertNotNull(chrono, "Required calendar not found by type: " + chronoId); + Set> cals = Chrono.getAvailableChronologies(); + assertTrue(cals.contains(chrono), "Required calendar not found in set of available calendars"); + } + + @Test(groups="tck") + public void test_calendar_list() { + Set> chronos = Chrono.getAvailableChronologies(); + assertNotNull(chronos, "Required list of calendars must be non-null"); + for (Chrono chrono : chronos) { + Chrono lookup = Chrono.of(chrono.getId()); + assertNotNull(lookup, "Required calendar not found: " + chrono); + } + assertEquals(chronos.size() >= data_of_calendars().length, true, "Chrono.getAvailableChronologies().size = " + chronos.size() + + ", expected >= " + data_of_calendars().length); + } + + /** + * Compute the number of days from the Epoch and compute the date from the number of days. + */ + @Test(dataProvider = "calendars", groups="tck") + public void test_epoch(String name, String alias, String description) { + Chrono chrono = Chrono.of(name); // a chronology. In practice this is rarely hardcoded + ChronoLocalDate date1 = chrono.dateNow(); + long epoch1 = date1.getLong(ChronoField.EPOCH_DAY); + ChronoLocalDate date2 = date1.with(ChronoField.EPOCH_DAY, epoch1); + assertEquals(date1, date2, "Date from epoch day is not same date: " + date1 + " != " + date2); + long epoch2 = date1.getLong(ChronoField.EPOCH_DAY); + assertEquals(epoch1, epoch2, "Epoch day not the same: " + epoch1 + " != " + epoch2); + } + + //----------------------------------------------------------------------- + // locale based lookup + //----------------------------------------------------------------------- + @DataProvider(name = "calendarsystemtype") + Object[][] data_CalendarType() { + return new Object[][] { + {HijrahChrono.INSTANCE, "islamicc"}, + {ISOChrono.INSTANCE, "iso8601"}, + {JapaneseChrono.INSTANCE, "japanese"}, + {MinguoChrono.INSTANCE, "roc"}, + {ThaiBuddhistChrono.INSTANCE, "buddhist"}, + }; + } + + @Test(dataProvider = "calendarsystemtype", groups="tck") + public void test_getCalendarType(Chrono chrono, String calendarType) { + assertEquals(chrono.getCalendarType(), calendarType); + } + + @Test(dataProvider = "calendarsystemtype", groups="tck") + public void test_lookupLocale(Chrono chrono, String calendarType) { + Locale locale = new Locale.Builder().setLanguage("en").setRegion("CA").setUnicodeLocaleKeyword("ca", calendarType).build(); + assertEquals(Chrono.ofLocale(locale), chrono); + } + + + //----------------------------------------------------------------------- + // serialization; serialize and check each calendar system + //----------------------------------------------------------------------- + @Test(groups={"implementation"}, dataProvider = "calendarsystemtype") + public > void test_chronoSerializationSingleton(C chrono, String calendarType) throws Exception { + C orginal = chrono; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(baos); + out.writeObject(orginal); + out.close(); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream in = new ObjectInputStream(bais); + @SuppressWarnings("unchecked") + C ser = (C) in.readObject(); + assertSame(ser, chrono, "Deserialized Chrono is not the singleton serialized"); + } + +} diff --git a/jdk/test/java/time/tck/java/time/temporal/TestChronoLocalDate.java b/jdk/test/java/time/tck/java/time/temporal/TestChronoLocalDate.java new file mode 100644 index 00000000000..95caf573a68 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/temporal/TestChronoLocalDate.java @@ -0,0 +1,503 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.temporal; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import java.time.Duration; +import java.time.LocalDate; +import java.time.temporal.Chrono; +import java.time.temporal.ChronoLocalDate; +import java.time.temporal.ChronoUnit; +import java.time.temporal.SimplePeriod; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAccessor; +import java.time.format.DateTimeBuilder; +import java.time.temporal.TemporalAdder; +import java.time.temporal.TemporalAdjuster; +import java.time.temporal.TemporalField; +import java.time.temporal.TemporalSubtractor; +import java.time.temporal.ValueRange; +import java.time.temporal.TemporalUnit; +import java.time.temporal.ISOChrono; + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test assertions that must be true for the built-in ISO chronology. + */ +@Test +public class TestChronoLocalDate { + + //----------------------------------------------------------------------- + // regular data factory for names and descriptions of ISO calendar + //----------------------------------------------------------------------- + @DataProvider(name = "calendars") + Chrono[][] data_of_calendars() { + return new Chrono[][]{ + {ISOChrono.INSTANCE}, + }; + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badWithAdjusterChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoLocalDate date = chrono.date(refDate); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoLocalDate date2 = chrono2.date(refDate); + TemporalAdjuster adjuster = new FixedAdjuster(date2); + if (chrono != chrono2) { + try { + date.with(adjuster); + Assert.fail("WithAdjuster should have thrown a ClassCastException"); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoLocalDate result = date.with(adjuster); + assertEquals(result, date2, "WithAdjuster failed to replace date"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badPlusAdjusterChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoLocalDate date = chrono.date(refDate); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoLocalDate date2 = chrono2.date(refDate); + TemporalAdder adjuster = new FixedAdjuster(date2); + if (chrono != chrono2) { + try { + date.plus(adjuster); + Assert.fail("WithAdjuster should have thrown a ClassCastException"); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoLocalDate result = date.plus(adjuster); + assertEquals(result, date2, "WithAdjuster failed to replace date"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badMinusAdjusterChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoLocalDate date = chrono.date(refDate); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoLocalDate date2 = chrono2.date(refDate); + TemporalSubtractor adjuster = new FixedAdjuster(date2); + if (chrono != chrono2) { + try { + date.minus(adjuster); + Assert.fail("WithAdjuster should have thrown a ClassCastException"); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoLocalDate result = date.minus(adjuster); + assertEquals(result, date2, "WithAdjuster failed to replace date"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badPlusTemporalUnitChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoLocalDate date = chrono.date(refDate); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoLocalDate date2 = chrono2.date(refDate); + TemporalUnit adjuster = new FixedTemporalUnit(date2); + if (chrono != chrono2) { + try { + date.plus(1, adjuster); + Assert.fail("TemporalUnit.doPlus plus should have thrown a ClassCastException" + date.getClass() + + ", can not be cast to " + date2.getClass()); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoLocalDate result = date.plus(1, adjuster); + assertEquals(result, date2, "WithAdjuster failed to replace date"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badMinusTemporalUnitChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoLocalDate date = chrono.date(refDate); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoLocalDate date2 = chrono2.date(refDate); + TemporalUnit adjuster = new FixedTemporalUnit(date2); + if (chrono != chrono2) { + try { + date.minus(1, adjuster); + Assert.fail("TemporalUnit.doPlus minus should have thrown a ClassCastException" + date.getClass() + + ", can not be cast to " + date2.getClass()); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoLocalDate result = date.minus(1, adjuster); + assertEquals(result, date2, "WithAdjuster failed to replace date"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badTemporalFieldChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoLocalDate date = chrono.date(refDate); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoLocalDate date2 = chrono2.date(refDate); + TemporalField adjuster = new FixedTemporalField(date2); + if (chrono != chrono2) { + try { + date.with(adjuster, 1); + Assert.fail("TemporalField doWith() should have thrown a ClassCastException" + date.getClass() + + ", can not be cast to " + date2.getClass()); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoLocalDate result = date.with(adjuster, 1); + assertEquals(result, date2, "TemporalField doWith() failed to replace date"); + } + } + } + + //----------------------------------------------------------------------- + // isBefore, isAfter, isEqual, DATE_COMPARATOR + //----------------------------------------------------------------------- + @Test(groups={"tck"}, dataProvider="calendars") + public void test_date_comparisons(Chrono chrono) { + List> dates = new ArrayList<>(); + + ChronoLocalDate date = chrono.date(LocalDate.of(1900, 1, 1)); + + // Insert dates in order, no duplicates + dates.add(date.minus(1000, ChronoUnit.YEARS)); + dates.add(date.minus(100, ChronoUnit.YEARS)); + dates.add(date.minus(10, ChronoUnit.YEARS)); + dates.add(date.minus(1, ChronoUnit.YEARS)); + dates.add(date.minus(1, ChronoUnit.MONTHS)); + dates.add(date.minus(1, ChronoUnit.WEEKS)); + dates.add(date.minus(1, ChronoUnit.DAYS)); + dates.add(date); + dates.add(date.plus(1, ChronoUnit.DAYS)); + dates.add(date.plus(1, ChronoUnit.WEEKS)); + dates.add(date.plus(1, ChronoUnit.MONTHS)); + dates.add(date.plus(1, ChronoUnit.YEARS)); + dates.add(date.plus(10, ChronoUnit.YEARS)); + dates.add(date.plus(100, ChronoUnit.YEARS)); + dates.add(date.plus(1000, ChronoUnit.YEARS)); + + // Check these dates against the corresponding dates for every calendar + for (Chrono[] clist : data_of_calendars()) { + List> otherDates = new ArrayList<>(); + Chrono chrono2 = clist[0]; + for (ChronoLocalDate d : dates) { + otherDates.add(chrono2.date(d)); + } + + // Now compare the sequence of original dates with the sequence of converted dates + for (int i = 0; i < dates.size(); i++) { + ChronoLocalDate a = dates.get(i); + for (int j = 0; j < otherDates.size(); j++) { + ChronoLocalDate b = otherDates.get(j); + int cmp = ChronoLocalDate.DATE_COMPARATOR.compare(a, b); + if (i < j) { + assertTrue(cmp < 0, a + " compare " + b); + assertEquals(a.isBefore(b), true, a + " isBefore " + b); + assertEquals(a.isAfter(b), false, a + " isAfter " + b); + assertEquals(a.isEqual(b), false, a + " isEqual " + b); + } else if (i > j) { + assertTrue(cmp > 0, a + " compare " + b); + assertEquals(a.isBefore(b), false, a + " isBefore " + b); + assertEquals(a.isAfter(b), true, a + " isAfter " + b); + assertEquals(a.isEqual(b), false, a + " isEqual " + b); + } else { + assertTrue(cmp == 0, a + " compare " + b); + assertEquals(a.isBefore(b), false, a + " isBefore " + b); + assertEquals(a.isAfter(b), false, a + " isAfter " + b); + assertEquals(a.isEqual(b), true, a + " isEqual " + b); + } + } + } + } + } + + public void test_date_comparator_checkGenerics_ISO() { + List> dates = new ArrayList<>(); + ChronoLocalDate date = LocalDate.of(1900, 1, 1); + + // Insert dates in order, no duplicates + dates.add(date.minus(10, ChronoUnit.YEARS)); + dates.add(date.minus(1, ChronoUnit.YEARS)); + dates.add(date.minus(1, ChronoUnit.MONTHS)); + dates.add(date.minus(1, ChronoUnit.WEEKS)); + dates.add(date.minus(1, ChronoUnit.DAYS)); + dates.add(date); + dates.add(date.plus(1, ChronoUnit.DAYS)); + dates.add(date.plus(1, ChronoUnit.WEEKS)); + dates.add(date.plus(1, ChronoUnit.MONTHS)); + dates.add(date.plus(1, ChronoUnit.YEARS)); + dates.add(date.plus(10, ChronoUnit.YEARS)); + + List> copy = new ArrayList<>(dates); + Collections.shuffle(copy); + Collections.sort(copy, ChronoLocalDate.DATE_COMPARATOR); + assertEquals(copy, dates); + assertTrue(ChronoLocalDate.DATE_COMPARATOR.compare(copy.get(0), copy.get(1)) < 0); + } + + public void test_date_comparator_checkGenerics_LocalDate() { + List dates = new ArrayList<>(); + LocalDate date = LocalDate.of(1900, 1, 1); + + // Insert dates in order, no duplicates + dates.add(date.minus(10, ChronoUnit.YEARS)); + dates.add(date.minus(1, ChronoUnit.YEARS)); + dates.add(date.minus(1, ChronoUnit.MONTHS)); + dates.add(date.minus(1, ChronoUnit.WEEKS)); + dates.add(date.minus(1, ChronoUnit.DAYS)); + dates.add(date); + dates.add(date.plus(1, ChronoUnit.DAYS)); + dates.add(date.plus(1, ChronoUnit.WEEKS)); + dates.add(date.plus(1, ChronoUnit.MONTHS)); + dates.add(date.plus(1, ChronoUnit.YEARS)); + dates.add(date.plus(10, ChronoUnit.YEARS)); + + List copy = new ArrayList<>(dates); + Collections.shuffle(copy); + Collections.sort(copy, ChronoLocalDate.DATE_COMPARATOR); + assertEquals(copy, dates); + assertTrue(ChronoLocalDate.DATE_COMPARATOR.compare(copy.get(0), copy.get(1)) < 0); + } + + //----------------------------------------------------------------------- + // Test Serialization of ISO via chrono API + //----------------------------------------------------------------------- + @Test( groups={"tck"}, dataProvider="calendars") + public > void test_ChronoSerialization(C chrono) throws Exception { + LocalDate ref = LocalDate.of(2000, 1, 5); + ChronoLocalDate orginal = chrono.date(ref); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(baos); + out.writeObject(orginal); + out.close(); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream in = new ObjectInputStream(bais); + @SuppressWarnings("unchecked") + ChronoLocalDate ser = (ChronoLocalDate) in.readObject(); + assertEquals(ser, orginal, "deserialized date is wrong"); + } + + /** + * FixedAdjusted returns a fixed Temporal in all adjustments. + * Construct an adjuster with the Temporal that should be returned from adjust. + */ + static class FixedAdjuster implements TemporalAdjuster, TemporalAdder, TemporalSubtractor { + private Temporal datetime; + + FixedAdjuster(Temporal datetime) { + this.datetime = datetime; + } + + @Override + public Temporal adjustInto(Temporal ignore) { + return datetime; + } + + @Override + public Temporal addTo(Temporal ignore) { + return datetime; + } + + @Override + public Temporal subtractFrom(Temporal ignore) { + return datetime; + } + + } + + /** + * FixedTemporalUnit returns a fixed Temporal in all adjustments. + * Construct an FixedTemporalUnit with the Temporal that should be returned from doPlus. + */ + static class FixedTemporalUnit implements TemporalUnit { + private Temporal temporal; + + FixedTemporalUnit(Temporal temporal) { + this.temporal = temporal; + } + + @Override + public String getName() { + return "FixedTemporalUnit"; + } + + @Override + public Duration getDuration() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean isDurationEstimated() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean isSupported(Temporal temporal) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @SuppressWarnings("unchecked") + @Override + public R doPlus(R dateTime, long periodToAdd) { + return (R) this.temporal; + } + + @Override + public SimplePeriod between(R dateTime1, R dateTime2) { + throw new UnsupportedOperationException("Not supported yet."); + } + } + + /** + * FixedTemporalField returns a fixed Temporal in all adjustments. + * Construct an FixedTemporalField with the Temporal that should be returned from doWith. + */ + static class FixedTemporalField implements TemporalField { + private Temporal temporal; + FixedTemporalField(Temporal temporal) { + this.temporal = temporal; + } + + @Override + public String getName() { + return "FixedTemporalField"; + } + + @Override + public TemporalUnit getBaseUnit() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public TemporalUnit getRangeUnit() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public ValueRange range() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean doIsSupported(TemporalAccessor temporal) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public ValueRange doRange(TemporalAccessor temporal) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public long doGet(TemporalAccessor temporal) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @SuppressWarnings("unchecked") + @Override + public R doWith(R temporal, long newValue) { + return (R) this.temporal; + } + + @Override + public boolean resolve(DateTimeBuilder builder, long value) { + throw new UnsupportedOperationException("Not supported yet."); + } + + } +} diff --git a/jdk/test/java/time/tck/java/time/temporal/TestChronoLocalDateTime.java b/jdk/test/java/time/tck/java/time/temporal/TestChronoLocalDateTime.java new file mode 100644 index 00000000000..c51be6c69b8 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/temporal/TestChronoLocalDateTime.java @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.temporal; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.List; + +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.temporal.Chrono; +import java.time.temporal.ChronoLocalDateTime; +import java.time.temporal.ChronoUnit; +import java.time.temporal.SimplePeriod; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAccessor; +import java.time.format.DateTimeBuilder; +import java.time.temporal.TemporalAdder; +import java.time.temporal.TemporalAdjuster; +import java.time.temporal.TemporalField; +import java.time.temporal.TemporalSubtractor; +import java.time.temporal.ValueRange; +import java.time.temporal.TemporalUnit; +import java.time.temporal.ISOChrono; +import java.time.calendar.HijrahChrono; +import java.time.calendar.JapaneseChrono; +import java.time.calendar.MinguoChrono; +import java.time.calendar.ThaiBuddhistChrono; + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test assertions that must be true for all built-in chronologies. + */ +@Test +public class TestChronoLocalDateTime { + + //----------------------------------------------------------------------- + // regular data factory for names and descriptions of available calendars + //----------------------------------------------------------------------- + @DataProvider(name = "calendars") + Chrono[][] data_of_calendars() { + return new Chrono[][]{ + {HijrahChrono.INSTANCE}, + {ISOChrono.INSTANCE}, + {JapaneseChrono.INSTANCE}, + {MinguoChrono.INSTANCE}, + {ThaiBuddhistChrono.INSTANCE}}; + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badWithAdjusterChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoLocalDateTime cdt = chrono.date(refDate).atTime(LocalTime.NOON); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoLocalDateTime cdt2 = chrono2.date(refDate).atTime(LocalTime.NOON); + TemporalAdjuster adjuster = new FixedAdjuster(cdt2); + if (chrono != chrono2) { + try { + cdt.with(adjuster); + Assert.fail("WithAdjuster should have thrown a ClassCastException, " + + "required: " + cdt + ", supplied: " + cdt2); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoLocalDateTime result = cdt.with(adjuster); + assertEquals(result, cdt2, "WithAdjuster failed to replace date"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badPlusAdjusterChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoLocalDateTime cdt = chrono.date(refDate).atTime(LocalTime.NOON); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoLocalDateTime cdt2 = chrono2.date(refDate).atTime(LocalTime.NOON); + TemporalAdder adjuster = new FixedAdjuster(cdt2); + if (chrono != chrono2) { + try { + cdt.plus(adjuster); + Assert.fail("WithAdjuster should have thrown a ClassCastException, " + + "required: " + cdt + ", supplied: " + cdt2); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoLocalDateTime result = cdt.plus(adjuster); + assertEquals(result, cdt2, "WithAdjuster failed to replace date time"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badMinusAdjusterChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoLocalDateTime cdt = chrono.date(refDate).atTime(LocalTime.NOON); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoLocalDateTime cdt2 = chrono2.date(refDate).atTime(LocalTime.NOON); + TemporalSubtractor adjuster = new FixedAdjuster(cdt2); + if (chrono != chrono2) { + try { + cdt.minus(adjuster); + Assert.fail("WithAdjuster should have thrown a ClassCastException, " + + "required: " + cdt + ", supplied: " + cdt2); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoLocalDateTime result = cdt.minus(adjuster); + assertEquals(result, cdt2, "WithAdjuster failed to replace date"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badPlusTemporalUnitChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoLocalDateTime cdt = chrono.date(refDate).atTime(LocalTime.NOON); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoLocalDateTime cdt2 = chrono2.date(refDate).atTime(LocalTime.NOON); + TemporalUnit adjuster = new FixedTemporalUnit(cdt2); + if (chrono != chrono2) { + try { + cdt.plus(1, adjuster); + Assert.fail("TemporalUnit.doPlus plus should have thrown a ClassCastException" + cdt + + ", can not be cast to " + cdt2); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoLocalDateTime result = cdt.plus(1, adjuster); + assertEquals(result, cdt2, "WithAdjuster failed to replace date"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badMinusTemporalUnitChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoLocalDateTime cdt = chrono.date(refDate).atTime(LocalTime.NOON); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoLocalDateTime cdt2 = chrono2.date(refDate).atTime(LocalTime.NOON); + TemporalUnit adjuster = new FixedTemporalUnit(cdt2); + if (chrono != chrono2) { + try { + cdt.minus(1, adjuster); + Assert.fail("TemporalUnit.doPlus minus should have thrown a ClassCastException" + cdt.getClass() + + ", can not be cast to " + cdt2.getClass()); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoLocalDateTime result = cdt.minus(1, adjuster); + assertEquals(result, cdt2, "WithAdjuster failed to replace date"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badTemporalFieldChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoLocalDateTime cdt = chrono.date(refDate).atTime(LocalTime.NOON); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoLocalDateTime cdt2 = chrono2.date(refDate).atTime(LocalTime.NOON); + TemporalField adjuster = new FixedTemporalField(cdt2); + if (chrono != chrono2) { + try { + cdt.with(adjuster, 1); + Assert.fail("TemporalField doWith() should have thrown a ClassCastException" + cdt.getClass() + + ", can not be cast to " + cdt2.getClass()); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoLocalDateTime result = cdt.with(adjuster, 1); + assertEquals(result, cdt2, "TemporalField doWith() failed to replace date"); + } + } + } + + //----------------------------------------------------------------------- + // isBefore, isAfter, isEqual + //----------------------------------------------------------------------- + @Test(groups={"tck"}, dataProvider="calendars") + public void test_datetime_comparisons(Chrono chrono) { + List> dates = new ArrayList<>(); + + ChronoLocalDateTime date = chrono.date(LocalDate.of(1900, 1, 1)).atTime(LocalTime.MIN); + + // Insert dates in order, no duplicates + dates.add(date.minus(100, ChronoUnit.YEARS)); + dates.add(date.minus(1, ChronoUnit.YEARS)); + dates.add(date.minus(1, ChronoUnit.MONTHS)); + dates.add(date.minus(1, ChronoUnit.WEEKS)); + dates.add(date.minus(1, ChronoUnit.DAYS)); + dates.add(date.minus(1, ChronoUnit.HOURS)); + dates.add(date.minus(1, ChronoUnit.MINUTES)); + dates.add(date.minus(1, ChronoUnit.SECONDS)); + dates.add(date.minus(1, ChronoUnit.NANOS)); + dates.add(date); + dates.add(date.plus(1, ChronoUnit.NANOS)); + dates.add(date.plus(1, ChronoUnit.SECONDS)); + dates.add(date.plus(1, ChronoUnit.MINUTES)); + dates.add(date.plus(1, ChronoUnit.HOURS)); + dates.add(date.plus(1, ChronoUnit.DAYS)); + dates.add(date.plus(1, ChronoUnit.WEEKS)); + dates.add(date.plus(1, ChronoUnit.MONTHS)); + dates.add(date.plus(1, ChronoUnit.YEARS)); + dates.add(date.plus(100, ChronoUnit.YEARS)); + + // Check these dates against the corresponding dates for every calendar + for (Chrono[] clist : data_of_calendars()) { + List> otherDates = new ArrayList<>(); + Chrono chrono2 = clist[0]; + for (ChronoLocalDateTime d : dates) { + otherDates.add(chrono2.date(d).atTime(d.getTime())); + } + + // Now compare the sequence of original dates with the sequence of converted dates + for (int i = 0; i < dates.size(); i++) { + ChronoLocalDateTime a = dates.get(i); + for (int j = 0; j < otherDates.size(); j++) { + ChronoLocalDateTime b = otherDates.get(j); + int cmp = ChronoLocalDateTime.DATE_TIME_COMPARATOR.compare(a, b); + if (i < j) { + assertTrue(cmp < 0, a + " compare " + b); + assertEquals(a.isBefore(b), true, a + " isBefore " + b); + assertEquals(a.isAfter(b), false, a + " isAfter " + b); + assertEquals(a.isEqual(b), false, a + " isEqual " + b); + } else if (i > j) { + assertTrue(cmp > 0, a + " compare " + b); + assertEquals(a.isBefore(b), false, a + " isBefore " + b); + assertEquals(a.isAfter(b), true, a + " isAfter " + b); + assertEquals(a.isEqual(b), false, a + " isEqual " + b); + } else { + assertTrue(cmp == 0, a + " compare " + b); + assertEquals(a.isBefore(b), false, a + " isBefore " + b); + assertEquals(a.isAfter(b), false, a + " isAfter " + b); + assertEquals(a.isEqual(b), true, a + " isEqual " + b); + } + } + } + } + } + + //----------------------------------------------------------------------- + // Test Serialization of ISO via chrono API + //----------------------------------------------------------------------- + @Test( groups={"tck"}, dataProvider="calendars") + public > void test_ChronoLocalDateTimeSerialization(C chrono) throws Exception { + LocalDateTime ref = LocalDate.of(2000, 1, 5).atTime(12, 1, 2, 3); + ChronoLocalDateTime orginal = chrono.date(ref).atTime(ref.getTime()); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(baos); + out.writeObject(orginal); + out.close(); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream in = new ObjectInputStream(bais); + @SuppressWarnings("unchecked") + ChronoLocalDateTime ser = (ChronoLocalDateTime) in.readObject(); + assertEquals(ser, orginal, "deserialized date is wrong"); + } + + /** + * FixedAdjusted returns a fixed Temporal in all adjustments. + * Construct an adjuster with the Temporal that should be returned from adjust. + */ + static class FixedAdjuster implements TemporalAdjuster, TemporalAdder, TemporalSubtractor { + private Temporal datetime; + + FixedAdjuster(Temporal datetime) { + this.datetime = datetime; + } + + @Override + public Temporal adjustInto(Temporal ignore) { + return datetime; + } + + @Override + public Temporal addTo(Temporal ignore) { + return datetime; + } + + @Override + public Temporal subtractFrom(Temporal ignore) { + return datetime; + } + + } + + /** + * FixedTemporalUnit returns a fixed Temporal in all adjustments. + * Construct an FixedTemporalUnit with the Temporal that should be returned from doPlus. + */ + static class FixedTemporalUnit implements TemporalUnit { + private Temporal temporal; + + FixedTemporalUnit(Temporal temporal) { + this.temporal = temporal; + } + + @Override + public String getName() { + return "FixedTemporalUnit"; + } + + @Override + public Duration getDuration() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean isDurationEstimated() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean isSupported(Temporal temporal) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @SuppressWarnings("unchecked") + @Override + public R doPlus(R dateTime, long periodToAdd) { + return (R) this.temporal; + } + + @Override + public SimplePeriod between(R dateTime1, R dateTime2) { + throw new UnsupportedOperationException("Not supported yet."); + } + } + + /** + * FixedTemporalField returns a fixed Temporal in all adjustments. + * Construct an FixedTemporalField with the Temporal that should be returned from doWith. + */ + static class FixedTemporalField implements TemporalField { + private Temporal temporal; + FixedTemporalField(Temporal temporal) { + this.temporal = temporal; + } + + @Override + public String getName() { + return "FixedTemporalField"; + } + + @Override + public TemporalUnit getBaseUnit() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public TemporalUnit getRangeUnit() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public ValueRange range() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean doIsSupported(TemporalAccessor temporal) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public ValueRange doRange(TemporalAccessor temporal) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public long doGet(TemporalAccessor temporal) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @SuppressWarnings("unchecked") + @Override + public R doWith(R temporal, long newValue) { + return (R) this.temporal; + } + + @Override + public boolean resolve(DateTimeBuilder builder, long value) { + throw new UnsupportedOperationException("Not supported yet."); + } + + } +} diff --git a/jdk/test/java/time/tck/java/time/temporal/TestChronoZonedDateTime.java b/jdk/test/java/time/tck/java/time/temporal/TestChronoZonedDateTime.java new file mode 100644 index 00000000000..e89725bb3d3 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/temporal/TestChronoZonedDateTime.java @@ -0,0 +1,475 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.temporal; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.List; + +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.temporal.Chrono; +import java.time.temporal.ChronoUnit; +import java.time.temporal.ChronoZonedDateTime; +import java.time.temporal.SimplePeriod; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAccessor; +import java.time.format.DateTimeBuilder; +import java.time.temporal.TemporalAdder; +import java.time.temporal.TemporalAdjuster; +import java.time.temporal.TemporalField; +import java.time.temporal.TemporalSubtractor; +import java.time.temporal.ValueRange; +import java.time.temporal.ISOChrono; +import java.time.temporal.TemporalUnit; +import java.time.calendar.HijrahChrono; +import java.time.calendar.JapaneseChrono; +import java.time.calendar.MinguoChrono; +import java.time.calendar.ThaiBuddhistChrono; + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test assertions that must be true for all built-in chronologies. + */ +@Test +public class TestChronoZonedDateTime { + + //----------------------------------------------------------------------- + // regular data factory for names and descriptions of available calendars + //----------------------------------------------------------------------- + @DataProvider(name = "calendars") + Chrono[][] data_of_calendars() { + return new Chrono[][]{ + {HijrahChrono.INSTANCE}, + {ISOChrono.INSTANCE}, + {JapaneseChrono.INSTANCE}, + {MinguoChrono.INSTANCE}, + {ThaiBuddhistChrono.INSTANCE}, + }; + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badWithAdjusterChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoZonedDateTime czdt = chrono.date(refDate).atTime(LocalTime.NOON).atZone(ZoneOffset.UTC); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoZonedDateTime czdt2 = chrono2.date(refDate).atTime(LocalTime.NOON).atZone(ZoneOffset.UTC); + TemporalAdjuster adjuster = new FixedAdjuster(czdt2); + if (chrono != chrono2) { + try { + czdt.with(adjuster); + Assert.fail("WithAdjuster should have thrown a ClassCastException, " + + "required: " + czdt + ", supplied: " + czdt2); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + ChronoZonedDateTime result = czdt.with(adjuster); + assertEquals(result, czdt2, "WithAdjuster failed to replace date"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badPlusAdjusterChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoZonedDateTime czdt = chrono.date(refDate).atTime(LocalTime.NOON).atZone(ZoneOffset.UTC); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoZonedDateTime czdt2 = chrono2.date(refDate).atTime(LocalTime.NOON).atZone(ZoneOffset.UTC); + TemporalAdder adjuster = new FixedAdjuster(czdt2); + if (chrono != chrono2) { + try { + czdt.plus(adjuster); + Assert.fail("WithAdjuster should have thrown a ClassCastException, " + + "required: " + czdt + ", supplied: " + czdt2); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoZonedDateTime result = czdt.plus(adjuster); + assertEquals(result, czdt2, "WithAdjuster failed to replace date time"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badMinusAdjusterChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoZonedDateTime czdt = chrono.date(refDate).atTime(LocalTime.NOON).atZone(ZoneOffset.UTC); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoZonedDateTime czdt2 = chrono2.date(refDate).atTime(LocalTime.NOON).atZone(ZoneOffset.UTC); + TemporalSubtractor adjuster = new FixedAdjuster(czdt2); + if (chrono != chrono2) { + try { + czdt.minus(adjuster); + Assert.fail("WithAdjuster should have thrown a ClassCastException, " + + "required: " + czdt + ", supplied: " + czdt2); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoZonedDateTime result = czdt.minus(adjuster); + assertEquals(result, czdt2, "WithAdjuster failed to replace date"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badPlusTemporalUnitChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoZonedDateTime czdt = chrono.date(refDate).atTime(LocalTime.NOON).atZone(ZoneOffset.UTC); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoZonedDateTime czdt2 = chrono2.date(refDate).atTime(LocalTime.NOON).atZone(ZoneOffset.UTC); + TemporalUnit adjuster = new FixedTemporalUnit(czdt2); + if (chrono != chrono2) { + try { + czdt.plus(1, adjuster); + Assert.fail("TemporalUnit.doPlus plus should have thrown a ClassCastException, " + czdt + + " can not be cast to " + czdt2); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoZonedDateTime result = czdt.plus(1, adjuster); + assertEquals(result, czdt2, "WithAdjuster failed to replace date"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badMinusTemporalUnitChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoZonedDateTime czdt = chrono.date(refDate).atTime(LocalTime.NOON).atZone(ZoneOffset.UTC); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoZonedDateTime czdt2 = chrono2.date(refDate).atTime(LocalTime.NOON).atZone(ZoneOffset.UTC); + TemporalUnit adjuster = new FixedTemporalUnit(czdt2); + if (chrono != chrono2) { + try { + czdt.minus(1, adjuster); + Assert.fail("TemporalUnit.doPlus minus should have thrown a ClassCastException, " + czdt.getClass() + + " can not be cast to " + czdt2.getClass()); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoZonedDateTime result = czdt.minus(1, adjuster); + assertEquals(result, czdt2, "WithAdjuster failed to replace date"); + } + } + } + + @Test(groups={"tck"}, dataProvider="calendars") + public void test_badTemporalFieldChrono(Chrono chrono) { + LocalDate refDate = LocalDate.of(1900, 1, 1); + ChronoZonedDateTime czdt = chrono.date(refDate).atTime(LocalTime.NOON).atZone(ZoneOffset.UTC); + for (Chrono[] clist : data_of_calendars()) { + Chrono chrono2 = clist[0]; + ChronoZonedDateTime czdt2 = chrono2.date(refDate).atTime(LocalTime.NOON).atZone(ZoneOffset.UTC); + TemporalField adjuster = new FixedTemporalField(czdt2); + if (chrono != chrono2) { + try { + czdt.with(adjuster, 1); + Assert.fail("TemporalField doWith() should have thrown a ClassCastException, " + czdt.getClass() + + " can not be cast to " + czdt2.getClass()); + } catch (ClassCastException cce) { + // Expected exception; not an error + } + } else { + // Same chronology, + ChronoZonedDateTime result = czdt.with(adjuster, 1); + assertEquals(result, czdt2, "TemporalField doWith() failed to replace date"); + } + } + } + + //----------------------------------------------------------------------- + // isBefore, isAfter, isEqual, INSTANT_COMPARATOR test a Chrono against the other Chronos + //----------------------------------------------------------------------- + @Test(groups={"tck"}, dataProvider="calendars") + public void test_zonedDateTime_comparisons(Chrono chrono) { + List> dates = new ArrayList<>(); + + ChronoZonedDateTime date = chrono.date(LocalDate.of(1900, 1, 1)) + .atTime(LocalTime.MIN) + .atZone(ZoneOffset.UTC); + + // Insert dates in order, no duplicates + dates.add(date.minus(100, ChronoUnit.YEARS)); + dates.add(date.minus(1, ChronoUnit.YEARS)); + dates.add(date.minus(1, ChronoUnit.MONTHS)); + dates.add(date.minus(1, ChronoUnit.WEEKS)); + dates.add(date.minus(1, ChronoUnit.DAYS)); + dates.add(date.minus(1, ChronoUnit.HOURS)); + dates.add(date.minus(1, ChronoUnit.MINUTES)); + dates.add(date.minus(1, ChronoUnit.SECONDS)); + dates.add(date.minus(1, ChronoUnit.NANOS)); + dates.add(date); + dates.add(date.plus(1, ChronoUnit.NANOS)); + dates.add(date.plus(1, ChronoUnit.SECONDS)); + dates.add(date.plus(1, ChronoUnit.MINUTES)); + dates.add(date.plus(1, ChronoUnit.HOURS)); + dates.add(date.plus(1, ChronoUnit.DAYS)); + dates.add(date.plus(1, ChronoUnit.WEEKS)); + dates.add(date.plus(1, ChronoUnit.MONTHS)); + dates.add(date.plus(1, ChronoUnit.YEARS)); + dates.add(date.plus(100, ChronoUnit.YEARS)); + + // Check these dates against the corresponding dates for every calendar + for (Chrono[] clist : data_of_calendars()) { + List> otherDates = new ArrayList<>(); + Chrono chrono2 = ISOChrono.INSTANCE; //clist[0]; + for (ChronoZonedDateTime d : dates) { + otherDates.add(chrono2.date(d).atTime(d.getTime()).atZone(d.getZone())); + } + + // Now compare the sequence of original dates with the sequence of converted dates + for (int i = 0; i < dates.size(); i++) { + ChronoZonedDateTime a = dates.get(i); + for (int j = 0; j < otherDates.size(); j++) { + ChronoZonedDateTime b = otherDates.get(j); + int cmp = ChronoZonedDateTime.INSTANT_COMPARATOR.compare(a, b); + if (i < j) { + assertTrue(cmp < 0, a + " compare " + b); + assertEquals(a.isBefore(b), true, a + " isBefore " + b); + assertEquals(a.isAfter(b), false, a + " ifAfter " + b); + assertEquals(a.isEqual(b), false, a + " isEqual " + b); + } else if (i > j) { + assertTrue(cmp > 0, a + " compare " + b); + assertEquals(a.isBefore(b), false, a + " isBefore " + b); + assertEquals(a.isAfter(b), true, a + " ifAfter " + b); + assertEquals(a.isEqual(b), false, a + " isEqual " + b); + } else { + assertTrue(cmp == 0, a + " compare " + b); + assertEquals(a.isBefore(b), false, a + " isBefore " + b); + assertEquals(a.isAfter(b), false, a + " ifAfter " + b); + assertEquals(a.isEqual(b), true, a + " isEqual " + b); + } + } + } + } + } + + //----------------------------------------------------------------------- + // Test Serialization of ISO via chrono API + //----------------------------------------------------------------------- + @Test( groups={"tck"}, dataProvider="calendars") + public > void test_ChronoZonedDateTimeSerialization(C chrono) throws Exception { + ZonedDateTime ref = LocalDate.of(2000, 1, 5).atTime(12, 1, 2, 3).atZone(ZoneId.of("GMT+01:23")); + ChronoZonedDateTime orginal = chrono.date(ref).atTime(ref.getTime()).atZone(ref.getZone()); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(baos); + out.writeObject(orginal); + out.close(); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream in = new ObjectInputStream(bais); + @SuppressWarnings("unchecked") + ChronoZonedDateTime ser = (ChronoZonedDateTime) in.readObject(); + assertEquals(ser, orginal, "deserialized date is wrong"); + } + + + /** + * FixedAdjusted returns a fixed Temporal in all adjustments. + * Construct an adjuster with the Temporal that should be returned from adjust. + */ + static class FixedAdjuster implements TemporalAdjuster, TemporalAdder, TemporalSubtractor { + private Temporal datetime; + + FixedAdjuster(Temporal datetime) { + this.datetime = datetime; + } + + @Override + public Temporal adjustInto(Temporal ignore) { + return datetime; + } + + @Override + public Temporal addTo(Temporal ignore) { + return datetime; + } + + @Override + public Temporal subtractFrom(Temporal ignore) { + return datetime; + } + + } + + /** + * FixedTemporalUnit returns a fixed Temporal in all adjustments. + * Construct an FixedTemporalUnit with the Temporal that should be returned from doPlus. + */ + static class FixedTemporalUnit implements TemporalUnit { + private Temporal temporal; + + FixedTemporalUnit(Temporal temporal) { + this.temporal = temporal; + } + + @Override + public String getName() { + return "FixedTemporalUnit"; + } + + @Override + public Duration getDuration() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean isDurationEstimated() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean isSupported(Temporal temporal) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @SuppressWarnings("unchecked") + @Override + public R doPlus(R dateTime, long periodToAdd) { + return (R) this.temporal; + } + + @Override + public SimplePeriod between(R dateTime1, R dateTime2) { + throw new UnsupportedOperationException("Not supported yet."); + } + } + + /** + * FixedTemporalField returns a fixed Temporal in all adjustments. + * Construct an FixedTemporalField with the Temporal that should be returned from doWith. + */ + static class FixedTemporalField implements TemporalField { + private Temporal temporal; + FixedTemporalField(Temporal temporal) { + this.temporal = temporal; + } + + @Override + public String getName() { + return "FixedTemporalField"; + } + + @Override + public TemporalUnit getBaseUnit() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public TemporalUnit getRangeUnit() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public ValueRange range() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean doIsSupported(TemporalAccessor temporal) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public ValueRange doRange(TemporalAccessor temporal) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public long doGet(TemporalAccessor temporal) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @SuppressWarnings("unchecked") + @Override + public R doWith(R temporal, long newValue) { + return (R) this.temporal; + } + + @Override + public boolean resolve(DateTimeBuilder builder, long value) { + throw new UnsupportedOperationException("Not supported yet."); + } + + } +} diff --git a/jdk/test/java/time/tck/java/time/temporal/TestISOChrono.java b/jdk/test/java/time/tck/java/time/temporal/TestISOChrono.java new file mode 100644 index 00000000000..634fab301e5 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/temporal/TestISOChrono.java @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.temporal; + +import static java.time.temporal.ChronoField.ERA; +import static java.time.temporal.ChronoField.YEAR; +import static java.time.temporal.ChronoField.YEAR_OF_ERA; +import static java.time.temporal.ISOChrono.ERA_BCE; +import static java.time.temporal.ISOChrono.ERA_CE; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.temporal.Chrono; +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoLocalDate; +import java.time.temporal.Adjusters; +import java.time.calendar.HijrahChrono; +import java.time.temporal.Era; +import java.time.temporal.ISOChrono; + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test. + */ +@Test +public class TestISOChrono { + + //----------------------------------------------------------------------- + // Chrono.ofName("ISO") Lookup by name + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_chrono_byName() { + Chrono c = ISOChrono.INSTANCE; + Chrono test = Chrono.of("ISO"); + Assert.assertNotNull(test, "The ISO calendar could not be found byName"); + Assert.assertEquals(test.getId(), "ISO", "ID mismatch"); + Assert.assertEquals(test.getCalendarType(), "iso8601", "Type mismatch"); + Assert.assertEquals(test, c); + } + + //----------------------------------------------------------------------- + // Lookup by Singleton + //----------------------------------------------------------------------- + @Test(groups="tck") + public void instanceNotNull() { + assertNotNull(ISOChrono.INSTANCE); + } + + //----------------------------------------------------------------------- + // Era creation + //----------------------------------------------------------------------- + @Test(groups="tck") + public void test_eraOf() { + assertEquals(ISOChrono.INSTANCE.eraOf(0), ERA_BCE); + assertEquals(ISOChrono.INSTANCE.eraOf(1), ERA_CE); + } + + //----------------------------------------------------------------------- + // creation, toLocalDate() + //----------------------------------------------------------------------- + @DataProvider(name="samples") + Object[][] data_samples() { + return new Object[][] { + {ISOChrono.INSTANCE.date(1, 7, 8), LocalDate.of(1, 7, 8)}, + {ISOChrono.INSTANCE.date(1, 7, 20), LocalDate.of(1, 7, 20)}, + {ISOChrono.INSTANCE.date(1, 7, 21), LocalDate.of(1, 7, 21)}, + + {ISOChrono.INSTANCE.date(2, 7, 8), LocalDate.of(2, 7, 8)}, + {ISOChrono.INSTANCE.date(3, 6, 27), LocalDate.of(3, 6, 27)}, + {ISOChrono.INSTANCE.date(3, 5, 23), LocalDate.of(3, 5, 23)}, + {ISOChrono.INSTANCE.date(4, 6, 16), LocalDate.of(4, 6, 16)}, + {ISOChrono.INSTANCE.date(4, 7, 3), LocalDate.of(4, 7, 3)}, + {ISOChrono.INSTANCE.date(4, 7, 4), LocalDate.of(4, 7, 4)}, + {ISOChrono.INSTANCE.date(5, 1, 1), LocalDate.of(5, 1, 1)}, + {ISOChrono.INSTANCE.date(1727, 3, 3), LocalDate.of(1727, 3, 3)}, + {ISOChrono.INSTANCE.date(1728, 10, 28), LocalDate.of(1728, 10, 28)}, + {ISOChrono.INSTANCE.date(2012, 10, 29), LocalDate.of(2012, 10, 29)}, + }; + } + + @Test(dataProvider="samples", groups={"tck"}) + public void test_toLocalDate(ChronoLocalDate isoDate, LocalDate iso) { + assertEquals(LocalDate.from(isoDate), iso); + } + + @Test(dataProvider="samples", groups={"tck"}) + public void test_fromCalendrical(ChronoLocalDate isoDate, LocalDate iso) { + assertEquals(ISOChrono.INSTANCE.date(iso), isoDate); + } + + @DataProvider(name="badDates") + Object[][] data_badDates() { + return new Object[][] { + {2012, 0, 0}, + + {2012, -1, 1}, + {2012, 0, 1}, + {2012, 14, 1}, + {2012, 15, 1}, + + {2012, 1, -1}, + {2012, 1, 0}, + {2012, 1, 32}, + + {2012, 12, -1}, + {2012, 12, 0}, + {2012, 12, 32}, + }; + } + + @Test(dataProvider="badDates", groups={"tck"}, expectedExceptions=DateTimeException.class) + public void test_badDates(int year, int month, int dom) { + ISOChrono.INSTANCE.date(year, month, dom); + } + + @Test(groups="tck") + public void test_date_withEra() { + int year = 5; + int month = 5; + int dayOfMonth = 5; + ChronoLocalDate test = ISOChrono.INSTANCE.date(ERA_BCE, year, month, dayOfMonth); + assertEquals(test.getEra(), ERA_BCE); + assertEquals(test.get(ChronoField.YEAR_OF_ERA), year); + assertEquals(test.get(ChronoField.MONTH_OF_YEAR), month); + assertEquals(test.get(ChronoField.DAY_OF_MONTH), dayOfMonth); + + assertEquals(test.get(YEAR), 1 + (-1 * year)); + assertEquals(test.get(ERA), 0); + assertEquals(test.get(YEAR_OF_ERA), year); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Test(expectedExceptions=DateTimeException.class, groups="tck") + public void test_date_withEra_withWrongEra() { + ISOChrono.INSTANCE.date((Era) HijrahChrono.ERA_AH, 1, 1, 1); + } + + //----------------------------------------------------------------------- + // with(DateTimeAdjuster) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_adjust1() { + ChronoLocalDate base = ISOChrono.INSTANCE.date(1728, 10, 28); + ChronoLocalDate test = base.with(Adjusters.lastDayOfMonth()); + assertEquals(test, ISOChrono.INSTANCE.date(1728, 10, 31)); + } + + @Test(groups={"tck"}) + public void test_adjust2() { + ChronoLocalDate base = ISOChrono.INSTANCE.date(1728, 12, 2); + ChronoLocalDate test = base.with(Adjusters.lastDayOfMonth()); + assertEquals(test, ISOChrono.INSTANCE.date(1728, 12, 31)); + } + + //----------------------------------------------------------------------- + // ISODate.with(Local*) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_adjust_toLocalDate() { + ChronoLocalDate isoDate = ISOChrono.INSTANCE.date(1726, 1, 4); + ChronoLocalDate test = isoDate.with(LocalDate.of(2012, 7, 6)); + assertEquals(test, ISOChrono.INSTANCE.date(2012, 7, 6)); + } + + @Test(groups={"tck"}) + public void test_adjust_toMonth() { + ChronoLocalDate isoDate = ISOChrono.INSTANCE.date(1726, 1, 4); + assertEquals(ISOChrono.INSTANCE.date(1726, 4, 4), isoDate.with(Month.APRIL)); + } + + //----------------------------------------------------------------------- + // LocalDate.with(ISODate) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_LocalDate_adjustToISODate() { + ChronoLocalDate isoDate = ISOChrono.INSTANCE.date(1728, 10, 29); + LocalDate test = LocalDate.MIN.with(isoDate); + assertEquals(test, LocalDate.of(1728, 10, 29)); + } + + @Test(groups={"tck"}) + public void test_LocalDateTime_adjustToISODate() { + ChronoLocalDate isoDate = ISOChrono.INSTANCE.date(1728, 10, 29); + LocalDateTime test = LocalDateTime.MIN.with(isoDate); + assertEquals(test, LocalDateTime.of(1728, 10, 29, 0, 0)); + } + + //----------------------------------------------------------------------- + // isLeapYear() + //----------------------------------------------------------------------- + @DataProvider(name="leapYears") + Object[][] leapYearInformation() { + return new Object[][] { + {2000, true}, + {1996, true}, + {1600, true}, + + {1900, false}, + {2100, false}, + }; + } + + @Test(dataProvider="leapYears", groups="tck") + public void test_isLeapYear(int year, boolean isLeapYear) { + assertEquals(ISOChrono.INSTANCE.isLeapYear(year), isLeapYear); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @Test(groups="tck") + public void test_now() { + assertEquals(LocalDate.from(ISOChrono.INSTANCE.dateNow()), LocalDate.now()); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @DataProvider(name="toString") + Object[][] data_toString() { + return new Object[][] { + {ISOChrono.INSTANCE.date(1, 1, 1), "0001-01-01"}, + {ISOChrono.INSTANCE.date(1728, 10, 28), "1728-10-28"}, + {ISOChrono.INSTANCE.date(1728, 10, 29), "1728-10-29"}, + {ISOChrono.INSTANCE.date(1727, 12, 5), "1727-12-05"}, + {ISOChrono.INSTANCE.date(1727, 12, 6), "1727-12-06"}, + }; + } + + @Test(dataProvider="toString", groups={"tck"}) + public void test_toString(ChronoLocalDate isoDate, String expected) { + assertEquals(isoDate.toString(), expected); + } + + //----------------------------------------------------------------------- + // equals() + //----------------------------------------------------------------------- + @Test(groups="tck") + public void test_equals_true() { + assertTrue(ISOChrono.INSTANCE.equals(ISOChrono.INSTANCE)); + } + + @Test(groups="tck") + public void test_equals_false() { + assertFalse(ISOChrono.INSTANCE.equals(HijrahChrono.INSTANCE)); + } + +} diff --git a/jdk/test/java/time/tck/java/time/zone/TCKFixedZoneRules.java b/jdk/test/java/time/tck/java/time/zone/TCKFixedZoneRules.java new file mode 100644 index 00000000000..08617bae23b --- /dev/null +++ b/jdk/test/java/time/tck/java/time/zone/TCKFixedZoneRules.java @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2010-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.zone; + +import java.time.zone.*; +import test.java.time.zone.*; + +import static org.testng.Assert.assertEquals; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Month; +import java.time.ZoneOffset; +import java.time.zone.ZoneOffsetTransitionRule.TimeDefinition; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test ZoneRules for fixed offset time-zones. + */ +@Test +public class TCKFixedZoneRules { + + private static final ZoneOffset OFFSET_PONE = ZoneOffset.ofHours(1); + private static final ZoneOffset OFFSET_PTWO = ZoneOffset.ofHours(2); + private static final ZoneOffset OFFSET_M18 = ZoneOffset.ofHours(-18); + private static final LocalDateTime LDT = LocalDateTime.of(2010, 12, 3, 11, 30); + private static final Instant INSTANT = LDT.toInstant(OFFSET_PONE); + + private ZoneRules make(ZoneOffset offset) { + return offset.getRules(); + } + + @DataProvider(name="rules") + Object[][] data_rules() { + return new Object[][] { + {make(OFFSET_PONE), OFFSET_PONE}, + {make(OFFSET_PTWO), OFFSET_PTWO}, + {make(OFFSET_M18), OFFSET_M18}, + }; + } + + //----------------------------------------------------------------------- + // Basics + //----------------------------------------------------------------------- + @Test(groups="tck", dataProvider="rules") + public void test_serialization(ZoneRules test, ZoneOffset expectedOffset) throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(baos); + out.writeObject(test); + baos.close(); + byte[] bytes = baos.toByteArray(); + + ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + ObjectInputStream in = new ObjectInputStream(bais); + ZoneRules result = (ZoneRules) in.readObject(); + + assertEquals(result, test); + assertEquals(result.getClass(), test.getClass()); + } + + //----------------------------------------------------------------------- + // basics + //----------------------------------------------------------------------- + @Test(groups="tck", dataProvider="rules") + public void test_getOffset_Instant(ZoneRules test, ZoneOffset expectedOffset) { + assertEquals(test.getOffset(INSTANT), expectedOffset); + assertEquals(test.getOffset((Instant) null), expectedOffset); + } + + @Test(groups="tck", dataProvider="rules") + public void test_getOffset_LocalDateTime(ZoneRules test, ZoneOffset expectedOffset) { + assertEquals(test.getOffset(LDT), expectedOffset); + assertEquals(test.getOffset((LocalDateTime) null), expectedOffset); + } + + @Test(groups="tck", dataProvider="rules") + public void test_getValidOffsets_LDT(ZoneRules test, ZoneOffset expectedOffset) { + assertEquals(test.getValidOffsets(LDT).size(), 1); + assertEquals(test.getValidOffsets(LDT).get(0), expectedOffset); + assertEquals(test.getValidOffsets(null).size(), 1); + assertEquals(test.getValidOffsets(null).get(0), expectedOffset); + } + + @Test(groups="tck", dataProvider="rules") + public void test_getTransition_LDT(ZoneRules test, ZoneOffset expectedOffset) { + assertEquals(test.getTransition(LDT), null); + assertEquals(test.getTransition(null), null); + } + + @Test(groups="tck", dataProvider="rules") + public void test_isValidOffset_LDT_ZO(ZoneRules test, ZoneOffset expectedOffset) { + assertEquals(test.isValidOffset(LDT, expectedOffset), true); + assertEquals(test.isValidOffset(LDT, ZoneOffset.UTC), false); + assertEquals(test.isValidOffset(LDT, null), false); + + assertEquals(test.isValidOffset(null, expectedOffset), true); + assertEquals(test.isValidOffset(null, ZoneOffset.UTC), false); + assertEquals(test.isValidOffset(null, null), false); + } + + @Test(groups="tck", dataProvider="rules") + public void test_getStandardOffset_Instant(ZoneRules test, ZoneOffset expectedOffset) { + assertEquals(test.getStandardOffset(INSTANT), expectedOffset); + assertEquals(test.getStandardOffset(null), expectedOffset); + } + + @Test(groups="tck", dataProvider="rules") + public void test_getDaylightSavings_Instant(ZoneRules test, ZoneOffset expectedOffset) { + assertEquals(test.getDaylightSavings(INSTANT), Duration.ZERO); + assertEquals(test.getDaylightSavings(null), Duration.ZERO); + } + + @Test(groups="tck", dataProvider="rules") + public void test_isDaylightSavings_Instant(ZoneRules test, ZoneOffset expectedOffset) { + assertEquals(test.isDaylightSavings(INSTANT), false); + assertEquals(test.isDaylightSavings(null), false); + } + + //------------------------------------------------------------------------- + @Test(groups="tck", dataProvider="rules") + public void test_nextTransition_Instant(ZoneRules test, ZoneOffset expectedOffset) { + assertEquals(test.nextTransition(INSTANT), null); + assertEquals(test.nextTransition(null), null); + } + + @Test(groups="tck", dataProvider="rules") + public void test_previousTransition_Instant(ZoneRules test, ZoneOffset expectedOffset) { + assertEquals(test.previousTransition(INSTANT), null); + assertEquals(test.previousTransition(null), null); + } + + //------------------------------------------------------------------------- + @Test(groups="tck", dataProvider="rules") + public void test_getTransitions(ZoneRules test, ZoneOffset expectedOffset) { + assertEquals(test.getTransitions().size(), 0); + } + + @Test(expectedExceptions=UnsupportedOperationException.class, groups="tck") + public void test_getTransitions_immutable() { + ZoneRules test = make(OFFSET_PTWO); + test.getTransitions().add(ZoneOffsetTransition.of(LDT, OFFSET_PONE, OFFSET_PTWO)); + } + + @Test(groups="tck", dataProvider="rules") + public void test_getTransitionRules(ZoneRules test, ZoneOffset expectedOffset) { + assertEquals(test.getTransitionRules().size(), 0); + } + + @Test(expectedExceptions=UnsupportedOperationException.class, groups="tck") + public void test_getTransitionRules_immutable() { + ZoneRules test = make(OFFSET_PTWO); + test.getTransitionRules().add(ZoneOffsetTransitionRule.of(Month.JULY, 2, null, LocalTime.of(12, 30), false, TimeDefinition.STANDARD, OFFSET_PONE, OFFSET_PTWO, OFFSET_PONE)); + } + + //----------------------------------------------------------------------- + // equals() / hashCode() + //----------------------------------------------------------------------- + @Test(groups="tck") + public void test_equalsHashCode() { + ZoneRules a = make(OFFSET_PONE); + ZoneRules b = make(OFFSET_PTWO); + + assertEquals(a.equals(a), true); + assertEquals(a.equals(b), false); + assertEquals(b.equals(a), false); + assertEquals(b.equals(b), true); + + assertEquals(a.equals("Rubbish"), false); + assertEquals(a.equals(null), false); + + assertEquals(a.hashCode() == a.hashCode(), true); + assertEquals(b.hashCode() == b.hashCode(), true); + } + +} diff --git a/jdk/test/java/time/tck/java/time/zone/TCKZoneOffsetTransition.java b/jdk/test/java/time/tck/java/time/zone/TCKZoneOffsetTransition.java new file mode 100644 index 00000000000..36dc4ba0800 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/zone/TCKZoneOffsetTransition.java @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2010-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.zone; + +import java.time.temporal.Year; +import java.time.zone.*; + +import static java.time.temporal.ChronoUnit.HOURS; +import static org.testng.Assert.assertEquals; + +import java.io.IOException; + +import tck.java.time.AbstractTCKTest; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.ZoneOffset; + +import org.testng.annotations.Test; + +/** + * Test ZoneOffsetTransition. + */ +@Test +public class TCKZoneOffsetTransition extends AbstractTCKTest { + + private static final ZoneOffset OFFSET_0100 = ZoneOffset.ofHours(1); + private static final ZoneOffset OFFSET_0200 = ZoneOffset.ofHours(2); + private static final ZoneOffset OFFSET_0230 = ZoneOffset.ofHoursMinutes(2, 30); + private static final ZoneOffset OFFSET_0300 = ZoneOffset.ofHours(3); + private static final ZoneOffset OFFSET_0400 = ZoneOffset.ofHours(4); + + //----------------------------------------------------------------------- + // factory + //----------------------------------------------------------------------- + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_factory_nullTransition() { + ZoneOffsetTransition.of(null, OFFSET_0100, OFFSET_0200); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_factory_nullOffsetBefore() { + ZoneOffsetTransition.of(LocalDateTime.of(2010, 12, 3, 11, 30), null, OFFSET_0200); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_factory_nullOffsetAfter() { + ZoneOffsetTransition.of(LocalDateTime.of(2010, 12, 3, 11, 30), OFFSET_0200, null); + } + + @Test(expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_factory_sameOffset() { + ZoneOffsetTransition.of(LocalDateTime.of(2010, 12, 3, 11, 30), OFFSET_0200, OFFSET_0200); + } + + @Test(expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_factory_noNanos() { + ZoneOffsetTransition.of(LocalDateTime.of(2010, 12, 3, 11, 30, 0, 500), OFFSET_0200, OFFSET_0300); + } + + //----------------------------------------------------------------------- + // getters + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_getters_gap() throws Exception { + LocalDateTime before = LocalDateTime.of(2010, 3, 31, 1, 0); + LocalDateTime after = LocalDateTime.of(2010, 3, 31, 2, 0); + ZoneOffsetTransition test = ZoneOffsetTransition.of(before, OFFSET_0200, OFFSET_0300); + assertEquals(test.isGap(), true); + assertEquals(test.isOverlap(), false); + assertEquals(test.getDateTimeBefore(), before); + assertEquals(test.getDateTimeAfter(), after); + assertEquals(test.getInstant(), before.toInstant(OFFSET_0200)); + assertEquals(test.getOffsetBefore(), OFFSET_0200); + assertEquals(test.getOffsetAfter(), OFFSET_0300); + assertEquals(test.getDuration(), Duration.of(1, HOURS)); + assertSerializable(test); + } + + @Test(groups={"tck"}) + public void test_getters_overlap() throws Exception { + LocalDateTime before = LocalDateTime.of(2010, 10, 31, 1, 0); + LocalDateTime after = LocalDateTime.of(2010, 10, 31, 0, 0); + ZoneOffsetTransition test = ZoneOffsetTransition.of(before, OFFSET_0300, OFFSET_0200); + assertEquals(test.isGap(), false); + assertEquals(test.isOverlap(), true); + assertEquals(test.getDateTimeBefore(), before); + assertEquals(test.getDateTimeAfter(), after); + assertEquals(test.getInstant(), before.toInstant(OFFSET_0300)); + assertEquals(test.getOffsetBefore(), OFFSET_0300); + assertEquals(test.getOffsetAfter(), OFFSET_0200); + assertEquals(test.getDuration(), Duration.of(-1, HOURS)); + assertSerializable(test); + } + + //----------------------------------------------------------------------- + @Test + public void test_serialization_unusual1() throws Exception { + LocalDateTime ldt = LocalDateTime.of(Year.MAX_VALUE, 12, 31, 1, 31, 53); + ZoneOffsetTransition test = ZoneOffsetTransition.of(ldt, ZoneOffset.of("+02:04:56"), ZoneOffset.of("-10:02:34")); + assertSerializable(test); + } + + @Test + public void test_serialization_unusual2() throws Exception { + LocalDateTime ldt = LocalDateTime.of(Year.MIN_VALUE, 1, 1, 12, 1, 3); + ZoneOffsetTransition test = ZoneOffsetTransition.of(ldt, ZoneOffset.of("+02:04:56"), ZoneOffset.of("+10:02:34")); + assertSerializable(test); + } + + //----------------------------------------------------------------------- + // isValidOffset() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_isValidOffset_gap() { + LocalDateTime ldt = LocalDateTime.of(2010, 3, 31, 1, 0); + ZoneOffsetTransition test = ZoneOffsetTransition.of(ldt, OFFSET_0200, OFFSET_0300); + assertEquals(test.isValidOffset(OFFSET_0100), false); + assertEquals(test.isValidOffset(OFFSET_0200), false); + assertEquals(test.isValidOffset(OFFSET_0230), false); + assertEquals(test.isValidOffset(OFFSET_0300), false); + assertEquals(test.isValidOffset(OFFSET_0400), false); + } + + @Test(groups={"tck"}) + public void test_isValidOffset_overlap() { + LocalDateTime ldt = LocalDateTime.of(2010, 10, 31, 1, 0); + ZoneOffsetTransition test = ZoneOffsetTransition.of(ldt, OFFSET_0300, OFFSET_0200); + assertEquals(test.isValidOffset(OFFSET_0100), false); + assertEquals(test.isValidOffset(OFFSET_0200), true); + assertEquals(test.isValidOffset(OFFSET_0230), false); + assertEquals(test.isValidOffset(OFFSET_0300), true); + assertEquals(test.isValidOffset(OFFSET_0400), false); + } + + //----------------------------------------------------------------------- + // compareTo() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_compareTo() { + ZoneOffsetTransition a = ZoneOffsetTransition.of( + LocalDateTime.ofEpochSecond(23875287L - 1, 0, OFFSET_0200), OFFSET_0200, OFFSET_0300); + ZoneOffsetTransition b = ZoneOffsetTransition.of( + LocalDateTime.ofEpochSecond(23875287L, 0, OFFSET_0300), OFFSET_0300, OFFSET_0200); + ZoneOffsetTransition c = ZoneOffsetTransition.of( + LocalDateTime.ofEpochSecond(23875287L + 1, 0, OFFSET_0100), OFFSET_0100,OFFSET_0400); + + assertEquals(a.compareTo(a) == 0, true); + assertEquals(a.compareTo(b) < 0, true); + assertEquals(a.compareTo(c) < 0, true); + + assertEquals(b.compareTo(a) > 0, true); + assertEquals(b.compareTo(b) == 0, true); + assertEquals(b.compareTo(c) < 0, true); + + assertEquals(c.compareTo(a) > 0, true); + assertEquals(c.compareTo(b) > 0, true); + assertEquals(c.compareTo(c) == 0, true); + } + + @Test(groups={"tck"}) + public void test_compareTo_sameInstant() { + ZoneOffsetTransition a = ZoneOffsetTransition.of( + LocalDateTime.ofEpochSecond(23875287L, 0, OFFSET_0200), OFFSET_0200, OFFSET_0300); + ZoneOffsetTransition b = ZoneOffsetTransition.of( + LocalDateTime.ofEpochSecond(23875287L, 0, OFFSET_0300), OFFSET_0300, OFFSET_0200); + ZoneOffsetTransition c = ZoneOffsetTransition.of( + LocalDateTime.ofEpochSecond(23875287L, 0, OFFSET_0100), OFFSET_0100, OFFSET_0400); + + assertEquals(a.compareTo(a) == 0, true); + assertEquals(a.compareTo(b) == 0, true); + assertEquals(a.compareTo(c) == 0, true); + + assertEquals(b.compareTo(a) == 0, true); + assertEquals(b.compareTo(b) == 0, true); + assertEquals(b.compareTo(c) == 0, true); + + assertEquals(c.compareTo(a) == 0, true); + assertEquals(c.compareTo(b) == 0, true); + assertEquals(c.compareTo(c) == 0, true); + } + + //----------------------------------------------------------------------- + // equals() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_equals() { + LocalDateTime ldtA = LocalDateTime.of(2010, 3, 31, 1, 0); + ZoneOffsetTransition a1 = ZoneOffsetTransition.of(ldtA, OFFSET_0200, OFFSET_0300); + ZoneOffsetTransition a2 = ZoneOffsetTransition.of(ldtA, OFFSET_0200, OFFSET_0300); + LocalDateTime ldtB = LocalDateTime.of(2010, 10, 31, 1, 0); + ZoneOffsetTransition b = ZoneOffsetTransition.of(ldtB, OFFSET_0300, OFFSET_0200); + + assertEquals(a1.equals(a1), true); + assertEquals(a1.equals(a2), true); + assertEquals(a1.equals(b), false); + assertEquals(a2.equals(a1), true); + assertEquals(a2.equals(a2), true); + assertEquals(a2.equals(b), false); + assertEquals(b.equals(a1), false); + assertEquals(b.equals(a2), false); + assertEquals(b.equals(b), true); + + assertEquals(a1.equals(""), false); + assertEquals(a1.equals(null), false); + } + + //----------------------------------------------------------------------- + // hashCode() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_hashCode_floatingWeek_gap_notEndOfDay() { + LocalDateTime ldtA = LocalDateTime.of(2010, 3, 31, 1, 0); + ZoneOffsetTransition a1 = ZoneOffsetTransition.of(ldtA, OFFSET_0200, OFFSET_0300); + ZoneOffsetTransition a2 = ZoneOffsetTransition.of(ldtA, OFFSET_0200, OFFSET_0300); + LocalDateTime ldtB = LocalDateTime.of(2010, 10, 31, 1, 0); + ZoneOffsetTransition b = ZoneOffsetTransition.of(ldtB, OFFSET_0300, OFFSET_0200); + + assertEquals(a1.hashCode(), a1.hashCode()); + assertEquals(a1.hashCode(), a2.hashCode()); + assertEquals(b.hashCode(), b.hashCode()); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toString_gap() { + LocalDateTime ldt = LocalDateTime.of(2010, 3, 31, 1, 0); + ZoneOffsetTransition test = ZoneOffsetTransition.of(ldt, OFFSET_0200, OFFSET_0300); + assertEquals(test.toString(), "Transition[Gap at 2010-03-31T01:00+02:00 to +03:00]"); + } + + @Test(groups={"tck"}) + public void test_toString_overlap() { + LocalDateTime ldt = LocalDateTime.of(2010, 10, 31, 1, 0); + ZoneOffsetTransition test = ZoneOffsetTransition.of(ldt, OFFSET_0300, OFFSET_0200); + assertEquals(test.toString(), "Transition[Overlap at 2010-10-31T01:00+03:00 to +02:00]"); + } + +} diff --git a/jdk/test/java/time/tck/java/time/zone/TCKZoneOffsetTransitionRule.java b/jdk/test/java/time/tck/java/time/zone/TCKZoneOffsetTransitionRule.java new file mode 100644 index 00000000000..f98d7141b16 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/zone/TCKZoneOffsetTransitionRule.java @@ -0,0 +1,557 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2010-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.zone; + +import java.time.ZoneId; +import java.time.zone.*; +import test.java.time.zone.*; + +import static org.testng.Assert.assertEquals; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import tck.java.time.AbstractTCKTest; +import java.time.DayOfWeek; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Month; +import java.time.ZoneOffset; +import java.time.zone.ZoneOffsetTransitionRule.TimeDefinition; + +import org.testng.annotations.Test; + +/** + * Test ZoneOffsetTransitionRule. + */ +@Test +public class TCKZoneOffsetTransitionRule extends AbstractTCKTest { + + private static final LocalTime TIME_0100 = LocalTime.of(1, 0); + private static final ZoneOffset OFFSET_0200 = ZoneOffset.ofHours(2); + private static final ZoneOffset OFFSET_0300 = ZoneOffset.ofHours(3); + + //----------------------------------------------------------------------- + // factory + //----------------------------------------------------------------------- + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_factory_nullMonth() { + ZoneOffsetTransitionRule.of( + null, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_factory_nullTime() { + ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, null, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_factory_nullTimeDefinition() { + ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, false, null, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_factory_nullStandardOffset() { + ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + null, OFFSET_0200, OFFSET_0300); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_factory_nullOffsetBefore() { + ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, null, OFFSET_0300); + } + + @Test(expectedExceptions=NullPointerException.class, groups={"tck"}) + public void test_factory_nullOffsetAfter() { + ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, null); + } + + @Test(expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_factory_invalidDayOfMonthIndicator_tooSmall() { + ZoneOffsetTransitionRule.of( + Month.MARCH, -29, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + } + + @Test(expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_factory_invalidDayOfMonthIndicator_zero() { + ZoneOffsetTransitionRule.of( + Month.MARCH, 0, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + } + + @Test(expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_factory_invalidDayOfMonthIndicator_tooLarge() { + ZoneOffsetTransitionRule.of( + Month.MARCH, 32, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + } + + @Test(expectedExceptions=IllegalArgumentException.class, groups={"tck"}) + public void test_factory_invalidMidnightFlag() { + ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, true, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + } + + //----------------------------------------------------------------------- + // getters + //----------------------------------------------------------------------- + @Test + public void test_getters_floatingWeek() throws Exception { + ZoneOffsetTransitionRule test = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + assertEquals(test.getMonth(), Month.MARCH); + assertEquals(test.getDayOfMonthIndicator(), 20); + assertEquals(test.getDayOfWeek(), DayOfWeek.SUNDAY); + assertEquals(test.getLocalTime(), TIME_0100); + assertEquals(test.isMidnightEndOfDay(), false); + assertEquals(test.getTimeDefinition(), TimeDefinition.WALL); + assertEquals(test.getStandardOffset(), OFFSET_0200); + assertEquals(test.getOffsetBefore(), OFFSET_0200); + assertEquals(test.getOffsetAfter(), OFFSET_0300); + assertSerializable(test); + } + + @Test + public void test_getters_floatingWeekBackwards() throws Exception { + ZoneOffsetTransitionRule test = ZoneOffsetTransitionRule.of( + Month.MARCH, -1, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + assertEquals(test.getMonth(), Month.MARCH); + assertEquals(test.getDayOfMonthIndicator(), -1); + assertEquals(test.getDayOfWeek(), DayOfWeek.SUNDAY); + assertEquals(test.getLocalTime(), TIME_0100); + assertEquals(test.isMidnightEndOfDay(), false); + assertEquals(test.getTimeDefinition(), TimeDefinition.WALL); + assertEquals(test.getStandardOffset(), OFFSET_0200); + assertEquals(test.getOffsetBefore(), OFFSET_0200); + assertEquals(test.getOffsetAfter(), OFFSET_0300); + assertSerializable(test); + } + + @Test + public void test_getters_fixedDate() throws Exception { + ZoneOffsetTransitionRule test = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, null, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + assertEquals(test.getMonth(), Month.MARCH); + assertEquals(test.getDayOfMonthIndicator(), 20); + assertEquals(test.getDayOfWeek(), null); + assertEquals(test.getLocalTime(), TIME_0100); + assertEquals(test.isMidnightEndOfDay(), false); + assertEquals(test.getTimeDefinition(), TimeDefinition.WALL); + assertEquals(test.getStandardOffset(), OFFSET_0200); + assertEquals(test.getOffsetBefore(), OFFSET_0200); + assertEquals(test.getOffsetAfter(), OFFSET_0300); + assertSerializable(test); + } + + @Test + public void test_serialization_unusualOffsets() throws Exception { + ZoneOffsetTransitionRule test = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, null, TIME_0100, false, TimeDefinition.STANDARD, + ZoneOffset.ofHoursMinutesSeconds(-12, -20, -50), + ZoneOffset.ofHoursMinutesSeconds(-4, -10, -34), + ZoneOffset.ofHours(-18)); + assertSerializable(test); + } + + @Test + public void test_serialization_endOfDay() throws Exception { + ZoneOffsetTransitionRule test = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.FRIDAY, LocalTime.MIDNIGHT, true, TimeDefinition.UTC, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + assertSerializable(test); + } + + @Test + public void test_serialization_unusualTime() throws Exception { + ZoneOffsetTransitionRule test = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.WEDNESDAY, LocalTime.of(13, 34, 56), false, TimeDefinition.STANDARD, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + assertSerializable(test); + } + + //----------------------------------------------------------------------- + // createTransition() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_createTransition_floatingWeek_gap_notEndOfDay() { + ZoneOffsetTransitionRule test = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + ZoneOffsetTransition trans = ZoneOffsetTransition.of( + LocalDateTime.of(2000, Month.MARCH, 26, 1, 0), OFFSET_0200, OFFSET_0300); + assertEquals(test.createTransition(2000), trans); + } + + @Test(groups={"tck"}) + public void test_createTransition_floatingWeek_overlap_endOfDay() { + ZoneOffsetTransitionRule test = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, LocalTime.MIDNIGHT, true, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0300, OFFSET_0200); + ZoneOffsetTransition trans = ZoneOffsetTransition.of( + LocalDateTime.of(2000, Month.MARCH, 27, 0, 0), OFFSET_0300, OFFSET_0200); + assertEquals(test.createTransition(2000), trans); + } + + @Test(groups={"tck"}) + public void test_createTransition_floatingWeekBackwards_last() { + ZoneOffsetTransitionRule test = ZoneOffsetTransitionRule.of( + Month.MARCH, -1, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + ZoneOffsetTransition trans = ZoneOffsetTransition.of( + LocalDateTime.of(2000, Month.MARCH, 26, 1, 0), OFFSET_0200, OFFSET_0300); + assertEquals(test.createTransition(2000), trans); + } + + @Test(groups={"tck"}) + public void test_createTransition_floatingWeekBackwards_seventhLast() { + ZoneOffsetTransitionRule test = ZoneOffsetTransitionRule.of( + Month.MARCH, -7, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + ZoneOffsetTransition trans = ZoneOffsetTransition.of( + LocalDateTime.of(2000, Month.MARCH, 19, 1, 0), OFFSET_0200, OFFSET_0300); + assertEquals(test.createTransition(2000), trans); + } + + @Test(groups={"tck"}) + public void test_createTransition_floatingWeekBackwards_secondLast() { + ZoneOffsetTransitionRule test = ZoneOffsetTransitionRule.of( + Month.MARCH, -2, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + ZoneOffsetTransition trans = ZoneOffsetTransition.of( + LocalDateTime.of(2000, Month.MARCH, 26, 1, 0), OFFSET_0200, OFFSET_0300); + assertEquals(test.createTransition(2000), trans); + } + + @Test(groups={"tck"}) + public void test_createTransition_fixedDate() { + ZoneOffsetTransitionRule test = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, null, TIME_0100, false, TimeDefinition.STANDARD, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + ZoneOffsetTransition trans = ZoneOffsetTransition.of( + LocalDateTime.of(2000, Month.MARCH, 20, 1, 0), OFFSET_0200, OFFSET_0300); + assertEquals(test.createTransition(2000), trans); + } + + //----------------------------------------------------------------------- + // equals() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_equals_monthDifferent() { + ZoneOffsetTransitionRule a = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + ZoneOffsetTransitionRule b = ZoneOffsetTransitionRule.of( + Month.APRIL, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + assertEquals(a.equals(a), true); + assertEquals(a.equals(b), false); + assertEquals(b.equals(a), false); + assertEquals(b.equals(b), true); + } + + @Test(groups={"tck"}) + public void test_equals_dayOfMonthDifferent() { + ZoneOffsetTransitionRule a = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + ZoneOffsetTransitionRule b = ZoneOffsetTransitionRule.of( + Month.MARCH, 21, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + assertEquals(a.equals(a), true); + assertEquals(a.equals(b), false); + assertEquals(b.equals(a), false); + assertEquals(b.equals(b), true); + } + + @Test(groups={"tck"}) + public void test_equals_dayOfWeekDifferent() { + ZoneOffsetTransitionRule a = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + ZoneOffsetTransitionRule b = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SATURDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + assertEquals(a.equals(a), true); + assertEquals(a.equals(b), false); + assertEquals(b.equals(a), false); + assertEquals(b.equals(b), true); + } + + @Test(groups={"tck"}) + public void test_equals_dayOfWeekDifferentNull() { + ZoneOffsetTransitionRule a = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + ZoneOffsetTransitionRule b = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, null, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + assertEquals(a.equals(a), true); + assertEquals(a.equals(b), false); + assertEquals(b.equals(a), false); + assertEquals(b.equals(b), true); + } + + @Test(groups={"tck"}) + public void test_equals_localTimeDifferent() { + ZoneOffsetTransitionRule a = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + ZoneOffsetTransitionRule b = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, LocalTime.MIDNIGHT, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + assertEquals(a.equals(a), true); + assertEquals(a.equals(b), false); + assertEquals(b.equals(a), false); + assertEquals(b.equals(b), true); + } + + @Test(groups={"tck"}) + public void test_equals_endOfDayDifferent() { + ZoneOffsetTransitionRule a = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, LocalTime.MIDNIGHT, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + ZoneOffsetTransitionRule b = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, LocalTime.MIDNIGHT, true, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + assertEquals(a.equals(a), true); + assertEquals(a.equals(b), false); + assertEquals(b.equals(a), false); + assertEquals(b.equals(b), true); + } + + @Test(groups={"tck"}) + public void test_equals_timeDefinitionDifferent() { + ZoneOffsetTransitionRule a = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + ZoneOffsetTransitionRule b = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.STANDARD, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + assertEquals(a.equals(a), true); + assertEquals(a.equals(b), false); + assertEquals(b.equals(a), false); + assertEquals(b.equals(b), true); + } + + @Test(groups={"tck"}) + public void test_equals_standardOffsetDifferent() { + ZoneOffsetTransitionRule a = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + ZoneOffsetTransitionRule b = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0300, OFFSET_0200, OFFSET_0300); + assertEquals(a.equals(a), true); + assertEquals(a.equals(b), false); + assertEquals(b.equals(a), false); + assertEquals(b.equals(b), true); + } + + @Test(groups={"tck"}) + public void test_equals_offsetBeforeDifferent() { + ZoneOffsetTransitionRule a = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + ZoneOffsetTransitionRule b = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0300, OFFSET_0300); + assertEquals(a.equals(a), true); + assertEquals(a.equals(b), false); + assertEquals(b.equals(a), false); + assertEquals(b.equals(b), true); + } + + @Test(groups={"tck"}) + public void test_equals_offsetAfterDifferent() { + ZoneOffsetTransitionRule a = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + ZoneOffsetTransitionRule b = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0200); + assertEquals(a.equals(a), true); + assertEquals(a.equals(b), false); + assertEquals(b.equals(a), false); + assertEquals(b.equals(b), true); + } + + @Test(groups={"tck"}) + public void test_equals_string_false() { + ZoneOffsetTransitionRule a = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + assertEquals(a.equals("TZDB"), false); + } + + @Test(groups={"tck"}) + public void test_equals_null_false() { + ZoneOffsetTransitionRule a = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + assertEquals(a.equals(null), false); + } + + //----------------------------------------------------------------------- + // hashCode() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_hashCode_floatingWeek_gap_notEndOfDay() { + ZoneOffsetTransitionRule a = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + ZoneOffsetTransitionRule b = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + assertEquals(a.hashCode(), b.hashCode()); + } + + @Test(groups={"tck"}) + public void test_hashCode_floatingWeek_overlap_endOfDay_nullDayOfWeek() { + ZoneOffsetTransitionRule a = ZoneOffsetTransitionRule.of( + Month.OCTOBER, 20, null, LocalTime.MIDNIGHT, true, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0300, OFFSET_0200); + ZoneOffsetTransitionRule b = ZoneOffsetTransitionRule.of( + Month.OCTOBER, 20, null, LocalTime.MIDNIGHT, true, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0300, OFFSET_0200); + assertEquals(a.hashCode(), b.hashCode()); + } + + @Test(groups={"tck"}) + public void test_hashCode_floatingWeekBackwards() { + ZoneOffsetTransitionRule a = ZoneOffsetTransitionRule.of( + Month.MARCH, -1, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + ZoneOffsetTransitionRule b = ZoneOffsetTransitionRule.of( + Month.MARCH, -1, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + assertEquals(a.hashCode(), b.hashCode()); + } + + @Test(groups={"tck"}) + public void test_hashCode_fixedDate() { + ZoneOffsetTransitionRule a = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, null, TIME_0100, false, TimeDefinition.STANDARD, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + ZoneOffsetTransitionRule b = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, null, TIME_0100, false, TimeDefinition.STANDARD, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + assertEquals(a.hashCode(), b.hashCode()); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_toString_floatingWeek_gap_notEndOfDay() { + ZoneOffsetTransitionRule test = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + assertEquals(test.toString(), "TransitionRule[Gap +02:00 to +03:00, SUNDAY on or after MARCH 20 at 01:00 WALL, standard offset +02:00]"); + } + + @Test(groups={"tck"}) + public void test_toString_floatingWeek_overlap_endOfDay() { + ZoneOffsetTransitionRule test = ZoneOffsetTransitionRule.of( + Month.OCTOBER, 20, DayOfWeek.SUNDAY, LocalTime.MIDNIGHT, true, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0300, OFFSET_0200); + assertEquals(test.toString(), "TransitionRule[Overlap +03:00 to +02:00, SUNDAY on or after OCTOBER 20 at 24:00 WALL, standard offset +02:00]"); + } + + @Test(groups={"tck"}) + public void test_toString_floatingWeekBackwards_last() { + ZoneOffsetTransitionRule test = ZoneOffsetTransitionRule.of( + Month.MARCH, -1, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + assertEquals(test.toString(), "TransitionRule[Gap +02:00 to +03:00, SUNDAY on or before last day of MARCH at 01:00 WALL, standard offset +02:00]"); + } + + @Test(groups={"tck"}) + public void test_toString_floatingWeekBackwards_secondLast() { + ZoneOffsetTransitionRule test = ZoneOffsetTransitionRule.of( + Month.MARCH, -2, DayOfWeek.SUNDAY, TIME_0100, false, TimeDefinition.WALL, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + assertEquals(test.toString(), "TransitionRule[Gap +02:00 to +03:00, SUNDAY on or before last day minus 1 of MARCH at 01:00 WALL, standard offset +02:00]"); + } + + @Test(groups={"tck"}) + public void test_toString_fixedDate() { + ZoneOffsetTransitionRule test = ZoneOffsetTransitionRule.of( + Month.MARCH, 20, null, TIME_0100, false, TimeDefinition.STANDARD, + OFFSET_0200, OFFSET_0200, OFFSET_0300); + assertEquals(test.toString(), "TransitionRule[Gap +02:00 to +03:00, MARCH 20 at 01:00 STANDARD, standard offset +02:00]"); + } + +} diff --git a/jdk/test/java/time/tck/java/time/zone/TCKZoneRules.java b/jdk/test/java/time/tck/java/time/zone/TCKZoneRules.java new file mode 100644 index 00000000000..294e2e07cb2 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/zone/TCKZoneRules.java @@ -0,0 +1,1005 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.zone; + +import java.time.temporal.Year; +import java.time.zone.*; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Iterator; +import java.util.List; + +import java.time.DayOfWeek; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Month; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.zone.ZoneOffsetTransitionRule.TimeDefinition; + +import org.testng.annotations.Test; + +/** + * Test ZoneRules. + */ +@Test +public class TCKZoneRules { + + private static final ZoneOffset OFFSET_ZERO = ZoneOffset.ofHours(0); + private static final ZoneOffset OFFSET_PONE = ZoneOffset.ofHours(1); + private static final ZoneOffset OFFSET_PTWO = ZoneOffset.ofHours(2); + public static final String LATEST_TZDB = "2009b"; + private static final int OVERLAP = 2; + private static final int GAP = 0; + + //----------------------------------------------------------------------- + // Basics + //----------------------------------------------------------------------- + public void test_serialization_loaded() throws Exception { + assertSerialization(europeLondon()); + assertSerialization(europeParis()); + assertSerialization(americaNewYork()); + } + + private void assertSerialization(ZoneRules test) throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(baos); + out.writeObject(test); + baos.close(); + byte[] bytes = baos.toByteArray(); + + ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + ObjectInputStream in = new ObjectInputStream(bais); + ZoneRules result = (ZoneRules) in.readObject(); + + assertEquals(result, test); + } + + //----------------------------------------------------------------------- + // Europe/London + //----------------------------------------------------------------------- + private ZoneRules europeLondon() { + return ZoneId.of("Europe/London").getRules(); + } + + public void test_London() { + ZoneRules test = europeLondon(); + assertEquals(test.isFixedOffset(), false); + } + + public void test_London_preTimeZones() { + ZoneRules test = europeLondon(); + ZonedDateTime old = createZDT(1800, 1, 1, ZoneOffset.UTC); + Instant instant = old.toInstant(); + ZoneOffset offset = ZoneOffset.ofHoursMinutesSeconds(0, -1, -15); + assertEquals(test.getOffset(instant), offset); + checkOffset(test, old.getDateTime(), offset, 1); + assertEquals(test.getStandardOffset(instant), offset); + assertEquals(test.getDaylightSavings(instant), Duration.ZERO); + assertEquals(test.isDaylightSavings(instant), false); + } + + public void test_London_getOffset() { + ZoneRules test = europeLondon(); + assertEquals(test.getOffset(createInstant(2008, 1, 1, ZoneOffset.UTC)), OFFSET_ZERO); + assertEquals(test.getOffset(createInstant(2008, 2, 1, ZoneOffset.UTC)), OFFSET_ZERO); + assertEquals(test.getOffset(createInstant(2008, 3, 1, ZoneOffset.UTC)), OFFSET_ZERO); + assertEquals(test.getOffset(createInstant(2008, 4, 1, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 5, 1, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 6, 1, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 7, 1, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 8, 1, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 9, 1, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 10, 1, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 11, 1, ZoneOffset.UTC)), OFFSET_ZERO); + assertEquals(test.getOffset(createInstant(2008, 12, 1, ZoneOffset.UTC)), OFFSET_ZERO); + } + + public void test_London_getOffset_toDST() { + ZoneRules test = europeLondon(); + assertEquals(test.getOffset(createInstant(2008, 3, 24, ZoneOffset.UTC)), OFFSET_ZERO); + assertEquals(test.getOffset(createInstant(2008, 3, 25, ZoneOffset.UTC)), OFFSET_ZERO); + assertEquals(test.getOffset(createInstant(2008, 3, 26, ZoneOffset.UTC)), OFFSET_ZERO); + assertEquals(test.getOffset(createInstant(2008, 3, 27, ZoneOffset.UTC)), OFFSET_ZERO); + assertEquals(test.getOffset(createInstant(2008, 3, 28, ZoneOffset.UTC)), OFFSET_ZERO); + assertEquals(test.getOffset(createInstant(2008, 3, 29, ZoneOffset.UTC)), OFFSET_ZERO); + assertEquals(test.getOffset(createInstant(2008, 3, 30, ZoneOffset.UTC)), OFFSET_ZERO); + assertEquals(test.getOffset(createInstant(2008, 3, 31, ZoneOffset.UTC)), OFFSET_PONE); + // cutover at 01:00Z + assertEquals(test.getOffset(createInstant(2008, 3, 30, 0, 59, 59, 999999999, ZoneOffset.UTC)), OFFSET_ZERO); + assertEquals(test.getOffset(createInstant(2008, 3, 30, 1, 0, 0, 0, ZoneOffset.UTC)), OFFSET_PONE); + } + + public void test_London_getOffset_fromDST() { + ZoneRules test = europeLondon(); + assertEquals(test.getOffset(createInstant(2008, 10, 24, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 10, 25, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 10, 26, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 10, 27, ZoneOffset.UTC)), OFFSET_ZERO); + assertEquals(test.getOffset(createInstant(2008, 10, 28, ZoneOffset.UTC)), OFFSET_ZERO); + assertEquals(test.getOffset(createInstant(2008, 10, 29, ZoneOffset.UTC)), OFFSET_ZERO); + assertEquals(test.getOffset(createInstant(2008, 10, 30, ZoneOffset.UTC)), OFFSET_ZERO); + assertEquals(test.getOffset(createInstant(2008, 10, 31, ZoneOffset.UTC)), OFFSET_ZERO); + // cutover at 01:00Z + assertEquals(test.getOffset(createInstant(2008, 10, 26, 0, 59, 59, 999999999, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 10, 26, 1, 0, 0, 0, ZoneOffset.UTC)), OFFSET_ZERO); + } + + public void test_London_getOffsetInfo() { + ZoneRules test = europeLondon(); + checkOffset(test, createLDT(2008, 1, 1), OFFSET_ZERO, 1); + checkOffset(test, createLDT(2008, 2, 1), OFFSET_ZERO, 1); + checkOffset(test, createLDT(2008, 3, 1), OFFSET_ZERO, 1); + checkOffset(test, createLDT(2008, 4, 1), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 5, 1), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 6, 1), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 7, 1), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 8, 1), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 9, 1), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 10, 1), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 11, 1), OFFSET_ZERO, 1); + checkOffset(test, createLDT(2008, 12, 1), OFFSET_ZERO, 1); + } + + public void test_London_getOffsetInfo_toDST() { + ZoneRules test = europeLondon(); + checkOffset(test, createLDT(2008, 3, 24), OFFSET_ZERO, 1); + checkOffset(test, createLDT(2008, 3, 25), OFFSET_ZERO, 1); + checkOffset(test, createLDT(2008, 3, 26), OFFSET_ZERO, 1); + checkOffset(test, createLDT(2008, 3, 27), OFFSET_ZERO, 1); + checkOffset(test, createLDT(2008, 3, 28), OFFSET_ZERO, 1); + checkOffset(test, createLDT(2008, 3, 29), OFFSET_ZERO, 1); + checkOffset(test, createLDT(2008, 3, 30), OFFSET_ZERO, 1); + checkOffset(test, createLDT(2008, 3, 31), OFFSET_PONE, 1); + // cutover at 01:00Z + checkOffset(test, LocalDateTime.of(2008, 3, 30, 0, 59, 59, 999999999), OFFSET_ZERO, 1); + checkOffset(test, LocalDateTime.of(2008, 3, 30, 2, 0, 0, 0), OFFSET_PONE, 1); + } + + public void test_London_getOffsetInfo_fromDST() { + ZoneRules test = europeLondon(); + checkOffset(test, createLDT(2008, 10, 24), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 10, 25), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 10, 26), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 10, 27), OFFSET_ZERO, 1); + checkOffset(test, createLDT(2008, 10, 28), OFFSET_ZERO, 1); + checkOffset(test, createLDT(2008, 10, 29), OFFSET_ZERO, 1); + checkOffset(test, createLDT(2008, 10, 30), OFFSET_ZERO, 1); + checkOffset(test, createLDT(2008, 10, 31), OFFSET_ZERO, 1); + // cutover at 01:00Z + checkOffset(test, LocalDateTime.of(2008, 10, 26, 0, 59, 59, 999999999), OFFSET_PONE, 1); + checkOffset(test, LocalDateTime.of(2008, 10, 26, 2, 0, 0, 0), OFFSET_ZERO, 1); + } + + public void test_London_getOffsetInfo_gap() { + ZoneRules test = europeLondon(); + final LocalDateTime dateTime = LocalDateTime.of(2008, 3, 30, 1, 0, 0, 0); + ZoneOffsetTransition trans = checkOffset(test, dateTime, OFFSET_ZERO, GAP); + assertEquals(trans.isGap(), true); + assertEquals(trans.isOverlap(), false); + assertEquals(trans.getOffsetBefore(), OFFSET_ZERO); + assertEquals(trans.getOffsetAfter(), OFFSET_PONE); + assertEquals(trans.getInstant(), createInstant(2008, 3, 30, 1, 0, ZoneOffset.UTC)); + assertEquals(trans.getDateTimeBefore(), LocalDateTime.of(2008, 3, 30, 1, 0)); + assertEquals(trans.getDateTimeAfter(), LocalDateTime.of(2008, 3, 30, 2, 0)); + assertEquals(trans.isValidOffset(OFFSET_ZERO), false); + assertEquals(trans.isValidOffset(OFFSET_PONE), false); + assertEquals(trans.isValidOffset(OFFSET_PTWO), false); + assertEquals(trans.toString(), "Transition[Gap at 2008-03-30T01:00Z to +01:00]"); + + assertFalse(trans.equals(null)); + assertFalse(trans.equals(OFFSET_ZERO)); + assertTrue(trans.equals(trans)); + + final ZoneOffsetTransition otherTrans = test.getTransition(dateTime); + assertTrue(trans.equals(otherTrans)); + assertEquals(trans.hashCode(), otherTrans.hashCode()); + } + + public void test_London_getOffsetInfo_overlap() { + ZoneRules test = europeLondon(); + final LocalDateTime dateTime = LocalDateTime.of(2008, 10, 26, 1, 0, 0, 0); + ZoneOffsetTransition trans = checkOffset(test, dateTime, OFFSET_PONE, OVERLAP); + assertEquals(trans.isGap(), false); + assertEquals(trans.isOverlap(), true); + assertEquals(trans.getOffsetBefore(), OFFSET_PONE); + assertEquals(trans.getOffsetAfter(), OFFSET_ZERO); + assertEquals(trans.getInstant(), createInstant(2008, 10, 26, 1, 0, ZoneOffset.UTC)); + assertEquals(trans.getDateTimeBefore(), LocalDateTime.of(2008, 10, 26, 2, 0)); + assertEquals(trans.getDateTimeAfter(), LocalDateTime.of(2008, 10, 26, 1, 0)); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(-1)), false); + assertEquals(trans.isValidOffset(OFFSET_ZERO), true); + assertEquals(trans.isValidOffset(OFFSET_PONE), true); + assertEquals(trans.isValidOffset(OFFSET_PTWO), false); + assertEquals(trans.toString(), "Transition[Overlap at 2008-10-26T02:00+01:00 to Z]"); + + assertFalse(trans.equals(null)); + assertFalse(trans.equals(OFFSET_PONE)); + assertTrue(trans.equals(trans)); + + final ZoneOffsetTransition otherTrans = test.getTransition(dateTime); + assertTrue(trans.equals(otherTrans)); + assertEquals(trans.hashCode(), otherTrans.hashCode()); + } + + public void test_London_getStandardOffset() { + ZoneRules test = europeLondon(); + ZonedDateTime zdt = createZDT(1840, 1, 1, ZoneOffset.UTC); + while (zdt.getYear() < 2010) { + Instant instant = zdt.toInstant(); + if (zdt.getYear() < 1848) { + assertEquals(test.getStandardOffset(instant), ZoneOffset.ofHoursMinutesSeconds(0, -1, -15)); + } else if (zdt.getYear() >= 1969 && zdt.getYear() < 1972) { + assertEquals(test.getStandardOffset(instant), OFFSET_PONE); + } else { + assertEquals(test.getStandardOffset(instant), OFFSET_ZERO); + } + zdt = zdt.plusMonths(6); + } + } + + public void test_London_getTransitions() { + ZoneRules test = europeLondon(); + List trans = test.getTransitions(); + + ZoneOffsetTransition first = trans.get(0); + assertEquals(first.getDateTimeBefore(), LocalDateTime.of(1847, 12, 1, 0, 0)); + assertEquals(first.getOffsetBefore(), ZoneOffset.ofHoursMinutesSeconds(0, -1, -15)); + assertEquals(first.getOffsetAfter(), OFFSET_ZERO); + + ZoneOffsetTransition spring1916 = trans.get(1); + assertEquals(spring1916.getDateTimeBefore(), LocalDateTime.of(1916, 5, 21, 2, 0)); + assertEquals(spring1916.getOffsetBefore(), OFFSET_ZERO); + assertEquals(spring1916.getOffsetAfter(), OFFSET_PONE); + + ZoneOffsetTransition autumn1916 = trans.get(2); + assertEquals(autumn1916.getDateTimeBefore(), LocalDateTime.of(1916, 10, 1, 3, 0)); + assertEquals(autumn1916.getOffsetBefore(), OFFSET_PONE); + assertEquals(autumn1916.getOffsetAfter(), OFFSET_ZERO); + + ZoneOffsetTransition zot = null; + Iterator it = trans.iterator(); + while (it.hasNext()) { + zot = it.next(); + if (zot.getDateTimeBefore().getYear() == 1990) { + break; + } + } + assertEquals(zot.getDateTimeBefore(), LocalDateTime.of(1990, 3, 25, 1, 0)); + assertEquals(zot.getOffsetBefore(), OFFSET_ZERO); + zot = it.next(); + assertEquals(zot.getDateTimeBefore(), LocalDateTime.of(1990, 10, 28, 2, 0)); + assertEquals(zot.getOffsetBefore(), OFFSET_PONE); + zot = it.next(); + assertEquals(zot.getDateTimeBefore(), LocalDateTime.of(1991, 3, 31, 1, 0)); + assertEquals(zot.getOffsetBefore(), OFFSET_ZERO); + zot = it.next(); + assertEquals(zot.getDateTimeBefore(), LocalDateTime.of(1991, 10, 27, 2, 0)); + assertEquals(zot.getOffsetBefore(), OFFSET_PONE); + zot = it.next(); + assertEquals(zot.getDateTimeBefore(), LocalDateTime.of(1992, 3, 29, 1, 0)); + assertEquals(zot.getOffsetBefore(), OFFSET_ZERO); + zot = it.next(); + assertEquals(zot.getDateTimeBefore(), LocalDateTime.of(1992, 10, 25, 2, 0)); + assertEquals(zot.getOffsetBefore(), OFFSET_PONE); + zot = it.next(); + assertEquals(zot.getDateTimeBefore(), LocalDateTime.of(1993, 3, 28, 1, 0)); + assertEquals(zot.getOffsetBefore(), OFFSET_ZERO); + zot = it.next(); + assertEquals(zot.getDateTimeBefore(), LocalDateTime.of(1993, 10, 24, 2, 0)); + assertEquals(zot.getOffsetBefore(), OFFSET_PONE); + zot = it.next(); + assertEquals(zot.getDateTimeBefore(), LocalDateTime.of(1994, 3, 27, 1, 0)); + assertEquals(zot.getOffsetBefore(), OFFSET_ZERO); + zot = it.next(); + assertEquals(zot.getDateTimeBefore(), LocalDateTime.of(1994, 10, 23, 2, 0)); + assertEquals(zot.getOffsetBefore(), OFFSET_PONE); + zot = it.next(); + assertEquals(zot.getDateTimeBefore(), LocalDateTime.of(1995, 3, 26, 1, 0)); + assertEquals(zot.getOffsetBefore(), OFFSET_ZERO); + zot = it.next(); + assertEquals(zot.getDateTimeBefore(), LocalDateTime.of(1995, 10, 22, 2, 0)); + assertEquals(zot.getOffsetBefore(), OFFSET_PONE); + zot = it.next(); + assertEquals(zot.getDateTimeBefore(), LocalDateTime.of(1996, 3, 31, 1, 0)); + assertEquals(zot.getOffsetBefore(), OFFSET_ZERO); + zot = it.next(); + assertEquals(zot.getDateTimeBefore(), LocalDateTime.of(1996, 10, 27, 2, 0)); + assertEquals(zot.getOffsetBefore(), OFFSET_PONE); + zot = it.next(); + assertEquals(zot.getDateTimeBefore(), LocalDateTime.of(1997, 3, 30, 1, 0)); + assertEquals(zot.getOffsetBefore(), OFFSET_ZERO); + zot = it.next(); + assertEquals(zot.getDateTimeBefore(), LocalDateTime.of(1997, 10, 26, 2, 0)); + assertEquals(zot.getOffsetBefore(), OFFSET_PONE); + assertEquals(it.hasNext(), false); + } + + public void test_London_getTransitionRules() { + ZoneRules test = europeLondon(); + List rules = test.getTransitionRules(); + assertEquals(rules.size(), 2); + + ZoneOffsetTransitionRule in = rules.get(0); + assertEquals(in.getMonth(), Month.MARCH); + assertEquals(in.getDayOfMonthIndicator(), 25); // optimized from -1 + assertEquals(in.getDayOfWeek(), DayOfWeek.SUNDAY); + assertEquals(in.getLocalTime(), LocalTime.of(1, 0)); + assertEquals(in.getTimeDefinition(), TimeDefinition.UTC); + assertEquals(in.getStandardOffset(), OFFSET_ZERO); + assertEquals(in.getOffsetBefore(), OFFSET_ZERO); + assertEquals(in.getOffsetAfter(), OFFSET_PONE); + + ZoneOffsetTransitionRule out = rules.get(1); + assertEquals(out.getMonth(), Month.OCTOBER); + assertEquals(out.getDayOfMonthIndicator(), 25); // optimized from -1 + assertEquals(out.getDayOfWeek(), DayOfWeek.SUNDAY); + assertEquals(out.getLocalTime(), LocalTime.of(1, 0)); + assertEquals(out.getTimeDefinition(), TimeDefinition.UTC); + assertEquals(out.getStandardOffset(), OFFSET_ZERO); + assertEquals(out.getOffsetBefore(), OFFSET_PONE); + assertEquals(out.getOffsetAfter(), OFFSET_ZERO); + } + + //----------------------------------------------------------------------- + public void test_London_nextTransition_historic() { + ZoneRules test = europeLondon(); + List trans = test.getTransitions(); + + ZoneOffsetTransition first = trans.get(0); + assertEquals(test.nextTransition(first.getInstant().minusNanos(1)), first); + + for (int i = 0; i < trans.size() - 1; i++) { + ZoneOffsetTransition cur = trans.get(i); + ZoneOffsetTransition next = trans.get(i + 1); + + assertEquals(test.nextTransition(cur.getInstant()), next); + assertEquals(test.nextTransition(next.getInstant().minusNanos(1)), next); + } + } + + public void test_London_nextTransition_rulesBased() { + ZoneRules test = europeLondon(); + List rules = test.getTransitionRules(); + List trans = test.getTransitions(); + + ZoneOffsetTransition last = trans.get(trans.size() - 1); + assertEquals(test.nextTransition(last.getInstant()), rules.get(0).createTransition(1998)); + + for (int year = 1998; year < 2010; year++) { + ZoneOffsetTransition a = rules.get(0).createTransition(year); + ZoneOffsetTransition b = rules.get(1).createTransition(year); + ZoneOffsetTransition c = rules.get(0).createTransition(year + 1); + + assertEquals(test.nextTransition(a.getInstant()), b); + assertEquals(test.nextTransition(b.getInstant().minusNanos(1)), b); + + assertEquals(test.nextTransition(b.getInstant()), c); + assertEquals(test.nextTransition(c.getInstant().minusNanos(1)), c); + } + } + + public void test_London_nextTransition_lastYear() { + ZoneRules test = europeLondon(); + List rules = test.getTransitionRules(); + ZoneOffsetTransition zot = rules.get(1).createTransition(Year.MAX_VALUE); + assertEquals(test.nextTransition(zot.getInstant()), null); + } + + //----------------------------------------------------------------------- + public void test_London_previousTransition_historic() { + ZoneRules test = europeLondon(); + List trans = test.getTransitions(); + + ZoneOffsetTransition first = trans.get(0); + assertEquals(test.previousTransition(first.getInstant()), null); + assertEquals(test.previousTransition(first.getInstant().minusNanos(1)), null); + + for (int i = 0; i < trans.size() - 1; i++) { + ZoneOffsetTransition prev = trans.get(i); + ZoneOffsetTransition cur = trans.get(i + 1); + + assertEquals(test.previousTransition(cur.getInstant()), prev); + assertEquals(test.previousTransition(prev.getInstant().plusSeconds(1)), prev); + assertEquals(test.previousTransition(prev.getInstant().plusNanos(1)), prev); + } + } + + public void test_London_previousTransition_rulesBased() { + ZoneRules test = europeLondon(); + List rules = test.getTransitionRules(); + List trans = test.getTransitions(); + + ZoneOffsetTransition last = trans.get(trans.size() - 1); + assertEquals(test.previousTransition(last.getInstant().plusSeconds(1)), last); + assertEquals(test.previousTransition(last.getInstant().plusNanos(1)), last); + + // Jan 1st of year between transitions and rules + ZonedDateTime odt = ZonedDateTime.ofInstant(last.getInstant(), last.getOffsetAfter()); + odt = odt.withDayOfYear(1).plusYears(1).with(LocalTime.MIDNIGHT); + assertEquals(test.previousTransition(odt.toInstant()), last); + + // later years + for (int year = 1998; year < 2010; year++) { + ZoneOffsetTransition a = rules.get(0).createTransition(year); + ZoneOffsetTransition b = rules.get(1).createTransition(year); + ZoneOffsetTransition c = rules.get(0).createTransition(year + 1); + + assertEquals(test.previousTransition(c.getInstant()), b); + assertEquals(test.previousTransition(b.getInstant().plusSeconds(1)), b); + assertEquals(test.previousTransition(b.getInstant().plusNanos(1)), b); + + assertEquals(test.previousTransition(b.getInstant()), a); + assertEquals(test.previousTransition(a.getInstant().plusSeconds(1)), a); + assertEquals(test.previousTransition(a.getInstant().plusNanos(1)), a); + } + } + + //----------------------------------------------------------------------- + // Europe/Paris + //----------------------------------------------------------------------- + private ZoneRules europeParis() { + return ZoneId.of("Europe/Paris").getRules(); + } + + public void test_Paris() { + ZoneRules test = europeParis(); + assertEquals(test.isFixedOffset(), false); + } + + public void test_Paris_preTimeZones() { + ZoneRules test = europeParis(); + ZonedDateTime old = createZDT(1800, 1, 1, ZoneOffset.UTC); + Instant instant = old.toInstant(); + ZoneOffset offset = ZoneOffset.ofHoursMinutesSeconds(0, 9, 21); + assertEquals(test.getOffset(instant), offset); + checkOffset(test, old.getDateTime(), offset, 1); + assertEquals(test.getStandardOffset(instant), offset); + assertEquals(test.getDaylightSavings(instant), Duration.ZERO); + assertEquals(test.isDaylightSavings(instant), false); + } + + public void test_Paris_getOffset() { + ZoneRules test = europeParis(); + assertEquals(test.getOffset(createInstant(2008, 1, 1, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 2, 1, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 3, 1, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 4, 1, ZoneOffset.UTC)), OFFSET_PTWO); + assertEquals(test.getOffset(createInstant(2008, 5, 1, ZoneOffset.UTC)), OFFSET_PTWO); + assertEquals(test.getOffset(createInstant(2008, 6, 1, ZoneOffset.UTC)), OFFSET_PTWO); + assertEquals(test.getOffset(createInstant(2008, 7, 1, ZoneOffset.UTC)), OFFSET_PTWO); + assertEquals(test.getOffset(createInstant(2008, 8, 1, ZoneOffset.UTC)), OFFSET_PTWO); + assertEquals(test.getOffset(createInstant(2008, 9, 1, ZoneOffset.UTC)), OFFSET_PTWO); + assertEquals(test.getOffset(createInstant(2008, 10, 1, ZoneOffset.UTC)), OFFSET_PTWO); + assertEquals(test.getOffset(createInstant(2008, 11, 1, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 12, 1, ZoneOffset.UTC)), OFFSET_PONE); + } + + public void test_Paris_getOffset_toDST() { + ZoneRules test = europeParis(); + assertEquals(test.getOffset(createInstant(2008, 3, 24, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 3, 25, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 3, 26, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 3, 27, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 3, 28, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 3, 29, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 3, 30, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 3, 31, ZoneOffset.UTC)), OFFSET_PTWO); + // cutover at 01:00Z + assertEquals(test.getOffset(createInstant(2008, 3, 30, 0, 59, 59, 999999999, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 3, 30, 1, 0, 0, 0, ZoneOffset.UTC)), OFFSET_PTWO); + } + + public void test_Paris_getOffset_fromDST() { + ZoneRules test = europeParis(); + assertEquals(test.getOffset(createInstant(2008, 10, 24, ZoneOffset.UTC)), OFFSET_PTWO); + assertEquals(test.getOffset(createInstant(2008, 10, 25, ZoneOffset.UTC)), OFFSET_PTWO); + assertEquals(test.getOffset(createInstant(2008, 10, 26, ZoneOffset.UTC)), OFFSET_PTWO); + assertEquals(test.getOffset(createInstant(2008, 10, 27, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 10, 28, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 10, 29, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 10, 30, ZoneOffset.UTC)), OFFSET_PONE); + assertEquals(test.getOffset(createInstant(2008, 10, 31, ZoneOffset.UTC)), OFFSET_PONE); + // cutover at 01:00Z + assertEquals(test.getOffset(createInstant(2008, 10, 26, 0, 59, 59, 999999999, ZoneOffset.UTC)), OFFSET_PTWO); + assertEquals(test.getOffset(createInstant(2008, 10, 26, 1, 0, 0, 0, ZoneOffset.UTC)), OFFSET_PONE); + } + + public void test_Paris_getOffsetInfo() { + ZoneRules test = europeParis(); + checkOffset(test, createLDT(2008, 1, 1), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 2, 1), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 3, 1), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 4, 1), OFFSET_PTWO, 1); + checkOffset(test, createLDT(2008, 5, 1), OFFSET_PTWO, 1); + checkOffset(test, createLDT(2008, 6, 1), OFFSET_PTWO, 1); + checkOffset(test, createLDT(2008, 7, 1), OFFSET_PTWO, 1); + checkOffset(test, createLDT(2008, 8, 1), OFFSET_PTWO, 1); + checkOffset(test, createLDT(2008, 9, 1), OFFSET_PTWO, 1); + checkOffset(test, createLDT(2008, 10, 1), OFFSET_PTWO, 1); + checkOffset(test, createLDT(2008, 11, 1), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 12, 1), OFFSET_PONE, 1); + } + + public void test_Paris_getOffsetInfo_toDST() { + ZoneRules test = europeParis(); + checkOffset(test, createLDT(2008, 3, 24), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 3, 25), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 3, 26), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 3, 27), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 3, 28), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 3, 29), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 3, 30), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 3, 31), OFFSET_PTWO, 1); + // cutover at 01:00Z which is 02:00+01:00(local Paris time) + checkOffset(test, LocalDateTime.of(2008, 3, 30, 1, 59, 59, 999999999), OFFSET_PONE, 1); + checkOffset(test, LocalDateTime.of(2008, 3, 30, 3, 0, 0, 0), OFFSET_PTWO, 1); + } + + public void test_Paris_getOffsetInfo_fromDST() { + ZoneRules test = europeParis(); + checkOffset(test, createLDT(2008, 10, 24), OFFSET_PTWO, 1); + checkOffset(test, createLDT(2008, 10, 25), OFFSET_PTWO, 1); + checkOffset(test, createLDT(2008, 10, 26), OFFSET_PTWO, 1); + checkOffset(test, createLDT(2008, 10, 27), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 10, 28), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 10, 29), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 10, 30), OFFSET_PONE, 1); + checkOffset(test, createLDT(2008, 10, 31), OFFSET_PONE, 1); + // cutover at 01:00Z which is 02:00+01:00(local Paris time) + checkOffset(test, LocalDateTime.of(2008, 10, 26, 1, 59, 59, 999999999), OFFSET_PTWO, 1); + checkOffset(test, LocalDateTime.of(2008, 10, 26, 3, 0, 0, 0), OFFSET_PONE, 1); + } + + public void test_Paris_getOffsetInfo_gap() { + ZoneRules test = europeParis(); + final LocalDateTime dateTime = LocalDateTime.of(2008, 3, 30, 2, 0, 0, 0); + ZoneOffsetTransition trans = checkOffset(test, dateTime, OFFSET_PONE, GAP); + assertEquals(trans.isGap(), true); + assertEquals(trans.isOverlap(), false); + assertEquals(trans.getOffsetBefore(), OFFSET_PONE); + assertEquals(trans.getOffsetAfter(), OFFSET_PTWO); + assertEquals(trans.getInstant(), createInstant(2008, 3, 30, 1, 0, ZoneOffset.UTC)); + assertEquals(trans.isValidOffset(OFFSET_ZERO), false); + assertEquals(trans.isValidOffset(OFFSET_PONE), false); + assertEquals(trans.isValidOffset(OFFSET_PTWO), false); + assertEquals(trans.toString(), "Transition[Gap at 2008-03-30T02:00+01:00 to +02:00]"); + + assertFalse(trans.equals(null)); + assertFalse(trans.equals(OFFSET_PONE)); + assertTrue(trans.equals(trans)); + + final ZoneOffsetTransition otherTrans = test.getTransition(dateTime); + assertTrue(trans.equals(otherTrans)); + assertEquals(trans.hashCode(), otherTrans.hashCode()); + } + + public void test_Paris_getOffsetInfo_overlap() { + ZoneRules test = europeParis(); + final LocalDateTime dateTime = LocalDateTime.of(2008, 10, 26, 2, 0, 0, 0); + ZoneOffsetTransition trans = checkOffset(test, dateTime, OFFSET_PTWO, OVERLAP); + assertEquals(trans.isGap(), false); + assertEquals(trans.isOverlap(), true); + assertEquals(trans.getOffsetBefore(), OFFSET_PTWO); + assertEquals(trans.getOffsetAfter(), OFFSET_PONE); + assertEquals(trans.getInstant(), createInstant(2008, 10, 26, 1, 0, ZoneOffset.UTC)); + assertEquals(trans.isValidOffset(OFFSET_ZERO), false); + assertEquals(trans.isValidOffset(OFFSET_PONE), true); + assertEquals(trans.isValidOffset(OFFSET_PTWO), true); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(3)), false); + assertEquals(trans.toString(), "Transition[Overlap at 2008-10-26T03:00+02:00 to +01:00]"); + + assertFalse(trans.equals(null)); + assertFalse(trans.equals(OFFSET_PTWO)); + assertTrue(trans.equals(trans)); + + final ZoneOffsetTransition otherTrans = test.getTransition(dateTime); + assertTrue(trans.equals(otherTrans)); + assertEquals(trans.hashCode(), otherTrans.hashCode()); + } + + public void test_Paris_getStandardOffset() { + ZoneRules test = europeParis(); + ZonedDateTime zdt = createZDT(1840, 1, 1, ZoneOffset.UTC); + while (zdt.getYear() < 2010) { + Instant instant = zdt.toInstant(); + if (zdt.getDate().isBefore(LocalDate.of(1911, 3, 11))) { + assertEquals(test.getStandardOffset(instant), ZoneOffset.ofHoursMinutesSeconds(0, 9, 21)); + } else if (zdt.getDate().isBefore(LocalDate.of(1940, 6, 14))) { + assertEquals(test.getStandardOffset(instant), OFFSET_ZERO); + } else if (zdt.getDate().isBefore(LocalDate.of(1944, 8, 25))) { + assertEquals(test.getStandardOffset(instant), OFFSET_PONE); + } else if (zdt.getDate().isBefore(LocalDate.of(1945, 9, 16))) { + assertEquals(test.getStandardOffset(instant), OFFSET_ZERO); + } else { + assertEquals(test.getStandardOffset(instant), OFFSET_PONE); + } + zdt = zdt.plusMonths(6); + } + } + + //----------------------------------------------------------------------- + // America/New_York + //----------------------------------------------------------------------- + private ZoneRules americaNewYork() { + return ZoneId.of("America/New_York").getRules(); + } + + public void test_NewYork() { + ZoneRules test = americaNewYork(); + assertEquals(test.isFixedOffset(), false); + } + + public void test_NewYork_preTimeZones() { + ZoneRules test = americaNewYork(); + ZonedDateTime old = createZDT(1800, 1, 1, ZoneOffset.UTC); + Instant instant = old.toInstant(); + ZoneOffset offset = ZoneOffset.of("-04:56:02"); + assertEquals(test.getOffset(instant), offset); + checkOffset(test, old.getDateTime(), offset, 1); + assertEquals(test.getStandardOffset(instant), offset); + assertEquals(test.getDaylightSavings(instant), Duration.ZERO); + assertEquals(test.isDaylightSavings(instant), false); + } + + public void test_NewYork_getOffset() { + ZoneRules test = americaNewYork(); + ZoneOffset offset = ZoneOffset.ofHours(-5); + assertEquals(test.getOffset(createInstant(2008, 1, 1, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getOffset(createInstant(2008, 2, 1, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getOffset(createInstant(2008, 3, 1, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getOffset(createInstant(2008, 4, 1, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getOffset(createInstant(2008, 5, 1, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getOffset(createInstant(2008, 6, 1, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getOffset(createInstant(2008, 7, 1, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getOffset(createInstant(2008, 8, 1, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getOffset(createInstant(2008, 9, 1, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getOffset(createInstant(2008, 10, 1, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getOffset(createInstant(2008, 11, 1, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getOffset(createInstant(2008, 12, 1, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getOffset(createInstant(2008, 1, 28, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getOffset(createInstant(2008, 2, 28, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getOffset(createInstant(2008, 3, 28, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getOffset(createInstant(2008, 4, 28, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getOffset(createInstant(2008, 5, 28, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getOffset(createInstant(2008, 6, 28, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getOffset(createInstant(2008, 7, 28, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getOffset(createInstant(2008, 8, 28, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getOffset(createInstant(2008, 9, 28, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getOffset(createInstant(2008, 10, 28, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getOffset(createInstant(2008, 11, 28, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getOffset(createInstant(2008, 12, 28, offset)), ZoneOffset.ofHours(-5)); + } + + public void test_NewYork_getOffset_toDST() { + ZoneRules test = americaNewYork(); + ZoneOffset offset = ZoneOffset.ofHours(-5); + assertEquals(test.getOffset(createInstant(2008, 3, 8, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getOffset(createInstant(2008, 3, 9, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getOffset(createInstant(2008, 3, 10, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getOffset(createInstant(2008, 3, 11, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getOffset(createInstant(2008, 3, 12, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getOffset(createInstant(2008, 3, 13, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getOffset(createInstant(2008, 3, 14, offset)), ZoneOffset.ofHours(-4)); + // cutover at 02:00 local + assertEquals(test.getOffset(createInstant(2008, 3, 9, 1, 59, 59, 999999999, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getOffset(createInstant(2008, 3, 9, 2, 0, 0, 0, offset)), ZoneOffset.ofHours(-4)); + } + + public void test_NewYork_getOffset_fromDST() { + ZoneRules test = americaNewYork(); + ZoneOffset offset = ZoneOffset.ofHours(-4); + assertEquals(test.getOffset(createInstant(2008, 11, 1, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getOffset(createInstant(2008, 11, 2, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getOffset(createInstant(2008, 11, 3, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getOffset(createInstant(2008, 11, 4, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getOffset(createInstant(2008, 11, 5, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getOffset(createInstant(2008, 11, 6, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getOffset(createInstant(2008, 11, 7, offset)), ZoneOffset.ofHours(-5)); + // cutover at 02:00 local + assertEquals(test.getOffset(createInstant(2008, 11, 2, 1, 59, 59, 999999999, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getOffset(createInstant(2008, 11, 2, 2, 0, 0, 0, offset)), ZoneOffset.ofHours(-5)); + } + + public void test_NewYork_getOffsetInfo() { + ZoneRules test = americaNewYork(); + checkOffset(test, createLDT(2008, 1, 1), ZoneOffset.ofHours(-5), 1); + checkOffset(test, createLDT(2008, 2, 1), ZoneOffset.ofHours(-5), 1); + checkOffset(test, createLDT(2008, 3, 1), ZoneOffset.ofHours(-5), 1); + checkOffset(test, createLDT(2008, 4, 1), ZoneOffset.ofHours(-4), 1); + checkOffset(test, createLDT(2008, 5, 1), ZoneOffset.ofHours(-4), 1); + checkOffset(test, createLDT(2008, 6, 1), ZoneOffset.ofHours(-4), 1); + checkOffset(test, createLDT(2008, 7, 1), ZoneOffset.ofHours(-4), 1); + checkOffset(test, createLDT(2008, 8, 1), ZoneOffset.ofHours(-4), 1); + checkOffset(test, createLDT(2008, 9, 1), ZoneOffset.ofHours(-4), 1); + checkOffset(test, createLDT(2008, 10, 1), ZoneOffset.ofHours(-4), 1); + checkOffset(test, createLDT(2008, 11, 1), ZoneOffset.ofHours(-4), 1); + checkOffset(test, createLDT(2008, 12, 1), ZoneOffset.ofHours(-5), 1); + checkOffset(test, createLDT(2008, 1, 28), ZoneOffset.ofHours(-5), 1); + checkOffset(test, createLDT(2008, 2, 28), ZoneOffset.ofHours(-5), 1); + checkOffset(test, createLDT(2008, 3, 28), ZoneOffset.ofHours(-4), 1); + checkOffset(test, createLDT(2008, 4, 28), ZoneOffset.ofHours(-4), 1); + checkOffset(test, createLDT(2008, 5, 28), ZoneOffset.ofHours(-4), 1); + checkOffset(test, createLDT(2008, 6, 28), ZoneOffset.ofHours(-4), 1); + checkOffset(test, createLDT(2008, 7, 28), ZoneOffset.ofHours(-4), 1); + checkOffset(test, createLDT(2008, 8, 28), ZoneOffset.ofHours(-4), 1); + checkOffset(test, createLDT(2008, 9, 28), ZoneOffset.ofHours(-4), 1); + checkOffset(test, createLDT(2008, 10, 28), ZoneOffset.ofHours(-4), 1); + checkOffset(test, createLDT(2008, 11, 28), ZoneOffset.ofHours(-5), 1); + checkOffset(test, createLDT(2008, 12, 28), ZoneOffset.ofHours(-5), 1); + } + + public void test_NewYork_getOffsetInfo_toDST() { + ZoneRules test = americaNewYork(); + checkOffset(test, createLDT(2008, 3, 8), ZoneOffset.ofHours(-5), 1); + checkOffset(test, createLDT(2008, 3, 9), ZoneOffset.ofHours(-5), 1); + checkOffset(test, createLDT(2008, 3, 10), ZoneOffset.ofHours(-4), 1); + checkOffset(test, createLDT(2008, 3, 11), ZoneOffset.ofHours(-4), 1); + checkOffset(test, createLDT(2008, 3, 12), ZoneOffset.ofHours(-4), 1); + checkOffset(test, createLDT(2008, 3, 13), ZoneOffset.ofHours(-4), 1); + checkOffset(test, createLDT(2008, 3, 14), ZoneOffset.ofHours(-4), 1); + // cutover at 02:00 local + checkOffset(test, LocalDateTime.of(2008, 3, 9, 1, 59, 59, 999999999), ZoneOffset.ofHours(-5), 1); + checkOffset(test, LocalDateTime.of(2008, 3, 9, 3, 0, 0, 0), ZoneOffset.ofHours(-4), 1); + } + + public void test_NewYork_getOffsetInfo_fromDST() { + ZoneRules test = americaNewYork(); + checkOffset(test, createLDT(2008, 11, 1), ZoneOffset.ofHours(-4), 1); + checkOffset(test, createLDT(2008, 11, 2), ZoneOffset.ofHours(-4), 1); + checkOffset(test, createLDT(2008, 11, 3), ZoneOffset.ofHours(-5), 1); + checkOffset(test, createLDT(2008, 11, 4), ZoneOffset.ofHours(-5), 1); + checkOffset(test, createLDT(2008, 11, 5), ZoneOffset.ofHours(-5), 1); + checkOffset(test, createLDT(2008, 11, 6), ZoneOffset.ofHours(-5), 1); + checkOffset(test, createLDT(2008, 11, 7), ZoneOffset.ofHours(-5), 1); + // cutover at 02:00 local + checkOffset(test, LocalDateTime.of(2008, 11, 2, 0, 59, 59, 999999999), ZoneOffset.ofHours(-4), 1); + checkOffset(test, LocalDateTime.of(2008, 11, 2, 2, 0, 0, 0), ZoneOffset.ofHours(-5), 1); + } + + public void test_NewYork_getOffsetInfo_gap() { + ZoneRules test = americaNewYork(); + final LocalDateTime dateTime = LocalDateTime.of(2008, 3, 9, 2, 0, 0, 0); + ZoneOffsetTransition trans = checkOffset(test, dateTime, ZoneOffset.ofHours(-5), GAP); + assertEquals(trans.isGap(), true); + assertEquals(trans.isOverlap(), false); + assertEquals(trans.getOffsetBefore(), ZoneOffset.ofHours(-5)); + assertEquals(trans.getOffsetAfter(), ZoneOffset.ofHours(-4)); + assertEquals(trans.getInstant(), createInstant(2008, 3, 9, 2, 0, ZoneOffset.ofHours(-5))); + assertEquals(trans.isValidOffset(OFFSET_PTWO), false); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(-5)), false); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(-4)), false); + assertEquals(trans.toString(), "Transition[Gap at 2008-03-09T02:00-05:00 to -04:00]"); + + assertFalse(trans.equals(null)); + assertFalse(trans.equals(ZoneOffset.ofHours(-5))); + assertTrue(trans.equals(trans)); + + final ZoneOffsetTransition otherTrans = test.getTransition(dateTime); + assertTrue(trans.equals(otherTrans)); + assertEquals(trans.hashCode(), otherTrans.hashCode()); + } + + public void test_NewYork_getOffsetInfo_overlap() { + ZoneRules test = americaNewYork(); + final LocalDateTime dateTime = LocalDateTime.of(2008, 11, 2, 1, 0, 0, 0); + ZoneOffsetTransition trans = checkOffset(test, dateTime, ZoneOffset.ofHours(-4), OVERLAP); + assertEquals(trans.isGap(), false); + assertEquals(trans.isOverlap(), true); + assertEquals(trans.getOffsetBefore(), ZoneOffset.ofHours(-4)); + assertEquals(trans.getOffsetAfter(), ZoneOffset.ofHours(-5)); + assertEquals(trans.getInstant(), createInstant(2008, 11, 2, 2, 0, ZoneOffset.ofHours(-4))); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(-1)), false); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(-5)), true); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(-4)), true); + assertEquals(trans.isValidOffset(OFFSET_PTWO), false); + assertEquals(trans.toString(), "Transition[Overlap at 2008-11-02T02:00-04:00 to -05:00]"); + + assertFalse(trans.equals(null)); + assertFalse(trans.equals(ZoneOffset.ofHours(-4))); + assertTrue(trans.equals(trans)); + + final ZoneOffsetTransition otherTrans = test.getTransition(dateTime); + assertTrue(trans.equals(otherTrans)); + assertEquals(trans.hashCode(), otherTrans.hashCode()); + } + + public void test_NewYork_getStandardOffset() { + ZoneRules test = americaNewYork(); + ZonedDateTime dateTime = createZDT(1860, 1, 1, ZoneOffset.UTC); + while (dateTime.getYear() < 2010) { + Instant instant = dateTime.toInstant(); + if (dateTime.getDate().isBefore(LocalDate.of(1883, 11, 18))) { + assertEquals(test.getStandardOffset(instant), ZoneOffset.of("-04:56:02")); + } else { + assertEquals(test.getStandardOffset(instant), ZoneOffset.ofHours(-5)); + } + dateTime = dateTime.plusMonths(6); + } + } + + //----------------------------------------------------------------------- + // Kathmandu + //----------------------------------------------------------------------- + private ZoneRules asiaKathmandu() { + return ZoneId.of("Asia/Kathmandu").getRules(); + } + + public void test_Kathmandu_nextTransition_historic() { + ZoneRules test = asiaKathmandu(); + List trans = test.getTransitions(); + + ZoneOffsetTransition first = trans.get(0); + assertEquals(test.nextTransition(first.getInstant().minusNanos(1)), first); + + for (int i = 0; i < trans.size() - 1; i++) { + ZoneOffsetTransition cur = trans.get(i); + ZoneOffsetTransition next = trans.get(i + 1); + + assertEquals(test.nextTransition(cur.getInstant()), next); + assertEquals(test.nextTransition(next.getInstant().minusNanos(1)), next); + } + } + + public void test_Kathmandu_nextTransition_noRules() { + ZoneRules test = asiaKathmandu(); + List trans = test.getTransitions(); + + ZoneOffsetTransition last = trans.get(trans.size() - 1); + assertEquals(test.nextTransition(last.getInstant()), null); + } + + //------------------------------------------------------------------------- + @Test(expectedExceptions=UnsupportedOperationException.class) + public void test_getTransitions_immutable() { + ZoneRules test = europeParis(); + test.getTransitions().clear(); + } + + @Test(expectedExceptions=UnsupportedOperationException.class) + public void test_getTransitionRules_immutable() { + ZoneRules test = europeParis(); + test.getTransitionRules().clear(); + } + + //----------------------------------------------------------------------- + // equals() / hashCode() + //----------------------------------------------------------------------- + public void test_equals() { + ZoneRules test1 = europeLondon(); + ZoneRules test2 = europeParis(); + ZoneRules test2b = europeParis(); + assertEquals(test1.equals(test2), false); + assertEquals(test2.equals(test1), false); + + assertEquals(test1.equals(test1), true); + assertEquals(test2.equals(test2), true); + assertEquals(test2.equals(test2b), true); + + assertEquals(test1.hashCode() == test1.hashCode(), true); + assertEquals(test2.hashCode() == test2.hashCode(), true); + assertEquals(test2.hashCode() == test2b.hashCode(), true); + } + + public void test_equals_null() { + assertEquals(europeLondon().equals(null), false); + } + + public void test_equals_notZoneRules() { + assertEquals(europeLondon().equals("Europe/London"), false); + } + + public void test_toString() { + assertEquals(europeLondon().toString().contains("ZoneRules"), true); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + private Instant createInstant(int year, int month, int day, ZoneOffset offset) { + return LocalDateTime.of(year, month, day, 0, 0).toInstant(offset); + } + + private Instant createInstant(int year, int month, int day, int hour, int min, ZoneOffset offset) { + return LocalDateTime.of(year, month, day, hour, min).toInstant(offset); + } + + private Instant createInstant(int year, int month, int day, int hour, int min, int sec, int nano, ZoneOffset offset) { + return LocalDateTime.of(year, month, day, hour, min, sec, nano).toInstant(offset); + } + + private ZonedDateTime createZDT(int year, int month, int day, ZoneId zone) { + return LocalDateTime.of(year, month, day, 0, 0).atZone(zone); + } + + private LocalDateTime createLDT(int year, int month, int day) { + return LocalDateTime.of(year, month, day, 0, 0); + } + + private ZoneOffsetTransition checkOffset(ZoneRules rules, LocalDateTime dateTime, ZoneOffset offset, int type) { + List validOffsets = rules.getValidOffsets(dateTime); + assertEquals(validOffsets.size(), type); + assertEquals(rules.getOffset(dateTime), offset); + if (type == 1) { + assertEquals(validOffsets.get(0), offset); + return null; + } else { + ZoneOffsetTransition zot = rules.getTransition(dateTime); + assertNotNull(zot); + assertEquals(zot.isOverlap(), type == 2); + assertEquals(zot.isGap(), type == 0); + assertEquals(zot.isValidOffset(offset), type == 2); + return zot; + } + } + +} diff --git a/jdk/test/java/time/tck/java/time/zone/TCKZoneRulesProvider.java b/jdk/test/java/time/tck/java/time/zone/TCKZoneRulesProvider.java new file mode 100644 index 00000000000..2d245f66115 --- /dev/null +++ b/jdk/test/java/time/tck/java/time/zone/TCKZoneRulesProvider.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2009-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.zone; + +import java.time.zone.*; +import test.java.time.zone.*; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import java.util.Collections; +import java.util.HashSet; +import java.util.NavigableMap; +import java.util.Set; +import java.util.TreeMap; + +import java.time.ZoneOffset; + +import org.testng.annotations.Test; + +/** + * Test ZoneRulesProvider. + */ +@Test +public class TCKZoneRulesProvider { + + private static String TZDB_VERSION = "2012i"; + + //----------------------------------------------------------------------- + // getAvailableZoneIds() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_getAvailableGroupIds() { + Set zoneIds = ZoneRulesProvider.getAvailableZoneIds(); + assertEquals(zoneIds.contains("Europe/London"), true); + zoneIds.clear(); + assertEquals(zoneIds.size(), 0); + Set zoneIds2 = ZoneRulesProvider.getAvailableZoneIds(); + assertEquals(zoneIds2.contains("Europe/London"), true); + } + + //----------------------------------------------------------------------- + // getRules(String) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_getRules_String() { + ZoneRules rules = ZoneRulesProvider.getRules("Europe/London"); + assertNotNull(rules); + ZoneRules rules2 = ZoneRulesProvider.getRules("Europe/London"); + assertEquals(rules2, rules); + } + + @Test(groups={"tck"}, expectedExceptions=ZoneRulesException.class) + public void test_getRules_String_unknownId() { + ZoneRulesProvider.getRules("Europe/Lon"); + } + + @Test(groups={"tck"}, expectedExceptions=NullPointerException.class) + public void test_getRules_String_null() { + ZoneRulesProvider.getRules(null); + } + + //----------------------------------------------------------------------- + // getVersions(String) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_getVersions_String() { + NavigableMap versions = ZoneRulesProvider.getVersions("Europe/London"); + assertTrue(versions.size() >= 1); + ZoneRules rules = ZoneRulesProvider.getRules("Europe/London"); + assertEquals(versions.lastEntry().getValue(), rules); + + NavigableMap copy = new TreeMap<>(versions); + versions.clear(); + assertEquals(versions.size(), 0); + NavigableMap versions2 = ZoneRulesProvider.getVersions("Europe/London"); + assertEquals(versions2, copy); + } + + @Test(groups={"tck"}, expectedExceptions=ZoneRulesException.class) + public void test_getVersions_String_unknownId() { + ZoneRulesProvider.getVersions("Europe/Lon"); + } + + @Test(groups={"tck"}, expectedExceptions=NullPointerException.class) + public void test_getVersions_String_null() { + ZoneRulesProvider.getVersions(null); + } + + //----------------------------------------------------------------------- + // refresh() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_refresh() { + assertEquals(ZoneRulesProvider.refresh(), false); + } + + //----------------------------------------------------------------------- + // registerProvider() + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_registerProvider() { + Set pre = ZoneRulesProvider.getAvailableZoneIds(); + assertEquals(pre.contains("FooLocation"), false); + ZoneRulesProvider.registerProvider(new MockTempProvider()); + assertEquals(pre.contains("FooLocation"), false); + Set post = ZoneRulesProvider.getAvailableZoneIds(); + assertEquals(post.contains("FooLocation"), true); + + assertEquals(ZoneRulesProvider.getRules("FooLocation"), ZoneOffset.of("+01:45").getRules()); + } + + static class MockTempProvider extends ZoneRulesProvider { + final ZoneRules rules = ZoneOffset.of("+01:45").getRules(); + @Override + public Set provideZoneIds() { + return new HashSet(Collections.singleton("FooLocation")); + } + @Override + protected ZoneRulesProvider provideBind(String zoneId) { + return this; + } + @Override + protected NavigableMap provideVersions(String zoneId) { + NavigableMap result = new TreeMap<>(); + result.put("BarVersion", rules); + return result; + } + @Override + protected ZoneRules provideRules(String zoneId) { + if (zoneId.equals("FooLocation")) { + return rules; + } + throw new ZoneRulesException("Invalid"); + } + } + +} diff --git a/jdk/test/java/time/test/java/time/AbstractTest.java b/jdk/test/java/time/test/java/time/AbstractTest.java new file mode 100644 index 00000000000..6d4d1340964 --- /dev/null +++ b/jdk/test/java/time/test/java/time/AbstractTest.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2011-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +/** + * Base test class. + */ +public abstract class AbstractTest { + + protected static boolean isIsoLeap(long year) { + if (year % 4 != 0) { + return false; + } + if (year % 100 == 0 && year % 400 != 0) { + return false; + } + return true; + } + + protected static void assertSerializable(Object o) throws IOException, ClassNotFoundException { + Object deserialisedObject = writeThenRead(o); + assertEquals(deserialisedObject, o); + } + + protected static void assertSerializableAndSame(Object o) throws IOException, ClassNotFoundException { + Object deserialisedObject = writeThenRead(o); + assertSame(deserialisedObject, o); + } + + private static Object writeThenRead(Object o) throws IOException, ClassNotFoundException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (ObjectOutputStream oos = new ObjectOutputStream(baos) ) { + oos.writeObject(o); + } + + try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) { + return ois.readObject(); + } + } + + protected static void assertImmutable(Class cls) { + assertTrue(Modifier.isPublic(cls.getModifiers())); + assertTrue(Modifier.isFinal(cls.getModifiers())); + Field[] fields = cls.getDeclaredFields(); + for (Field field : fields) { + if (field.getName().contains("$") == false) { + if (Modifier.isStatic(field.getModifiers())) { + assertTrue(Modifier.isFinal(field.getModifiers()), "Field:" + field.getName()); + } else { + assertTrue(Modifier.isPrivate(field.getModifiers()), "Field:" + field.getName()); + assertTrue(Modifier.isFinal(field.getModifiers()), "Field:" + field.getName()); + } + } + } + Constructor[] cons = cls.getDeclaredConstructors(); + for (Constructor con : cons) { + assertTrue(Modifier.isPrivate(con.getModifiers())); + } + } + +} diff --git a/jdk/test/java/time/test/java/time/MockSimplePeriod.java b/jdk/test/java/time/test/java/time/MockSimplePeriod.java new file mode 100644 index 00000000000..1c5d8726bed --- /dev/null +++ b/jdk/test/java/time/test/java/time/MockSimplePeriod.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time; + +import java.time.*; + +import static java.time.temporal.ChronoUnit.DAYS; +import static java.time.temporal.ChronoUnit.FOREVER; +import static java.time.temporal.ChronoUnit.SECONDS; + +import java.util.Objects; + +import java.time.temporal.Temporal; +import java.time.temporal.TemporalSubtractor; +import java.time.temporal.TemporalAdder; +import java.time.temporal.TemporalUnit; + +/** + * Mock period of time measured using a single unit, such as {@code 3 Days}. + */ +public final class MockSimplePeriod + implements TemporalAdder, TemporalSubtractor, Comparable { + + /** + * A constant for a period of zero, measured in days. + */ + public static final MockSimplePeriod ZERO_DAYS = new MockSimplePeriod(0, DAYS); + /** + * A constant for a period of zero, measured in seconds. + */ + public static final MockSimplePeriod ZERO_SECONDS = new MockSimplePeriod(0, SECONDS); + + /** + * The amount of the period. + */ + private final long amount; + /** + * The unit the period is measured in. + */ + private final TemporalUnit unit; + + /** + * Obtains a {@code MockSimplePeriod} from an amount and unit. + *

    + * The parameters represent the two parts of a phrase like '6 Days'. + * + * @param amount the amount of the period, measured in terms of the unit, positive or negative + * @param unit the unit that the period is measured in, must not be the 'Forever' unit, not null + * @return the {@code MockSimplePeriod} instance, not null + * @throws DateTimeException if the period unit is {@link java.time.temporal.ChronoUnit#FOREVER}. + */ + public static MockSimplePeriod of(long amount, TemporalUnit unit) { + return new MockSimplePeriod(amount, unit); + } + + private MockSimplePeriod(long amount, TemporalUnit unit) { + Objects.requireNonNull(unit, "unit"); + if (unit == FOREVER) { + throw new DateTimeException("Cannot create a period of the Forever unit"); + } + this.amount = amount; + this.unit = unit; + } + + //----------------------------------------------------------------------- + public long getAmount() { + return amount; + } + + public TemporalUnit getUnit() { + return unit; + } + + //------------------------------------------------------------------------- + @Override + public Temporal addTo(Temporal temporal) { + return temporal.plus(amount, unit); + } + + @Override + public Temporal subtractFrom(Temporal temporal) { + return temporal.minus(amount, unit); + } + + //----------------------------------------------------------------------- + @Override + public int compareTo(MockSimplePeriod otherPeriod) { + if (unit.equals(otherPeriod.getUnit()) == false) { + throw new IllegalArgumentException("Units cannot be compared: " + unit + " and " + otherPeriod.getUnit()); + } + return Long.compare(amount, otherPeriod.amount); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof MockSimplePeriod) { + MockSimplePeriod other = (MockSimplePeriod) obj; + return this.amount == other.amount && + this.unit.equals(other.unit); + } + return false; + } + + @Override + public int hashCode() { + return unit.hashCode() ^ (int) (amount ^ (amount >>> 32)); + } + + @Override + public String toString() { + return amount + " " + unit.getName(); + } + +} diff --git a/jdk/test/java/time/test/java/time/TestClock_Fixed.java b/jdk/test/java/time/test/java/time/TestClock_Fixed.java new file mode 100644 index 00000000000..cf07ceea07b --- /dev/null +++ b/jdk/test/java/time/test/java/time/TestClock_Fixed.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012 Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertSame; + +import java.time.Clock; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; + +import org.testng.annotations.Test; + +/** + * Test fixed clock. + */ +@Test +public class TestClock_Fixed { + + private static final ZoneId PARIS = ZoneId.of("Europe/Paris"); + private static final Instant INSTANT = LocalDateTime.of(2008, 6, 30, 11, 30, 10, 500).atZone(ZoneOffset.ofHours(2)).toInstant(); + + //------------------------------------------------------------------------- + public void test_withZone_same() { + Clock test = Clock.fixed(INSTANT, PARIS); + Clock changed = test.withZone(PARIS); + assertSame(test, changed); + } + + //----------------------------------------------------------------------- + public void test_toString() { + Clock test = Clock.fixed(INSTANT, PARIS); + assertEquals(test.toString(), "FixedClock[2008-06-30T09:30:10.000000500Z,Europe/Paris]"); + } + +} diff --git a/jdk/test/java/time/test/java/time/TestClock_Offset.java b/jdk/test/java/time/test/java/time/TestClock_Offset.java new file mode 100644 index 00000000000..ed62300e681 --- /dev/null +++ b/jdk/test/java/time/test/java/time/TestClock_Offset.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012 Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertSame; + +import java.time.Clock; +import java.time.Duration; +import java.time.ZoneId; + +import org.testng.annotations.Test; + +/** + * Test offset clock. + */ +@Test +public class TestClock_Offset { + + private static final ZoneId PARIS = ZoneId.of("Europe/Paris"); + private static final Duration OFFSET = Duration.ofSeconds(2); + + //------------------------------------------------------------------------- + public void test_withZone_same() { + Clock test = Clock.offset(Clock.system(PARIS), OFFSET); + Clock changed = test.withZone(PARIS); + assertSame(test, changed); + } + + //----------------------------------------------------------------------- + public void test_toString() { + Clock test = Clock.offset(Clock.systemUTC(), OFFSET); + assertEquals(test.toString(), "OffsetClock[SystemClock[Z],PT2S]"); + } + +} diff --git a/jdk/test/java/time/test/java/time/TestClock_System.java b/jdk/test/java/time/test/java/time/TestClock_System.java new file mode 100644 index 00000000000..00e9bbffe84 --- /dev/null +++ b/jdk/test/java/time/test/java/time/TestClock_System.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012 Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time; + +import java.time.*; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertSame; + +import org.testng.annotations.Test; + +/** + * Test system clock. + */ +@Test +public class TestClock_System { + + private static final ZoneId PARIS = ZoneId.of("Europe/Paris"); + + public void test_withZone_same() { + Clock test = Clock.system(PARIS); + Clock changed = test.withZone(PARIS); + assertSame(test, changed); + } + + //----------------------------------------------------------------------- + public void test_toString() { + Clock test = Clock.system(PARIS); + assertEquals(test.toString(), "SystemClock[Europe/Paris]"); + } + +} diff --git a/jdk/test/java/time/test/java/time/TestClock_Tick.java b/jdk/test/java/time/test/java/time/TestClock_Tick.java new file mode 100644 index 00000000000..0e8fdca4e05 --- /dev/null +++ b/jdk/test/java/time/test/java/time/TestClock_Tick.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012 Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertSame; + +import java.time.Clock; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; + +import org.testng.annotations.Test; + +/** + * Test tick clock. + */ +@Test +public class TestClock_Tick { + + private static final ZoneId MOSCOW = ZoneId.of("Europe/Moscow"); + private static final ZoneId PARIS = ZoneId.of("Europe/Paris"); + private static final ZonedDateTime ZDT = LocalDateTime.of(2008, 6, 30, 11, 30, 10, 500).atZone(ZoneOffset.ofHours(2)); + + //------------------------------------------------------------------------- + public void test_withZone_same() { + Clock test = Clock.tick(Clock.system(PARIS), Duration.ofMillis(500)); + Clock changed = test.withZone(PARIS); + assertSame(test, changed); + } + + //----------------------------------------------------------------------- + public void test_toString() { + Clock test = Clock.tick(Clock.systemUTC(), Duration.ofMillis(500)); + assertEquals(test.toString(), "TickClock[SystemClock[Z],PT0.5S]"); + } + +} diff --git a/jdk/test/java/time/test/java/time/TestDuration.java b/jdk/test/java/time/test/java/time/TestDuration.java new file mode 100644 index 00000000000..2b2d8105003 --- /dev/null +++ b/jdk/test/java/time/test/java/time/TestDuration.java @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +import java.time.Duration; + +import org.testng.annotations.Test; + +/** + * Test Duration. + */ +@Test +public class TestDuration extends AbstractTest { + + //----------------------------------------------------------------------- + @Test + public void test_immutable() { + assertImmutable(Duration.class); + } + + //----------------------------------------------------------------------- + @Test(groups={"implementation"}) + public void test_interfaces() { + assertTrue(Serializable.class.isAssignableFrom(Duration.class)); + assertTrue(Comparable.class.isAssignableFrom(Duration.class)); + } + + //----------------------------------------------------------------------- + // serialization + //----------------------------------------------------------------------- + @Test(groups={"implementation"}) + public void test_deserializationSingleton() throws Exception { + Duration orginal = Duration.ZERO; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(baos); + out.writeObject(orginal); + out.close(); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ObjectInputStream in = new ObjectInputStream(bais); + Duration ser = (Duration) in.readObject(); + assertSame(ser, Duration.ZERO); + } + + @Test(groups={"implementation"}) + public void plus_zeroReturnsThis() { + Duration t = Duration.ofSeconds(-1); + assertSame(t.plus(Duration.ZERO), t); + } + + @Test(groups={"implementation"}) + public void plus_zeroSingleton() { + Duration t = Duration.ofSeconds(-1); + assertSame(t.plus(Duration.ofSeconds(1)), Duration.ZERO); + } + + @Test(groups={"implementation"}) + public void plusSeconds_zeroReturnsThis() { + Duration t = Duration.ofSeconds(-1); + assertSame(t.plusSeconds(0), t); + } + + @Test(groups={"implementation"}) + public void plusSeconds_zeroSingleton() { + Duration t = Duration.ofSeconds(-1); + assertSame(t.plusSeconds(1), Duration.ZERO); + } + + @Test(groups={"implementation"}) + public void plusMillis_zeroReturnsThis() { + Duration t = Duration.ofSeconds(-1, 2000000); + assertSame(t.plusMillis(0), t); + } + + @Test(groups={"implementation"}) + public void plusMillis_zeroSingleton() { + Duration t = Duration.ofSeconds(-1, 2000000); + assertSame(t.plusMillis(998), Duration.ZERO); + } + + @Test(groups={"implementation"}) + public void plusNanos_zeroReturnsThis() { + Duration t = Duration.ofSeconds(-1, 2000000); + assertSame(t.plusNanos(0), t); + } + + @Test(groups={"implementation"}) + public void plusNanos_zeroSingleton() { + Duration t = Duration.ofSeconds(-1, 2000000); + assertSame(t.plusNanos(998000000), Duration.ZERO); + } + + @Test(groups={"implementation"}) + public void minus_zeroReturnsThis() { + Duration t = Duration.ofSeconds(1); + assertSame(t.minus(Duration.ZERO), t); + } + + @Test(groups={"implementation"}) + public void minus_zeroSingleton() { + Duration t = Duration.ofSeconds(1); + assertSame(t.minus(Duration.ofSeconds(1)), Duration.ZERO); + } + + @Test(groups={"implementation"}) + public void minusSeconds_zeroReturnsThis() { + Duration t = Duration.ofSeconds(1); + assertSame(t.minusSeconds(0), t); + } + + @Test(groups={"implementation"}) + public void minusSeconds_zeroSingleton() { + Duration t = Duration.ofSeconds(1); + assertSame(t.minusSeconds(1), Duration.ZERO); + } + + @Test(groups={"implementation"}) + public void minusMillis_zeroReturnsThis() { + Duration t = Duration.ofSeconds(1, 2000000); + assertSame(t.minusMillis(0), t); + } + + @Test(groups={"implementation"}) + public void minusMillis_zeroSingleton() { + Duration t = Duration.ofSeconds(1, 2000000); + assertSame(t.minusMillis(1002), Duration.ZERO); + } + + @Test(groups={"implementation"}) + public void minusNanos_zeroReturnsThis() { + Duration t = Duration.ofSeconds(1, 2000000); + assertSame(t.minusNanos(0), t); + } + + @Test(groups={"implementation"}) + public void minusNanos_zeroSingleton() { + Duration t = Duration.ofSeconds(1, 2000000); + assertSame(t.minusNanos(1002000000), Duration.ZERO); + } + + @Test(groups={"implementation"}) + public void test_abs_same() { + Duration base = Duration.ofSeconds(12); + assertSame(base.abs(), base); + } + + void doTest_comparisons_Duration(Duration... durations) { + for (int i = 0; i < durations.length; i++) { + Duration a = durations[i]; + for (int j = 0; j < durations.length; j++) { + Duration b = durations[j]; + if (i < j) { + assertEquals(a.compareTo(b)< 0, true, a + " <=> " + b); + assertEquals(a.isLessThan(b), true, a + " <=> " + b); + assertEquals(a.isGreaterThan(b), false, a + " <=> " + b); + assertEquals(a.equals(b), false, a + " <=> " + b); + } else if (i > j) { + assertEquals(a.compareTo(b) > 0, true, a + " <=> " + b); + assertEquals(a.isLessThan(b), false, a + " <=> " + b); + assertEquals(a.isGreaterThan(b), true, a + " <=> " + b); + assertEquals(a.equals(b), false, a + " <=> " + b); + } else { + assertEquals(a.compareTo(b), 0, a + " <=> " + b); + assertEquals(a.isLessThan(b), false, a + " <=> " + b); + assertEquals(a.isGreaterThan(b), false, a + " <=> " + b); + assertEquals(a.equals(b), true, a + " <=> " + b); + } + } + } + } + +} diff --git a/jdk/test/java/time/test/java/time/TestInstant.java b/jdk/test/java/time/test/java/time/TestInstant.java new file mode 100644 index 00000000000..cf135a1ae2d --- /dev/null +++ b/jdk/test/java/time/test/java/time/TestInstant.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time; + +import java.time.Instant; + +import org.testng.annotations.Test; + +/** + * Test Instant. + */ +@Test +public class TestInstant extends AbstractTest { + + @Test + public void test_immutable() { + assertImmutable(Instant.class); + } + +} diff --git a/jdk/test/java/time/test/java/time/TestLocalDate.java b/jdk/test/java/time/test/java/time/TestLocalDate.java new file mode 100644 index 00000000000..44033123cdf --- /dev/null +++ b/jdk/test/java/time/test/java/time/TestLocalDate.java @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time; + +import static java.time.temporal.ChronoField.YEAR; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; + +import java.time.LocalDate; +import java.time.Month; +import java.time.temporal.ChronoUnit; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test LocalDate. + */ +@Test +public class TestLocalDate extends AbstractTest { + + private LocalDate TEST_2007_07_15; + + @BeforeMethod(groups={"tck", "implementation"}) + public void setUp() { + TEST_2007_07_15 = LocalDate.of(2007, 7, 15); + } + + //----------------------------------------------------------------------- + @Test + public void test_immutable() { + assertImmutable(LocalDate.class); + } + + //----------------------------------------------------------------------- + // Since plusDays/minusDays actually depends on MJDays, it cannot be used for testing + private LocalDate next(LocalDate date) { + int newDayOfMonth = date.getDayOfMonth() + 1; + if (newDayOfMonth <= date.getMonth().length(isIsoLeap(date.getYear()))) { + return date.withDayOfMonth(newDayOfMonth); + } + date = date.withDayOfMonth(1); + if (date.getMonth() == Month.DECEMBER) { + date = date.withYear(date.getYear() + 1); + } + return date.with(date.getMonth().plus(1)); + } + + private LocalDate previous(LocalDate date) { + int newDayOfMonth = date.getDayOfMonth() - 1; + if (newDayOfMonth > 0) { + return date.withDayOfMonth(newDayOfMonth); + } + date = date.with(date.getMonth().minus(1)); + if (date.getMonth() == Month.DECEMBER) { + date = date.withYear(date.getYear() - 1); + } + return date.withDayOfMonth(date.getMonth().length(isIsoLeap(date.getYear()))); + } + + @Test(groups={"implementation"}) + public void test_with_DateTimeField_long_noChange_same() { + LocalDate t = TEST_2007_07_15.with(YEAR, 2007); + assertSame(t, TEST_2007_07_15); + } + + @Test(groups={"implementation"}) + public void test_withYear_int_noChange_same() { + LocalDate t = TEST_2007_07_15.withYear(2007); + assertSame(t, TEST_2007_07_15); + } + + @Test(groups={"implementation"}) + public void test_withMonth_int_noChange_same() { + LocalDate t = TEST_2007_07_15.withMonth(7); + assertSame(t, TEST_2007_07_15); + } + + @Test(groups={"implementation"}) + public void test_withDayOfMonth_noChange_same() { + LocalDate t = TEST_2007_07_15.withDayOfMonth(15); + assertSame(t, TEST_2007_07_15); + } + + @Test(groups={"implementation"}) + public void test_withDayOfYear_noChange_same() { + LocalDate t = TEST_2007_07_15.withDayOfYear(31 + 28 + 31 + 30 + 31 + 30 + 15); + assertSame(t, TEST_2007_07_15); + } + + @Test(groups={"implementation"}) + public void test_plus_Period_zero() { + LocalDate t = TEST_2007_07_15.plus(MockSimplePeriod.ZERO_DAYS); + assertSame(t, TEST_2007_07_15); + } + + @Test(groups={"implementation"}) + public void test_plus_longPeriodUnit_zero() { + LocalDate t = TEST_2007_07_15.plus(0, ChronoUnit.DAYS); + assertSame(t, TEST_2007_07_15); + } + + @Test(groups={"implementation"}) + public void test_plusYears_long_noChange_same() { + LocalDate t = TEST_2007_07_15.plusYears(0); + assertSame(t, TEST_2007_07_15); + } + + @Test(groups={"implementation"}) + public void test_plusMonths_long_noChange_same() { + LocalDate t = TEST_2007_07_15.plusMonths(0); + assertSame(t, TEST_2007_07_15); + } + + //----------------------------------------------------------------------- + // plusWeeks() + //----------------------------------------------------------------------- + @DataProvider(name="samplePlusWeeksSymmetry") + Object[][] provider_samplePlusWeeksSymmetry() { + return new Object[][] { + {LocalDate.of(-1, 1, 1)}, + {LocalDate.of(-1, 2, 28)}, + {LocalDate.of(-1, 3, 1)}, + {LocalDate.of(-1, 12, 31)}, + {LocalDate.of(0, 1, 1)}, + {LocalDate.of(0, 2, 28)}, + {LocalDate.of(0, 2, 29)}, + {LocalDate.of(0, 3, 1)}, + {LocalDate.of(0, 12, 31)}, + {LocalDate.of(2007, 1, 1)}, + {LocalDate.of(2007, 2, 28)}, + {LocalDate.of(2007, 3, 1)}, + {LocalDate.of(2007, 12, 31)}, + {LocalDate.of(2008, 1, 1)}, + {LocalDate.of(2008, 2, 28)}, + {LocalDate.of(2008, 2, 29)}, + {LocalDate.of(2008, 3, 1)}, + {LocalDate.of(2008, 12, 31)}, + {LocalDate.of(2099, 1, 1)}, + {LocalDate.of(2099, 2, 28)}, + {LocalDate.of(2099, 3, 1)}, + {LocalDate.of(2099, 12, 31)}, + {LocalDate.of(2100, 1, 1)}, + {LocalDate.of(2100, 2, 28)}, + {LocalDate.of(2100, 3, 1)}, + {LocalDate.of(2100, 12, 31)}, + }; + } + + @Test(dataProvider="samplePlusWeeksSymmetry", groups={"implementation"}) + public void test_plusWeeks_symmetry(LocalDate reference) { + for (int weeks = 0; weeks < 365 * 8; weeks++) { + LocalDate t = reference.plusWeeks(weeks).plusWeeks(-weeks); + assertEquals(t, reference); + + t = reference.plusWeeks(-weeks).plusWeeks(weeks); + assertEquals(t, reference); + } + } + + @Test(groups={"implementation"}) + public void test_plusWeeks_noChange_same() { + LocalDate t = TEST_2007_07_15.plusWeeks(0); + assertSame(t, TEST_2007_07_15); + } + + //----------------------------------------------------------------------- + // plusDays() + //----------------------------------------------------------------------- + @DataProvider(name="samplePlusDaysSymmetry") + Object[][] provider_samplePlusDaysSymmetry() { + return new Object[][] { + {LocalDate.of(-1, 1, 1)}, + {LocalDate.of(-1, 2, 28)}, + {LocalDate.of(-1, 3, 1)}, + {LocalDate.of(-1, 12, 31)}, + {LocalDate.of(0, 1, 1)}, + {LocalDate.of(0, 2, 28)}, + {LocalDate.of(0, 2, 29)}, + {LocalDate.of(0, 3, 1)}, + {LocalDate.of(0, 12, 31)}, + {LocalDate.of(2007, 1, 1)}, + {LocalDate.of(2007, 2, 28)}, + {LocalDate.of(2007, 3, 1)}, + {LocalDate.of(2007, 12, 31)}, + {LocalDate.of(2008, 1, 1)}, + {LocalDate.of(2008, 2, 28)}, + {LocalDate.of(2008, 2, 29)}, + {LocalDate.of(2008, 3, 1)}, + {LocalDate.of(2008, 12, 31)}, + {LocalDate.of(2099, 1, 1)}, + {LocalDate.of(2099, 2, 28)}, + {LocalDate.of(2099, 3, 1)}, + {LocalDate.of(2099, 12, 31)}, + {LocalDate.of(2100, 1, 1)}, + {LocalDate.of(2100, 2, 28)}, + {LocalDate.of(2100, 3, 1)}, + {LocalDate.of(2100, 12, 31)}, + }; + } + + @Test(dataProvider="samplePlusDaysSymmetry", groups={"implementation"}) + public void test_plusDays_symmetry(LocalDate reference) { + for (int days = 0; days < 365 * 8; days++) { + LocalDate t = reference.plusDays(days).plusDays(-days); + assertEquals(t, reference); + + t = reference.plusDays(-days).plusDays(days); + assertEquals(t, reference); + } + } + + @Test(groups={"implementation"}) + public void test_plusDays_noChange_same() { + LocalDate t = TEST_2007_07_15.plusDays(0); + assertSame(t, TEST_2007_07_15); + } + + @Test(groups={"implementation"}) + public void test_minus_Period_zero() { + LocalDate t = TEST_2007_07_15.minus(MockSimplePeriod.ZERO_DAYS); + assertSame(t, TEST_2007_07_15); + } + + @Test(groups={"implementation"}) + public void test_minus_longPeriodUnit_zero() { + LocalDate t = TEST_2007_07_15.minus(0, ChronoUnit.DAYS); + assertSame(t, TEST_2007_07_15); + } + + @Test(groups={"implementation"}) + public void test_minusYears_long_noChange_same() { + LocalDate t = TEST_2007_07_15.minusYears(0); + assertSame(t, TEST_2007_07_15); + } + + @Test(groups={"implementation"}) + public void test_minusMonths_long_noChange_same() { + LocalDate t = TEST_2007_07_15.minusMonths(0); + assertSame(t, TEST_2007_07_15); + } + + //----------------------------------------------------------------------- + // minusWeeks() + //----------------------------------------------------------------------- + @DataProvider(name="sampleMinusWeeksSymmetry") + Object[][] provider_sampleMinusWeeksSymmetry() { + return new Object[][] { + {LocalDate.of(-1, 1, 1)}, + {LocalDate.of(-1, 2, 28)}, + {LocalDate.of(-1, 3, 1)}, + {LocalDate.of(-1, 12, 31)}, + {LocalDate.of(0, 1, 1)}, + {LocalDate.of(0, 2, 28)}, + {LocalDate.of(0, 2, 29)}, + {LocalDate.of(0, 3, 1)}, + {LocalDate.of(0, 12, 31)}, + {LocalDate.of(2007, 1, 1)}, + {LocalDate.of(2007, 2, 28)}, + {LocalDate.of(2007, 3, 1)}, + {LocalDate.of(2007, 12, 31)}, + {LocalDate.of(2008, 1, 1)}, + {LocalDate.of(2008, 2, 28)}, + {LocalDate.of(2008, 2, 29)}, + {LocalDate.of(2008, 3, 1)}, + {LocalDate.of(2008, 12, 31)}, + {LocalDate.of(2099, 1, 1)}, + {LocalDate.of(2099, 2, 28)}, + {LocalDate.of(2099, 3, 1)}, + {LocalDate.of(2099, 12, 31)}, + {LocalDate.of(2100, 1, 1)}, + {LocalDate.of(2100, 2, 28)}, + {LocalDate.of(2100, 3, 1)}, + {LocalDate.of(2100, 12, 31)}, + }; + } + + @Test(dataProvider="sampleMinusWeeksSymmetry", groups={"implementation"}) + public void test_minusWeeks_symmetry(LocalDate reference) { + for (int weeks = 0; weeks < 365 * 8; weeks++) { + LocalDate t = reference.minusWeeks(weeks).minusWeeks(-weeks); + assertEquals(t, reference); + + t = reference.minusWeeks(-weeks).minusWeeks(weeks); + assertEquals(t, reference); + } + } + + @Test(groups={"implementation"}) + public void test_minusWeeks_noChange_same() { + LocalDate t = TEST_2007_07_15.minusWeeks(0); + assertSame(t, TEST_2007_07_15); + } + + //----------------------------------------------------------------------- + // minusDays() + //----------------------------------------------------------------------- + @DataProvider(name="sampleMinusDaysSymmetry") + Object[][] provider_sampleMinusDaysSymmetry() { + return new Object[][] { + {LocalDate.of(-1, 1, 1)}, + {LocalDate.of(-1, 2, 28)}, + {LocalDate.of(-1, 3, 1)}, + {LocalDate.of(-1, 12, 31)}, + {LocalDate.of(0, 1, 1)}, + {LocalDate.of(0, 2, 28)}, + {LocalDate.of(0, 2, 29)}, + {LocalDate.of(0, 3, 1)}, + {LocalDate.of(0, 12, 31)}, + {LocalDate.of(2007, 1, 1)}, + {LocalDate.of(2007, 2, 28)}, + {LocalDate.of(2007, 3, 1)}, + {LocalDate.of(2007, 12, 31)}, + {LocalDate.of(2008, 1, 1)}, + {LocalDate.of(2008, 2, 28)}, + {LocalDate.of(2008, 2, 29)}, + {LocalDate.of(2008, 3, 1)}, + {LocalDate.of(2008, 12, 31)}, + {LocalDate.of(2099, 1, 1)}, + {LocalDate.of(2099, 2, 28)}, + {LocalDate.of(2099, 3, 1)}, + {LocalDate.of(2099, 12, 31)}, + {LocalDate.of(2100, 1, 1)}, + {LocalDate.of(2100, 2, 28)}, + {LocalDate.of(2100, 3, 1)}, + {LocalDate.of(2100, 12, 31)}, + }; + } + + @Test(dataProvider="sampleMinusDaysSymmetry", groups={"implementation"}) + public void test_minusDays_symmetry(LocalDate reference) { + for (int days = 0; days < 365 * 8; days++) { + LocalDate t = reference.minusDays(days).minusDays(-days); + assertEquals(t, reference); + + t = reference.minusDays(-days).minusDays(days); + assertEquals(t, reference); + } + } + + @Test(groups={"implementation"}) + public void test_minusDays_noChange_same() { + LocalDate t = TEST_2007_07_15.minusDays(0); + assertSame(t, TEST_2007_07_15); + } + + @Test(groups={"implementation"}) + public void test_toEpochDay_fromMJDays_symmetry() { + long date_0000_01_01 = -678941 - 40587; + + LocalDate test = LocalDate.of(0, 1, 1); + for (long i = date_0000_01_01; i < 700000; i++) { + assertEquals(LocalDate.ofEpochDay(test.toEpochDay()), test); + test = next(test); + } + test = LocalDate.of(0, 1, 1); + for (long i = date_0000_01_01; i > -2000000; i--) { + assertEquals(LocalDate.ofEpochDay(test.toEpochDay()), test); + test = previous(test); + } + } + + void doTest_comparisons_LocalDate(LocalDate... localDates) { + for (int i = 0; i < localDates.length; i++) { + LocalDate a = localDates[i]; + for (int j = 0; j < localDates.length; j++) { + LocalDate b = localDates[j]; + if (i < j) { + assertTrue(a.compareTo(b) < 0, a + " <=> " + b); + assertEquals(a.isBefore(b), true, a + " <=> " + b); + assertEquals(a.isAfter(b), false, a + " <=> " + b); + assertEquals(a.equals(b), false, a + " <=> " + b); + } else if (i > j) { + assertTrue(a.compareTo(b) > 0, a + " <=> " + b); + assertEquals(a.isBefore(b), false, a + " <=> " + b); + assertEquals(a.isAfter(b), true, a + " <=> " + b); + assertEquals(a.equals(b), false, a + " <=> " + b); + } else { + assertEquals(a.compareTo(b), 0, a + " <=> " + b); + assertEquals(a.isBefore(b), false, a + " <=> " + b); + assertEquals(a.isAfter(b), false, a + " <=> " + b); + assertEquals(a.equals(b), true, a + " <=> " + b); + } + } + } + } + +} diff --git a/jdk/test/java/time/test/java/time/TestLocalDateTime.java b/jdk/test/java/time/test/java/time/TestLocalDateTime.java new file mode 100644 index 00000000000..d850be50715 --- /dev/null +++ b/jdk/test/java/time/test/java/time/TestLocalDateTime.java @@ -0,0 +1,570 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Period; +import java.time.temporal.ChronoUnit; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test LocalDateTime. + */ +@Test +public class TestLocalDateTime extends AbstractTest { + + private LocalDateTime TEST_2007_07_15_12_30_40_987654321 = LocalDateTime.of(2007, 7, 15, 12, 30, 40, 987654321); + + //----------------------------------------------------------------------- + @Test + public void test_immutable() { + assertImmutable(LocalDateTime.class); + } + + //----------------------------------------------------------------------- + @DataProvider(name="sampleDates") + Object[][] provider_sampleDates() { + return new Object[][] { + {2008, 7, 5}, + {2007, 7, 5}, + {2006, 7, 5}, + {2005, 7, 5}, + {2004, 1, 1}, + {-1, 1, 2}, + }; + } + + @DataProvider(name="sampleTimes") + Object[][] provider_sampleTimes() { + return new Object[][] { + {0, 0, 0, 0}, + {0, 0, 0, 1}, + {0, 0, 1, 0}, + {0, 0, 1, 1}, + {0, 1, 0, 0}, + {0, 1, 0, 1}, + {0, 1, 1, 0}, + {0, 1, 1, 1}, + {1, 0, 0, 0}, + {1, 0, 0, 1}, + {1, 0, 1, 0}, + {1, 0, 1, 1}, + {1, 1, 0, 0}, + {1, 1, 0, 1}, + {1, 1, 1, 0}, + {1, 1, 1, 1}, + }; + } + + @Test(groups={"implementation"}) + public void test_withYear_int_noChange() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.withYear(2007); + assertSame(t.getDate(), TEST_2007_07_15_12_30_40_987654321.getDate()); + assertSame(t.getTime(), TEST_2007_07_15_12_30_40_987654321.getTime()); + } + + @Test(groups={"implementation"}) + public void test_withMonth_int_noChange() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.withMonth(7); + assertSame(t.getDate(), TEST_2007_07_15_12_30_40_987654321.getDate()); + assertSame(t.getTime(), TEST_2007_07_15_12_30_40_987654321.getTime()); + } + + @Test(groups={"implementation"}) + public void test_withDayOfMonth_noChange() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.withDayOfMonth(15); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_withDayOfYear_noChange() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.withDayOfYear(31 + 28 + 31 + 30 + 31 + 30 + 15); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_withHour_noChange() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.withHour(12); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_withHour_toMidnight() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(1, 0)).withHour(0); + assertSame(t.getTime(), LocalTime.MIDNIGHT); + } + + @Test(groups={"implementation"}) + public void test_withHour_toMidday() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(1, 0)).withHour(12); + assertSame(t.getTime(), LocalTime.NOON); + } + + @Test(groups={"implementation"}) + public void test_withMinute_noChange() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.withMinute(30); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_withMinute_toMidnight() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(0, 1)).withMinute(0); + assertSame(t.getTime(), LocalTime.MIDNIGHT); + } + + @Test(groups={"implementation"}) + public void test_withMinute_toMidday() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(12, 1)).withMinute(0); + assertSame(t.getTime(), LocalTime.NOON); + } + + @Test(groups={"implementation"}) + public void test_withSecond_noChange() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.withSecond(40); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_withSecond_toMidnight() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(0, 0, 1)).withSecond(0); + assertSame(t.getTime(), LocalTime.MIDNIGHT); + } + + @Test(groups={"implementation"}) + public void test_withSecond_toMidday() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(12, 0, 1)).withSecond(0); + assertSame(t.getTime(), LocalTime.NOON); + } + + @Test(groups={"implementation"}) + public void test_withNanoOfSecond_noChange() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.withNano(987654321); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_withNanoOfSecond_toMidnight() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(0, 0, 0, 1)).withNano(0); + assertSame(t.getTime(), LocalTime.MIDNIGHT); + } + + @Test(groups={"implementation"}) + public void test_withNanoOfSecond_toMidday() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(12, 0, 0, 1)).withNano(0); + assertSame(t.getTime(), LocalTime.NOON); + } + + @Test(groups={"implementation"}) + public void test_plus_adjuster_zero() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plus(Period.ZERO); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_plus_Period_zero() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plus(MockSimplePeriod.ZERO_DAYS); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_plus_longPeriodUnit_zero() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plus(0, ChronoUnit.DAYS); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_plusYears_int_noChange() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusYears(0); + assertSame(TEST_2007_07_15_12_30_40_987654321, t); + } + + @Test(groups={"implementation"}) + public void test_plusMonths_int_noChange() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusMonths(0); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_plusWeeks_noChange() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusWeeks(0); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_plusDays_noChange() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusDays(0); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_plusHours_noChange() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusHours(0); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_plusHours_toMidnight() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(23, 0)).plusHours(1); + assertSame(t.getTime(), LocalTime.MIDNIGHT); + } + + @Test(groups={"implementation"}) + public void test_plusHours_toMidday() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(11, 0)).plusHours(1); + assertSame(t.getTime(), LocalTime.NOON); + } + + @Test(groups={"implementation"}) + public void test_plusMinutes_noChange() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusMinutes(0); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_plusMinutes_noChange_oneDay_same() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusMinutes(24 * 60); + assertSame(t.getTime(), TEST_2007_07_15_12_30_40_987654321.getTime()); + } + + @Test(groups={"implementation"}) + public void test_plusMinutes_toMidnight() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(23, 59)).plusMinutes(1); + assertSame(t.getTime(), LocalTime.MIDNIGHT); + } + + @Test(groups={"implementation"}) + public void test_plusMinutes_toMidday() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(11, 59)).plusMinutes(1); + assertSame(t.getTime(), LocalTime.NOON); + } + + @Test(groups={"implementation"}) + public void test_plusSeconds_noChange() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusSeconds(0); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_plusSeconds_noChange_oneDay_same() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusSeconds(24 * 60 * 60); + assertSame(t.getTime(), TEST_2007_07_15_12_30_40_987654321.getTime()); + } + + @Test(groups={"implementation"}) + public void test_plusSeconds_toMidnight() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(23, 59, 59)).plusSeconds(1); + assertSame(t.getTime(), LocalTime.MIDNIGHT); + } + + @Test(groups={"implementation"}) + public void test_plusSeconds_toMidday() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(11, 59, 59)).plusSeconds(1); + assertSame(t.getTime(), LocalTime.NOON); + } + + @Test(groups={"implementation"}) + public void test_plusNanos_noChange() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusNanos(0); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_plusNanos_noChange_oneDay_same() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.plusNanos(24 * 60 * 60 * 1000000000L); + assertSame(t.getTime(), TEST_2007_07_15_12_30_40_987654321.getTime()); + } + + @Test(groups={"implementation"}) + public void test_plusNanos_toMidnight() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(23, 59, 59, 999999999)).plusNanos(1); + assertSame(t.getTime(), LocalTime.MIDNIGHT); + } + + @Test(groups={"implementation"}) + public void test_plusNanos_toMidday() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(11, 59, 59, 999999999)).plusNanos(1); + assertSame(t.getTime(), LocalTime.NOON); + } + + @Test(groups={"implementation"}) + public void test_minus_adjuster_zero() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minus(Period.ZERO); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_minus_Period_zero() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minus(MockSimplePeriod.ZERO_DAYS); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_minus_longPeriodUnit_zero() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minus(0, ChronoUnit.DAYS); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_minusYears_int_noChange() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusYears(0); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_minusMonths_int_noChange() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusMonths(0); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_minusWeeks_noChange() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusWeeks(0); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_minusDays_noChange() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusDays(0); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_minusHours_noChange() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusHours(0); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_minusHours_toMidnight() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(1, 0)).minusHours(1); + assertSame(t.getTime(), LocalTime.MIDNIGHT); + } + + @Test(groups={"implementation"}) + public void test_minusHours_toMidday() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(13, 0)).minusHours(1); + assertSame(t.getTime(), LocalTime.NOON); + } + + @Test(groups={"implementation"}) + public void test_minusMinutes_noChange() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusMinutes(0); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_minusMinutes_noChange_oneDay_same() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusMinutes(24 * 60); + assertSame(t.getTime(), TEST_2007_07_15_12_30_40_987654321.getTime()); + } + + @Test(groups={"implementation"}) + public void test_minusMinutes_toMidnight() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(0, 1)).minusMinutes(1); + assertSame(t.getTime(), LocalTime.MIDNIGHT); + } + + @Test(groups={"implementation"}) + public void test_minusMinutes_toMidday() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(12, 1)).minusMinutes(1); + assertSame(t.getTime(), LocalTime.NOON); + } + + @Test(groups={"implementation"}) + public void test_minusSeconds_noChange() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusSeconds(0); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_minusSeconds_noChange_oneDay() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusSeconds(24 * 60 * 60); + assertEquals(t.getDate(), TEST_2007_07_15_12_30_40_987654321.getDate().minusDays(1)); + assertSame(t.getTime(), TEST_2007_07_15_12_30_40_987654321.getTime()); + } + + @Test(groups={"implementation"}) + public void test_minusSeconds_toMidnight() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(0, 0, 1)).minusSeconds(1); + assertSame(t.getTime(), LocalTime.MIDNIGHT); + } + + @Test(groups={"implementation"}) + public void test_minusSeconds_toMidday() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(12, 0, 1)).minusSeconds(1); + assertSame(t.getTime(), LocalTime.NOON); + } + + @Test(groups={"implementation"}) + public void test_minusNanos_noChange() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusNanos(0); + assertSame(t, TEST_2007_07_15_12_30_40_987654321); + } + + @Test(groups={"implementation"}) + public void test_minusNanos_noChange_oneDay() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.minusNanos(24 * 60 * 60 * 1000000000L); + assertEquals(t.getDate(), TEST_2007_07_15_12_30_40_987654321.getDate().minusDays(1)); + assertSame(t.getTime(), TEST_2007_07_15_12_30_40_987654321.getTime()); + } + + @Test(groups={"implementation"}) + public void test_minusNanos_toMidnight() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(0, 0, 0, 1)).minusNanos(1); + assertSame(t.getTime(), LocalTime.MIDNIGHT); + } + + @Test(groups={"implementation"}) + public void test_minusNanos_toMidday() { + LocalDateTime t = TEST_2007_07_15_12_30_40_987654321.with(LocalTime.of(12, 0, 0, 1)).minusNanos(1); + assertSame(t.getTime(), LocalTime.NOON); + } + + //----------------------------------------------------------------------- + // getDate() + //----------------------------------------------------------------------- + @Test(dataProvider="sampleDates", groups={"implementation"}) + public void test_getDate(int year, int month, int day) { + LocalDate d = LocalDate.of(year, month, day); + LocalDateTime dt = LocalDateTime.of(d, LocalTime.MIDNIGHT); + assertSame(dt.getDate(), d); + } + + //----------------------------------------------------------------------- + // getTime() + //----------------------------------------------------------------------- + @Test(dataProvider="sampleTimes", groups={"implementation"}) + public void test_getTime(int h, int m, int s, int ns) { + LocalTime t = LocalTime.of(h, m, s, ns); + LocalDateTime dt = LocalDateTime.of(LocalDate.of(2011, 7, 30), t); + assertSame(dt.getTime(), t); + } + + void test_comparisons_LocalDateTime(LocalDate... localDates) { + test_comparisons_LocalDateTime( + localDates, + LocalTime.MIDNIGHT, + LocalTime.of(0, 0, 0, 999999999), + LocalTime.of(0, 0, 59, 0), + LocalTime.of(0, 0, 59, 999999999), + LocalTime.of(0, 59, 0, 0), + LocalTime.of(0, 59, 59, 999999999), + LocalTime.NOON, + LocalTime.of(12, 0, 0, 999999999), + LocalTime.of(12, 0, 59, 0), + LocalTime.of(12, 0, 59, 999999999), + LocalTime.of(12, 59, 0, 0), + LocalTime.of(12, 59, 59, 999999999), + LocalTime.of(23, 0, 0, 0), + LocalTime.of(23, 0, 0, 999999999), + LocalTime.of(23, 0, 59, 0), + LocalTime.of(23, 0, 59, 999999999), + LocalTime.of(23, 59, 0, 0), + LocalTime.of(23, 59, 59, 999999999) + ); + } + + void test_comparisons_LocalDateTime(LocalDate[] localDates, LocalTime... localTimes) { + LocalDateTime[] localDateTimes = new LocalDateTime[localDates.length * localTimes.length]; + int i = 0; + + for (LocalDate localDate : localDates) { + for (LocalTime localTime : localTimes) { + localDateTimes[i++] = LocalDateTime.of(localDate, localTime); + } + } + + doTest_comparisons_LocalDateTime(localDateTimes); + } + + void doTest_comparisons_LocalDateTime(LocalDateTime[] localDateTimes) { + for (int i = 0; i < localDateTimes.length; i++) { + LocalDateTime a = localDateTimes[i]; + for (int j = 0; j < localDateTimes.length; j++) { + LocalDateTime b = localDateTimes[j]; + if (i < j) { + assertTrue(a.compareTo(b) < 0, a + " <=> " + b); + assertEquals(a.isBefore(b), true, a + " <=> " + b); + assertEquals(a.isAfter(b), false, a + " <=> " + b); + assertEquals(a.equals(b), false, a + " <=> " + b); + } else if (i > j) { + assertTrue(a.compareTo(b) > 0, a + " <=> " + b); + assertEquals(a.isBefore(b), false, a + " <=> " + b); + assertEquals(a.isAfter(b), true, a + " <=> " + b); + assertEquals(a.equals(b), false, a + " <=> " + b); + } else { + assertEquals(a.compareTo(b), 0, a + " <=> " + b); + assertEquals(a.isBefore(b), false, a + " <=> " + b); + assertEquals(a.isAfter(b), false, a + " <=> " + b); + assertEquals(a.equals(b), true, a + " <=> " + b); + } + } + } + } + +} diff --git a/jdk/test/java/time/test/java/time/TestLocalTime.java b/jdk/test/java/time/test/java/time/TestLocalTime.java new file mode 100644 index 00000000000..0a0b27796b1 --- /dev/null +++ b/jdk/test/java/time/test/java/time/TestLocalTime.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertSame; + +import java.time.LocalTime; + +import org.testng.annotations.Test; + +/** + * Test LocalTime. + */ +@Test +public class TestLocalTime extends AbstractTest { + + //----------------------------------------------------------------------- + @Test + public void test_immutable() { + assertImmutable(LocalTime.class); + } + + //----------------------------------------------------------------------- + private void check(LocalTime time, int h, int m, int s, int n) { + assertEquals(time.getHour(), h); + assertEquals(time.getMinute(), m); + assertEquals(time.getSecond(), s); + assertEquals(time.getNano(), n); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck","implementation"}) + public void constant_MIDNIGHT() { + check(LocalTime.MIDNIGHT, 0, 0, 0, 0); + } + + @Test(groups={"implementation"}) + public void constant_MIDNIGHT_same() { + assertSame(LocalTime.MIDNIGHT, LocalTime.MIDNIGHT); + assertSame(LocalTime.MIDNIGHT, LocalTime.of(0, 0)); + } + + @Test(groups={"tck","implementation"}) + public void constant_MIDDAY() { + check(LocalTime.NOON, 12, 0, 0, 0); + } + + @Test(groups={"implementation"}) + public void constant_MIDDAY_same() { + assertSame(LocalTime.NOON, LocalTime.NOON); + assertSame(LocalTime.NOON, LocalTime.of(12, 0)); + } + + //----------------------------------------------------------------------- + @Test(groups={"tck","implementation"}) + public void constant_MIN_TIME() { + check(LocalTime.MIN, 0, 0, 0, 0); + } + + @Test(groups={"implementation"}) + public void constant_MIN_TIME_same() { + assertSame(LocalTime.MIN, LocalTime.of(0, 0)); + } + + @Test(groups={"tck","implementation"}) + public void constant_MAX_TIME() { + check(LocalTime.MAX, 23, 59, 59, 999999999); + } + + @Test(groups={"implementation"}) + public void constant_MAX_TIME_same() { + assertSame(LocalTime.NOON, LocalTime.NOON); + assertSame(LocalTime.NOON, LocalTime.of(12, 0)); + } + + @Test(groups={"implementation"}) + public void factory_time_2ints_singletons() { + for (int i = 0; i < 24; i++) { + LocalTime test1 = LocalTime.of(i, 0); + LocalTime test2 = LocalTime.of(i, 0); + assertSame(test1, test2); + } + } + + @Test(groups={"implementation"}) + public void factory_time_3ints_singletons() { + for (int i = 0; i < 24; i++) { + LocalTime test1 = LocalTime.of(i, 0, 0); + LocalTime test2 = LocalTime.of(i, 0, 0); + assertSame(test1, test2); + } + } + + @Test(groups={"implementation"}) + public void factory_time_4ints_singletons() { + for (int i = 0; i < 24; i++) { + LocalTime test1 = LocalTime.of(i, 0, 0, 0); + LocalTime test2 = LocalTime.of(i, 0, 0, 0); + assertSame(test1, test2); + } + } + + @Test(groups={"implementation"}) + public void factory_ofSecondOfDay_singletons() { + for (int i = 0; i < 24; i++) { + LocalTime test1 = LocalTime.ofSecondOfDay(i * 60L * 60L); + LocalTime test2 = LocalTime.of(i, 0); + assertSame(test1, test2); + } + } + + @Test(groups={"implementation"}) + public void factory_ofSecondOfDay7_long_int_singletons() { + for (int i = 0; i < 24; i++) { + LocalTime test1 = LocalTime.ofSecondOfDay(i * 60L * 60L, 0); + LocalTime test2 = LocalTime.of(i, 0); + assertSame(test1, test2); + } + } + + @Test(groups={"implementation"}) + public void factory_ofNanoOfDay_singletons() { + for (int i = 0; i < 24; i++) { + LocalTime test1 = LocalTime.ofNanoOfDay(i * 1000000000L * 60L * 60L); + LocalTime test2 = LocalTime.of(i, 0); + assertSame(test1, test2); + } + } + +} diff --git a/jdk/test/java/time/test/java/time/TestPeriod.java b/jdk/test/java/time/test/java/time/TestPeriod.java new file mode 100644 index 00000000000..f7caf9e334b --- /dev/null +++ b/jdk/test/java/time/test/java/time/TestPeriod.java @@ -0,0 +1,1575 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time; + +import static java.time.temporal.ChronoUnit.DAYS; +import static java.time.temporal.ChronoUnit.HALF_DAYS; +import static java.time.temporal.ChronoUnit.HOURS; +import static java.time.temporal.ChronoUnit.MICROS; +import static java.time.temporal.ChronoUnit.MILLIS; +import static java.time.temporal.ChronoUnit.MINUTES; +import static java.time.temporal.ChronoUnit.MONTHS; +import static java.time.temporal.ChronoUnit.NANOS; +import static java.time.temporal.ChronoUnit.SECONDS; +import static java.time.temporal.ChronoUnit.YEARS; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +import java.time.DateTimeException; +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.Month; +import java.time.Period; +import java.time.temporal.YearMonth; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test. + */ +@Test +public class TestPeriod extends AbstractTest { + + //----------------------------------------------------------------------- + // basics + //----------------------------------------------------------------------- + public void test_interfaces() { + assertTrue(Serializable.class.isAssignableFrom(Period.class)); + } + + @DataProvider(name="serialization") + Object[][] data_serialization() { + return new Object[][] { + {Period.ZERO}, + {Period.of(0, DAYS)}, + {Period.of(1, DAYS)}, + {Period.of(1, 2, 3, 4, 5, 6)}, + }; + } + + @Test(dataProvider="serialization") + public void test_serialization(Period period) throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(period); + oos.close(); + + ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream( + baos.toByteArray())); + if (period.isZero()) { + assertSame(ois.readObject(), period); + } else { + assertEquals(ois.readObject(), period); + } + } + + @Test + public void test_immutable() { + assertImmutable(Period.class); + } + + //----------------------------------------------------------------------- + // factories + //----------------------------------------------------------------------- + public void factory_zeroSingleton() { + assertSame(Period.ZERO, Period.ZERO); + assertSame(Period.of(0, 0, 0, 0, 0, 0), Period.ZERO); + assertSame(Period.of(0, 0, 0, 0, 0, 0, 0), Period.ZERO); + assertSame(Period.ofDate(0, 0, 0), Period.ZERO); + assertSame(Period.ofTime(0, 0, 0), Period.ZERO); + assertSame(Period.ofTime(0, 0, 0, 0), Period.ZERO); + assertSame(Period.of(0, YEARS), Period.ZERO); + assertSame(Period.of(0, MONTHS), Period.ZERO); + assertSame(Period.of(0, DAYS), Period.ZERO); + assertSame(Period.of(0, HOURS), Period.ZERO); + assertSame(Period.of(0, MINUTES), Period.ZERO); + assertSame(Period.of(0, SECONDS), Period.ZERO); + assertSame(Period.of(0, NANOS), Period.ZERO); + } + + //----------------------------------------------------------------------- + // of(PeriodProvider) + //----------------------------------------------------------------------- + public void factory_of_ints() { + assertPeriod(Period.of(1, 2, 3, 4, 5, 6), 1, 2, 3, 4, 5, 6, 0); + assertPeriod(Period.of(0, 2, 3, 4, 5, 6), 0, 2, 3, 4, 5, 6, 0); + assertPeriod(Period.of(1, 0, 0, 0, 0, 0), 1, 0, 0, 0, 0, 0, 0); + assertPeriod(Period.of(0, 0, 0, 0, 0, 0), 0, 0, 0, 0, 0, 0, 0); + assertPeriod(Period.of(-1, -2, -3, -4, -5, -6), -1, -2, -3, -4, -5, -6, 0); + } + + //----------------------------------------------------------------------- + // ofDate + //----------------------------------------------------------------------- + public void factory_ofDate_ints() { + assertPeriod(Period.ofDate(1, 2, 3), 1, 2, 3, 0, 0, 0, 0); + assertPeriod(Period.ofDate(0, 2, 3), 0, 2, 3, 0, 0, 0, 0); + assertPeriod(Period.ofDate(1, 0, 0), 1, 0, 0, 0, 0, 0, 0); + assertPeriod(Period.ofDate(0, 0, 0), 0, 0, 0, 0, 0, 0, 0); + assertPeriod(Period.ofDate(-1, -2, -3), -1, -2, -3, 0, 0, 0, 0); + } + + //----------------------------------------------------------------------- + // ofTime + //----------------------------------------------------------------------- + public void factory_ofTime_3ints() { + assertPeriod(Period.ofTime(1, 2, 3), 0, 0, 0, 1, 2, 3, 0); + assertPeriod(Period.ofTime(0, 2, 3), 0, 0, 0, 0, 2, 3, 0); + assertPeriod(Period.ofTime(1, 0, 0), 0, 0, 0, 1, 0, 0, 0); + assertPeriod(Period.ofTime(0, 0, 0), 0, 0, 0, 0, 0, 0, 0); + assertPeriod(Period.ofTime(-1, -2, -3), 0, 0, 0, -1, -2, -3, 0); + } + + public void factory_ofTime_4ints() { + assertPeriod(Period.ofTime(1, 2, 3, 4), 0, 0, 0, 1, 2, 3, 4); + assertPeriod(Period.ofTime(0, 2, 3, 4), 0, 0, 0, 0, 2, 3, 4); + assertPeriod(Period.ofTime(1, 0, 0, 0), 0, 0, 0, 1, 0, 0, 0); + assertPeriod(Period.ofTime(0, 0, 0, 0), 0, 0, 0, 0, 0, 0, 0); + assertPeriod(Period.ofTime(-1, -2, -3, -4), 0, 0, 0, -1, -2, -3, -4); + } + + //----------------------------------------------------------------------- + // of one field + //----------------------------------------------------------------------- + public void test_factory_of_intPeriodUnit() { + assertEquals(Period.of(1, YEARS), Period.of(1, YEARS)); + assertEquals(Period.of(2, MONTHS), Period.of(2, MONTHS)); + assertEquals(Period.of(3, DAYS), Period.of(3, DAYS)); + + assertEquals(Period.of(1, HALF_DAYS), Period.of(12, HOURS)); + assertEquals(Period.of(Integer.MAX_VALUE / (3600 * 8), HOURS), Period.of(Integer.MAX_VALUE / (3600 * 8), HOURS)); + assertEquals(Period.of(-1, MINUTES), Period.of(-1, MINUTES)); + assertEquals(Period.of(-2, SECONDS), Period.of(-2, SECONDS)); + assertEquals(Period.of(Integer.MIN_VALUE, NANOS), Period.of(Integer.MIN_VALUE, NANOS)); + assertEquals(Period.of(2, MILLIS), Period.of(2000000, NANOS)); + assertEquals(Period.of(2, MICROS), Period.of(2000, NANOS)); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_factory_of_intPeriodUnit_null() { + Period.of(1, null); + } + + //----------------------------------------------------------------------- + public void factory_years() { + assertPeriod(Period.of(1, YEARS), 1, 0, 0, 0, 0, 0, 0); + assertPeriod(Period.of(0, YEARS), 0, 0, 0, 0, 0, 0, 0); + assertPeriod(Period.of(-1, YEARS), -1, 0, 0, 0, 0, 0, 0); + assertPeriod(Period.of(Integer.MAX_VALUE, YEARS), Integer.MAX_VALUE, 0, 0, 0, 0, 0, 0); + assertPeriod(Period.of(Integer.MIN_VALUE, YEARS), Integer.MIN_VALUE, 0, 0, 0, 0, 0, 0); + } + + public void factory_months() { + assertPeriod(Period.of(1, MONTHS), 0, 1, 0, 0, 0, 0, 0); + assertPeriod(Period.of(0, MONTHS), 0, 0, 0, 0, 0, 0, 0); + assertPeriod(Period.of(-1, MONTHS), 0, -1, 0, 0, 0, 0, 0); + assertPeriod(Period.of(Integer.MAX_VALUE, MONTHS), 0, Integer.MAX_VALUE, 0, 0, 0, 0, 0); + assertPeriod(Period.of(Integer.MIN_VALUE, MONTHS), 0, Integer.MIN_VALUE, 0, 0, 0, 0, 0); + } + + public void factory_days() { + assertPeriod(Period.of(1, DAYS), 0, 0, 1, 0, 0, 0, 0); + assertPeriod(Period.of(0, DAYS), 0, 0, 0, 0, 0, 0, 0); + assertPeriod(Period.of(-1, DAYS), 0, 0, -1, 0, 0, 0, 0); + assertPeriod(Period.of(Integer.MAX_VALUE, DAYS), 0, 0, Integer.MAX_VALUE, 0, 0, 0, 0); + assertPeriod(Period.of(Integer.MIN_VALUE, DAYS), 0, 0, Integer.MIN_VALUE, 0, 0, 0, 0); + } + + public void factory_hours() { + assertPeriod(Period.of(1, HOURS), 0, 0, 0, 1, 0, 0, 0); + assertPeriod(Period.of(0, HOURS), 0, 0, 0, 0, 0, 0, 0); + assertPeriod(Period.of(-1, HOURS), 0, 0, 0, -1, 0, 0, 0); + assertPeriod(Period.of(Integer.MAX_VALUE / (3600 * 8), HOURS), 0, 0, 0, Integer.MAX_VALUE / (3600 * 8), 0, 0, 0); + assertPeriod(Period.of(Integer.MIN_VALUE / (3600 * 8), HOURS), 0, 0, 0, Integer.MIN_VALUE / (3600 * 8), 0, 0, 0); + } + + public void factory_minutes() { + assertPeriod(Period.of(1, MINUTES), 0, 0, 0, 0, 1, 0, 0); + assertPeriod(Period.of(0, MINUTES), 0, 0, 0, 0, 0, 0, 0); + assertPeriod(Period.of(-1, MINUTES), 0, 0, 0, 0, -1, 0, 0); + int val = Integer.MAX_VALUE / (60 * 8); + assertPeriod(Period.of(val, MINUTES), 0, 0, 0, + (int) (val / 60L), + (int) (val % 60), + 0, 0); + val = Integer.MIN_VALUE / (60 * 8); + assertPeriod(Period.of(val, MINUTES), 0, 0, 0, + (int) (val / 60L), + (int) (val % 60), + 0, 0); + } + + public void factory_seconds() { + assertPeriod(Period.of(1, SECONDS), 0, 0, 0, 0, 0, 1, 0); + assertPeriod(Period.of(0, SECONDS), 0, 0, 0, 0, 0, 0, 0); + assertPeriod(Period.of(-1, SECONDS), 0, 0, 0, 0, 0, -1, 0); + assertPeriod(Period.of(Integer.MAX_VALUE, SECONDS), 0, 0, 0, + (int) (Integer.MAX_VALUE / 3600L), + (int) ((Integer.MAX_VALUE / 60L) % 60), + (int) (Integer.MAX_VALUE % 60), + 0); + assertPeriod(Period.of(Integer.MIN_VALUE, SECONDS), 0, 0, 0, + (int) (Integer.MIN_VALUE / 3600L), + (int) ((Integer.MIN_VALUE / 60L) % 60), + (int) (Integer.MIN_VALUE % 60), + 0); + } + + public void factory_nanos() { + assertPeriod(Period.of(1, NANOS), 0, 0, 0, 0, 0, 0, 1); + assertPeriod(Period.of(0, NANOS), 0, 0, 0, 0, 0, 0, 0); + assertPeriod(Period.of(-1, NANOS), 0, 0, 0, 0, 0, 0, -1); + assertPeriod(Period.of(Long.MAX_VALUE, NANOS), 0, 0, 0, + (int) (Long.MAX_VALUE / 3600_000_000_000L), + (int) ((Long.MAX_VALUE / 60_000_000_000L) % 60), + (int) ((Long.MAX_VALUE / 1_000_000_000L) % 60), + Long.MAX_VALUE % 1_000_000_000L); + assertPeriod(Period.of(Long.MIN_VALUE, NANOS), 0, 0, 0, + (int) (Long.MIN_VALUE / 3600_000_000_000L), + (int) ((Long.MIN_VALUE / 60_000_000_000L) % 60), + (int) ((Long.MIN_VALUE / 1_000_000_000L) % 60), + Long.MIN_VALUE % 1_000_000_000L); + } + + //----------------------------------------------------------------------- + // of(Duration) + //----------------------------------------------------------------------- + public void factory_duration() { + assertPeriod(Period.of(Duration.ofSeconds(2, 3)), 0, 0, 0, 0, 0, 2, 3); + assertPeriod(Period.of(Duration.ofSeconds(59, 3)), 0, 0, 0, 0, 0, 59, 3); + assertPeriod(Period.of(Duration.ofSeconds(60, 3)), 0, 0, 0, 0, 1, 0, 3); + assertPeriod(Period.of(Duration.ofSeconds(61, 3)), 0, 0, 0, 0, 1, 1, 3); + assertPeriod(Period.of(Duration.ofSeconds(3599, 3)), 0, 0, 0, 0, 59, 59, 3); + assertPeriod(Period.of(Duration.ofSeconds(3600, 3)), 0, 0, 0, 1, 0, 0, 3); + } + + public void factory_duration_negative() { + assertPeriod(Period.of(Duration.ofSeconds(-2, 3)), 0, 0, 0, 0, 0, -1, -999999997); + assertPeriod(Period.of(Duration.ofSeconds(-59, 3)), 0, 0, 0, 0, 0, -58, -999999997); + assertPeriod(Period.of(Duration.ofSeconds(-60, 3)), 0, 0, 0, 0, 0, -59, -999999997); + assertPeriod(Period.of(Duration.ofSeconds(-60, -3)), 0, 0, 0, 0, -1, 0, -3); + + assertPeriod(Period.of(Duration.ofSeconds(2, -3)), 0, 0, 0, 0, 0, 1, 999999997); + } + + public void factory_duration_big() { + Duration dur = Duration.ofSeconds(Integer.MAX_VALUE, 3); + long secs = Integer.MAX_VALUE; + assertPeriod(Period.of(dur), 0, 0, 0, (int) (secs / 3600), (int) ((secs % 3600) / 60), (int) (secs % 60), 3); + } + + @Test(expectedExceptions=NullPointerException.class) + public void factory_duration_null() { + Period.of((Duration) null); + } + + //----------------------------------------------------------------------- + // between + //----------------------------------------------------------------------- + @DataProvider(name="betweenDates") + Object[][] data_betweenDates() { + return new Object[][] { + {2010, 1, 1, 2010, 1, 1, 0, 0, 0}, + {2010, 1, 1, 2010, 1, 2, 0, 0, 1}, + {2010, 1, 1, 2010, 2, 1, 0, 1, 0}, + {2010, 1, 1, 2010, 2, 2, 0, 1, 1}, + {2010, 1, 1, 2011, 1, 1, 1, 0, 0}, + + {2010, 6, 12, 2010, 1, 1, 0, -5, -11}, + {2010, 6, 12, 2010, 1, 2, 0, -5, -10}, + {2010, 6, 12, 2010, 2, 1, 0, -4, -11}, + {2010, 6, 12, 2010, 9, 24, 0, 3, 12}, + + {2010, 6, 12, 2009, 1, 1, -1, -5, -11}, + {2010, 6, 12, 2009, 1, 2, -1, -5, -10}, + {2010, 6, 12, 2009, 2, 1, -1, -4, -11}, + {2010, 6, 12, 2009, 9, 24, 0, -9, 12}, + + {2010, 6, 12, 2008, 1, 1, -2, -5, -11}, + {2010, 6, 12, 2008, 1, 2, -2, -5, -10}, + {2010, 6, 12, 2008, 2, 1, -2, -4, -11}, + {2010, 6, 12, 2008, 9, 24, -1, -9, 12}, + }; + } + + @Test(dataProvider="betweenDates") + public void factory_between_LocalDate(int y1, int m1, int d1, int y2, int m2, int d2, int ye, int me, int de) { + LocalDate start = LocalDate.of(y1, m1, d1); + LocalDate end = LocalDate.of(y2, m2, d2); + Period test = Period.between(start, end); + assertPeriod(test, ye, me, de, 0, 0, 0, 0); + //assertEquals(start.plus(test), end); + } + + @DataProvider(name="betweenTimes") + Object[][] data_betweenTimes() { + return new Object[][] { + {12, 30, 40, 12, 30, 45, 0, 0, 5}, + {12, 30, 40, 12, 35, 40, 0, 5, 0}, + {12, 30, 40, 13, 30, 40, 1, 0, 0}, + + {12, 30, 40, 12, 30, 35, 0, 0, -5}, + {12, 30, 40, 12, 25, 40, 0, -5, 0}, + {12, 30, 40, 11, 30, 40, -1, 0, 0}, + }; + } + + @Test(dataProvider="betweenTimes") + public void factory_between_LocalTime(int h1, int m1, int s1, int h2, int m2, int s2, int he, int me, int se) { + LocalTime start = LocalTime.of(h1, m1, s1); + LocalTime end = LocalTime.of(h2, m2, s2); + Period test = Period.between(start, end); + assertPeriod(test, 0, 0, 0, he, me, se, 0); + //assertEquals(start.plus(test), end); + } + + public void factory_between_YearMonth() { + assertPeriod(Period.between(YearMonth.of(2012, 6), YearMonth.of(2013, 7)), 1, 1, 0, 0, 0, 0, 0); + assertPeriod(Period.between(YearMonth.of(2012, 6), YearMonth.of(2013, 3)), 0, 9, 0, 0, 0, 0, 0); + assertPeriod(Period.between(YearMonth.of(2012, 6), YearMonth.of(2011, 7)), 0, -11, 0, 0, 0, 0, 0); + } + + public void factory_between_Month() { + assertPeriod(Period.between(Month.FEBRUARY, Month.MAY), 0, 3, 0, 0, 0, 0, 0); + assertPeriod(Period.between(Month.NOVEMBER, Month.MAY), 0, -6, 0, 0, 0, 0, 0); + } + + //----------------------------------------------------------------------- + // betweenISO + //----------------------------------------------------------------------- + @DataProvider(name="betweenISO") + Object[][] data_betweenISO() { + return new Object[][] { + {2010, 1, 1, 2010, 1, 1, 0, 0, 0}, + {2010, 1, 1, 2010, 1, 2, 0, 0, 1}, + {2010, 1, 1, 2010, 1, 31, 0, 0, 30}, + {2010, 1, 1, 2010, 2, 1, 0, 1, 0}, + {2010, 1, 1, 2010, 2, 28, 0, 1, 27}, + {2010, 1, 1, 2010, 3, 1, 0, 2, 0}, + {2010, 1, 1, 2010, 12, 31, 0, 11, 30}, + {2010, 1, 1, 2011, 1, 1, 1, 0, 0}, + {2010, 1, 1, 2011, 12, 31, 1, 11, 30}, + {2010, 1, 1, 2012, 1, 1, 2, 0, 0}, + + {2010, 1, 10, 2010, 1, 1, 0, 0, -9}, + {2010, 1, 10, 2010, 1, 2, 0, 0, -8}, + {2010, 1, 10, 2010, 1, 9, 0, 0, -1}, + {2010, 1, 10, 2010, 1, 10, 0, 0, 0}, + {2010, 1, 10, 2010, 1, 11, 0, 0, 1}, + {2010, 1, 10, 2010, 1, 31, 0, 0, 21}, + {2010, 1, 10, 2010, 2, 1, 0, 0, 22}, + {2010, 1, 10, 2010, 2, 9, 0, 0, 30}, + {2010, 1, 10, 2010, 2, 10, 0, 1, 0}, + {2010, 1, 10, 2010, 2, 28, 0, 1, 18}, + {2010, 1, 10, 2010, 3, 1, 0, 1, 19}, + {2010, 1, 10, 2010, 3, 9, 0, 1, 27}, + {2010, 1, 10, 2010, 3, 10, 0, 2, 0}, + {2010, 1, 10, 2010, 12, 31, 0, 11, 21}, + {2010, 1, 10, 2011, 1, 1, 0, 11, 22}, + {2010, 1, 10, 2011, 1, 9, 0, 11, 30}, + {2010, 1, 10, 2011, 1, 10, 1, 0, 0}, + + {2010, 3, 30, 2011, 5, 1, 1, 1, 1}, + {2010, 4, 30, 2011, 5, 1, 1, 0, 1}, + + {2010, 2, 28, 2012, 2, 27, 1, 11, 30}, + {2010, 2, 28, 2012, 2, 28, 2, 0, 0}, + {2010, 2, 28, 2012, 2, 29, 2, 0, 1}, + + {2012, 2, 28, 2014, 2, 27, 1, 11, 30}, + {2012, 2, 28, 2014, 2, 28, 2, 0, 0}, + {2012, 2, 28, 2014, 3, 1, 2, 0, 1}, + + {2012, 2, 29, 2014, 2, 28, 1, 11, 30}, + {2012, 2, 29, 2014, 3, 1, 2, 0, 1}, + {2012, 2, 29, 2014, 3, 2, 2, 0, 2}, + + {2012, 2, 29, 2016, 2, 28, 3, 11, 30}, + {2012, 2, 29, 2016, 2, 29, 4, 0, 0}, + {2012, 2, 29, 2016, 3, 1, 4, 0, 1}, + + {2010, 1, 1, 2009, 12, 31, 0, 0, -1}, + {2010, 1, 1, 2009, 12, 30, 0, 0, -2}, + {2010, 1, 1, 2009, 12, 2, 0, 0, -30}, + {2010, 1, 1, 2009, 12, 1, 0, -1, 0}, + {2010, 1, 1, 2009, 11, 30, 0, -1, -1}, + {2010, 1, 1, 2009, 11, 2, 0, -1, -29}, + {2010, 1, 1, 2009, 11, 1, 0, -2, 0}, + {2010, 1, 1, 2009, 1, 2, 0, -11, -30}, + {2010, 1, 1, 2009, 1, 1, -1, 0, 0}, + + {2010, 1, 15, 2010, 1, 15, 0, 0, 0}, + {2010, 1, 15, 2010, 1, 14, 0, 0, -1}, + {2010, 1, 15, 2010, 1, 1, 0, 0, -14}, + {2010, 1, 15, 2009, 12, 31, 0, 0, -15}, + {2010, 1, 15, 2009, 12, 16, 0, 0, -30}, + {2010, 1, 15, 2009, 12, 15, 0, -1, 0}, + {2010, 1, 15, 2009, 12, 14, 0, -1, -1}, + + {2010, 2, 28, 2009, 3, 1, 0, -11, -27}, + {2010, 2, 28, 2009, 2, 28, -1, 0, 0}, + {2010, 2, 28, 2009, 2, 27, -1, 0, -1}, + + {2010, 2, 28, 2008, 2, 29, -1, -11, -28}, + {2010, 2, 28, 2008, 2, 28, -2, 0, 0}, + {2010, 2, 28, 2008, 2, 27, -2, 0, -1}, + + {2012, 2, 29, 2009, 3, 1, -2, -11, -28}, + {2012, 2, 29, 2009, 2, 28, -3, 0, -1}, + {2012, 2, 29, 2009, 2, 27, -3, 0, -2}, + + {2012, 2, 29, 2008, 3, 1, -3, -11, -28}, + {2012, 2, 29, 2008, 2, 29, -4, 0, 0}, + {2012, 2, 29, 2008, 2, 28, -4, 0, -1}, + }; + } + + @Test(dataProvider="betweenISO") + public void factory_betweenISO_LocalDate(int y1, int m1, int d1, int y2, int m2, int d2, int ye, int me, int de) { + LocalDate start = LocalDate.of(y1, m1, d1); + LocalDate end = LocalDate.of(y2, m2, d2); + Period test = Period.betweenISO(start, end); + assertPeriod(test, ye, me, de, 0, 0, 0, 0); + //assertEquals(start.plus(test), end); + } + + @Test(expectedExceptions=NullPointerException.class) + public void factory_betweenISO_LocalDate_nullFirst() { + Period.betweenISO((LocalDate) null, LocalDate.of(2010, 1, 1)); + } + + @Test(expectedExceptions=NullPointerException.class) + public void factory_betweenISO_LocalDate_nullSecond() { + Period.betweenISO(LocalDate.of(2010, 1, 1), (LocalDate) null); + } + + //------------------------------------------------------------------------- + @Test(expectedExceptions=NullPointerException.class) + public void factory_betweenISO_LocalTime_nullFirst() { + Period.betweenISO((LocalTime) null, LocalTime.of(12, 30)); + } + + @Test(expectedExceptions=NullPointerException.class) + public void factory_betweenISO_LocalTime_nullSecond() { + Period.betweenISO(LocalTime.of(12, 30), (LocalTime) null); + } + + //----------------------------------------------------------------------- + // parse() + //----------------------------------------------------------------------- + @Test(dataProvider="toStringAndParse") + public void test_parse(Period test, String expected) { + assertEquals(test, Period.parse(expected)); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_parse_nullText() { + Period.parse((String) null); + } + + //----------------------------------------------------------------------- + // isZero() + //----------------------------------------------------------------------- + public void test_isZero() { + assertEquals(Period.of(1, 2, 3, 4, 5, 6, 7).isZero(), false); + assertEquals(Period.of(1, 2, 3, 0, 0, 0, 0).isZero(), false); + assertEquals(Period.of(0, 0, 0, 4, 5, 6, 7).isZero(), false); + assertEquals(Period.of(1, 0, 0, 0, 0, 0, 0).isZero(), false); + assertEquals(Period.of(0, 2, 0, 0, 0, 0, 0).isZero(), false); + assertEquals(Period.of(0, 0, 3, 0, 0, 0, 0).isZero(), false); + assertEquals(Period.of(0, 0, 0, 4, 0, 0, 0).isZero(), false); + assertEquals(Period.of(0, 0, 0, 0, 5, 0, 0).isZero(), false); + assertEquals(Period.of(0, 0, 0, 0, 0, 6, 0).isZero(), false); + assertEquals(Period.of(0, 0, 0, 0, 0, 0, 7).isZero(), false); + assertEquals(Period.of(0, 0, 0, 0, 0, 0).isZero(), true); + } + + //----------------------------------------------------------------------- + // isPositive() + //----------------------------------------------------------------------- + public void test_isPositive() { + assertEquals(Period.of(1, 2, 3, 4, 5, 6, 7).isPositive(), true); + assertEquals(Period.of(1, 2, 3, 0, 0, 0, 0).isPositive(), true); + assertEquals(Period.of(0, 0, 0, 4, 5, 6, 7).isPositive(), true); + assertEquals(Period.of(1, 0, 0, 0, 0, 0, 0).isPositive(), true); + assertEquals(Period.of(0, 2, 0, 0, 0, 0, 0).isPositive(), true); + assertEquals(Period.of(0, 0, 3, 0, 0, 0, 0).isPositive(), true); + assertEquals(Period.of(0, 0, 0, 4, 0, 0, 0).isPositive(), true); + assertEquals(Period.of(0, 0, 0, 0, 5, 0, 0).isPositive(), true); + assertEquals(Period.of(0, 0, 0, 0, 0, 6, 0).isPositive(), true); + assertEquals(Period.of(0, 0, 0, 0, 0, 0, 7).isPositive(), true); + assertEquals(Period.of(-1, -2, -3, -4, -5, -6, -7).isPositive(), false); + assertEquals(Period.of(-1, -2, 3, 4, -5, -6, -7).isPositive(), false); + assertEquals(Period.of(-1, 0, 0, 0, 0, 0, 0).isPositive(), false); + assertEquals(Period.of(0, -2, 0, 0, 0, 0, 0).isPositive(), false); + assertEquals(Period.of(0, 0, -3, 0, 0, 0, 0).isPositive(), false); + assertEquals(Period.of(0, 0, 0, -4, 0, 0, 0).isPositive(), false); + assertEquals(Period.of(0, 0, 0, 0, -5, 0, 0).isPositive(), false); + assertEquals(Period.of(0, 0, 0, 0, 0, -6, 0).isPositive(), false); + assertEquals(Period.of(0, 0, 0, 0, 0, 0, -7).isPositive(), false); + assertEquals(Period.of(0, 0, 0, 0, 0, 0).isPositive(), false); + } + + //----------------------------------------------------------------------- + // withYears() + //----------------------------------------------------------------------- + public void test_withYears() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertPeriod(test.withYears(10), 10, 2, 3, 4, 5, 6, 7); + } + + public void test_withYears_noChange() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertSame(test.withYears(1), test); + } + + public void test_withYears_toZero() { + Period test = Period.of(1, YEARS); + assertSame(test.withYears(0), Period.ZERO); + } + + //----------------------------------------------------------------------- + // withMonths() + //----------------------------------------------------------------------- + public void test_withMonths() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertPeriod(test.withMonths(10), 1, 10, 3, 4, 5, 6, 7); + } + + public void test_withMonths_noChange() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertSame(test.withMonths(2), test); + } + + public void test_withMonths_toZero() { + Period test = Period.of(1, MONTHS); + assertSame(test.withMonths(0), Period.ZERO); + } + + //----------------------------------------------------------------------- + // withDays() + //----------------------------------------------------------------------- + public void test_withDays() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertPeriod(test.withDays(10), 1, 2, 10, 4, 5, 6, 7); + } + + public void test_withDays_noChange() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertSame(test.withDays(3), test); + } + + public void test_withDays_toZero() { + Period test = Period.of(1, DAYS); + assertSame(test.withDays(0), Period.ZERO); + } + + //----------------------------------------------------------------------- + // withTimeNanos() + //----------------------------------------------------------------------- + public void test_withNanos() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertPeriod(test.withTimeNanos(10), 1, 2, 3, 0, 0, 0, 10); + } + + public void test_withNanos_noChange() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertSame(test.withTimeNanos(((4 * 60 + 5) * 60 + 6) * 1_000_000_000L + 7), test); + } + + public void test_withNanos_toZero() { + Period test = Period.of(1, NANOS); + assertSame(test.withTimeNanos(0), Period.ZERO); + } + + + + //----------------------------------------------------------------------- + // plusYears() + //----------------------------------------------------------------------- + public void test_plusYears() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertPeriod(test.plus(10, YEARS), 11, 2, 3, 4, 5, 6, 7); + assertPeriod(test.plus(Period.of(10, YEARS)), 11, 2, 3, 4, 5, 6, 7); + } + + public void test_plusYears_noChange() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertSame(test.plus(0, YEARS), test); + assertPeriod(test.plus(Period.of(0, YEARS)), 1, 2, 3, 4, 5, 6, 7); + } + + public void test_plusYears_toZero() { + Period test = Period.of(-1, YEARS); + assertSame(test.plus(1, YEARS), Period.ZERO); + assertSame(test.plus(Period.of(1, YEARS)), Period.ZERO); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_plusYears_overflowTooBig() { + Period test = Period.of(Integer.MAX_VALUE, YEARS); + test.plus(1, YEARS); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_plusYears_overflowTooSmall() { + Period test = Period.of(Integer.MIN_VALUE, YEARS); + test.plus(-1, YEARS); + } + + //----------------------------------------------------------------------- + // plusMonths() + //----------------------------------------------------------------------- + public void test_plusMonths() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertPeriod(test.plus(10, MONTHS), 1, 12, 3, 4, 5, 6, 7); + assertPeriod(test.plus(Period.of(10, MONTHS)), 1, 12, 3, 4, 5, 6, 7); + } + + public void test_plusMonths_noChange() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertSame(test.plus(0, MONTHS), test); + assertEquals(test.plus(Period.of(0, MONTHS)), test); + } + + public void test_plusMonths_toZero() { + Period test = Period.of(-1, MONTHS); + assertSame(test.plus(1, MONTHS), Period.ZERO); + assertSame(test.plus(Period.of(1, MONTHS)), Period.ZERO); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_plusMonths_overflowTooBig() { + Period test = Period.of(Integer.MAX_VALUE, MONTHS); + test.plus(1, MONTHS); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_plusMonths_overflowTooSmall() { + Period test = Period.of(Integer.MIN_VALUE, MONTHS); + test.plus(-1, MONTHS); + } + + //----------------------------------------------------------------------- + // plusDays() + //----------------------------------------------------------------------- + public void test_plusDays() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertPeriod(test.plus(10, DAYS), 1, 2, 13, 4, 5, 6, 7); + } + + public void test_plusDays_noChange() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertSame(test.plus(0, DAYS), test); + } + + public void test_plusDays_toZero() { + Period test = Period.of(-1, DAYS); + assertSame(test.plus(1, DAYS), Period.ZERO); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_plusDays_overflowTooBig() { + Period test = Period.of(Integer.MAX_VALUE, DAYS); + test.plus(1, DAYS); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_plusDays_overflowTooSmall() { + Period test = Period.of(Integer.MIN_VALUE, DAYS); + test.plus(-1, DAYS); + } + + //----------------------------------------------------------------------- + // plusHours() + //----------------------------------------------------------------------- + public void test_plusHours() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertPeriod(test.plus(10, HOURS), 1, 2, 3, 14, 5, 6, 7); + } + + public void test_plusHours_noChange() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertSame(test.plus(0, HOURS), test); + } + + public void test_plusHours_toZero() { + Period test = Period.of(-1, HOURS); + assertSame(test.plus(1, HOURS), Period.ZERO); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_plusHours_overflowTooBig() { + Period test = Period.of(Integer.MAX_VALUE, HOURS); + test.plus(1, HOURS); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_plusHours_overflowTooSmall() { + Period test = Period.of(Integer.MIN_VALUE, HOURS); + test.plus(-1, HOURS); + } + + //----------------------------------------------------------------------- + // plusMinutes() + //----------------------------------------------------------------------- + public void test_plusMinutes() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertPeriod(test.plus(10, MINUTES), 1, 2, 3, 4, 15, 6, 7); + } + + public void test_plusMinutes_noChange() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertSame(test.plus(0, MINUTES), test); + } + + public void test_plusMinutes_toZero() { + Period test = Period.of(-1, MINUTES); + assertSame(test.plus(1, MINUTES), Period.ZERO); + } + + //----------------------------------------------------------------------- + // plusSeconds() + //----------------------------------------------------------------------- + public void test_plusSeconds() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertPeriod(test.plus(10, SECONDS), 1, 2, 3, 4, 5, 16, 7); + } + + public void test_plusSeconds_noChange() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertSame(test.plus(0, SECONDS), test); + } + + public void test_plusSeconds_toZero() { + Period test = Period.of(-1, SECONDS); + assertSame(test.plus(1, SECONDS), Period.ZERO); + } + + //----------------------------------------------------------------------- + // plusNanos() + //----------------------------------------------------------------------- + public void test_plusNanos() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertPeriod(test.plus(10, NANOS), 1, 2, 3, 4, 5, 6, 17); + } + + public void test_plusNanos_noChange() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertSame(test.plus(0, NANOS), test); + } + + public void test_plusNanos_toZero() { + Period test = Period.of(-1, NANOS); + assertSame(test.plus(1, NANOS), Period.ZERO); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_plusNanos_overflowTooBig() { + Period test = Period.of(Long.MAX_VALUE, NANOS); + test.plus(1, NANOS); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_plusNanos_overflowTooSmall() { + Period test = Period.of(Long.MIN_VALUE, NANOS); + test.plus(-1, NANOS); + } + + //----------------------------------------------------------------------- + // minusYears() + //----------------------------------------------------------------------- + public void test_minusYears() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertPeriod(test.minus(10, YEARS), -9, 2, 3, 4, 5, 6, 7); + } + + public void test_minusYears_noChange() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertSame(test.minus(0, YEARS), test); + } + + public void test_minusYears_toZero() { + Period test = Period.of(1, YEARS); + assertSame(test.minus(1, YEARS), Period.ZERO); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_minusYears_overflowTooBig() { + Period test = Period.of(Integer.MAX_VALUE, YEARS); + test.minus(-1, YEARS); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_minusYears_overflowTooSmall() { + Period test = Period.of(Integer.MIN_VALUE, YEARS); + test.minus(1, YEARS); + } + + //----------------------------------------------------------------------- + // minusMonths() + //----------------------------------------------------------------------- + public void test_minusMonths() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertPeriod(test.minus(10, MONTHS), 1, -8, 3, 4, 5, 6, 7); + } + + public void test_minusMonths_noChange() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertSame(test.minus(0, MONTHS), test); + } + + public void test_minusMonths_toZero() { + Period test = Period.of(1, MONTHS); + assertSame(test.minus(1, MONTHS), Period.ZERO); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_minusMonths_overflowTooBig() { + Period test = Period.of(Integer.MAX_VALUE, MONTHS); + test.minus(-1, MONTHS); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_minusMonths_overflowTooSmall() { + Period test = Period.of(Integer.MIN_VALUE, MONTHS); + test.minus(1, MONTHS); + } + + //----------------------------------------------------------------------- + // minusDays() + //----------------------------------------------------------------------- + public void test_minusDays() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertPeriod(test.minus(10, DAYS), 1, 2, -7, 4, 5, 6, 7); + } + + public void test_minusDays_noChange() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertSame(test.minus(0, DAYS), test); + } + + public void test_minusDays_toZero() { + Period test = Period.of(1, DAYS); + assertSame(test.minus(1, DAYS), Period.ZERO); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_minusDays_overflowTooBig() { + Period test = Period.of(Integer.MAX_VALUE, DAYS); + test.minus(-1, DAYS); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_minusDays_overflowTooSmall() { + Period test = Period.of(Integer.MIN_VALUE, DAYS); + test.minus(1, DAYS); + } + + //----------------------------------------------------------------------- + // minusHours() + //----------------------------------------------------------------------- + public void test_minusHours() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertPeriod(test.minus(10, HOURS), 1, 2, 3, -5, -54, -53, -999999993); + assertEquals(test.minus(10, HOURS).plus(10, HOURS), test); + } + + public void test_minusHours_noChange() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertSame(test.minus(0, HOURS), test); + } + + public void test_minusHours_toZero() { + Period test = Period.of(1, HOURS); + assertSame(test.minus(1, HOURS), Period.ZERO); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_minusHours_overflowTooBig() { + Period test = Period.of(Integer.MAX_VALUE, HOURS); + test.minus(-1, HOURS); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_minusHours_overflowTooSmall() { + Period test = Period.of(Integer.MIN_VALUE, HOURS); + test.minus(1, HOURS); + } + + //----------------------------------------------------------------------- + // minusMinutes() + //----------------------------------------------------------------------- + public void test_minusMinutes() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertPeriod(test.minus(10, MINUTES), 1, 2, 3, 3, 55, 6, 7); + assertEquals(test.minus(10, MINUTES).plus(10, MINUTES), test); + } + + public void test_minusMinutes_noChange() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertSame(test.minus(0, MINUTES), test); + } + + public void test_minusMinutes_toZero() { + Period test = Period.of(1, MINUTES); + assertSame(test.minus(1, MINUTES), Period.ZERO); + } + + //----------------------------------------------------------------------- + // minusSeconds() + //----------------------------------------------------------------------- + public void test_minusSeconds() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertPeriod(test.minus(10, SECONDS), 1, 2, 3, 4, 4, 56, 7); + assertEquals(test.minus(10, SECONDS).plus(10, SECONDS), test); + } + + public void test_minusSeconds_noChange() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertSame(test.minus(0, SECONDS), test); + } + + public void test_minusSeconds_toZero() { + Period test = Period.of(1, SECONDS); + assertSame(test.minus(1, SECONDS), Period.ZERO); + } + + //----------------------------------------------------------------------- + // minusNanos() + //----------------------------------------------------------------------- + public void test_minusNanos() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertPeriod(test.minus(10, NANOS), 1, 2, 3, 4, 5, 5, 999999997); + } + + public void test_minusNanos_noChange() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertSame(test.minus(0, NANOS), test); + } + + public void test_minusNanos_toZero() { + Period test = Period.of(1, NANOS); + assertSame(test.minus(1, NANOS), Period.ZERO); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_minusNanos_overflowTooBig() { + Period test = Period.of(Long.MAX_VALUE, NANOS); + test.minus(-1, NANOS); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_minusNanos_overflowTooSmall() { + Period test = Period.of(Long.MIN_VALUE, NANOS); + test.minus(1, NANOS); + } + + //----------------------------------------------------------------------- + // multipliedBy() + //----------------------------------------------------------------------- + public void test_multipliedBy() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertPeriod(test.multipliedBy(2), 2, 4, 6, 8, 10, 12, 14); + assertPeriod(test.multipliedBy(-3), -3, -6, -9, -12, -15, -18, -21); + } + + public void test_multipliedBy_zeroBase() { + assertSame(Period.ZERO.multipliedBy(2), Period.ZERO); + } + + public void test_multipliedBy_zero() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertSame(test.multipliedBy(0), Period.ZERO); + } + + public void test_multipliedBy_one() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertSame(test.multipliedBy(1), test); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_multipliedBy_overflowTooBig() { + Period test = Period.of(Integer.MAX_VALUE / 2 + 1, YEARS); + test.multipliedBy(2); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_multipliedBy_overflowTooSmall() { + Period test = Period.of(Integer.MIN_VALUE / 2 - 1, YEARS); + test.multipliedBy(2); + } + + //----------------------------------------------------------------------- + // negated() + //----------------------------------------------------------------------- + public void test_negated() { + Period test = Period.of(1, 2, 3, 4, 5, 6, 7); + assertPeriod(test.negated(), -1, -2, -3, -4, -5, -6, -7); + } + + public void test_negated_zero() { + assertSame(Period.ZERO.negated(), Period.ZERO); + } + + public void test_negated_max() { + assertPeriod(Period.of(Integer.MAX_VALUE, YEARS).negated(), -Integer.MAX_VALUE, 0, 0, 0, 0, 0, 0); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_negated_overflow() { + Period.of(Integer.MIN_VALUE, YEARS).negated(); + } + + //----------------------------------------------------------------------- + // normalizedHoursToDays() + //----------------------------------------------------------------------- + @DataProvider(name="normalizedHoursToDays") + Object[][] data_normalizedHoursToDays() { + return new Object[][] { + {0, 0, 0, 0}, + {1, 0, 1, 0}, + {-1, 0, -1, 0}, + + {1, 1, 1, 1}, + {1, 23, 1, 23}, + {1, 24, 2, 0}, + {1, 25, 2, 1}, + + {1, -1, 0, 23}, + {1, -23, 0, 1}, + {1, -24, 0, 0}, + {1, -25, 0, -1}, + {1, -47, 0, -23}, + {1, -48, -1, 0}, + {1, -49, -1, -1}, + + {-1, 1, 0, -23}, + {-1, 23, 0, -1}, + {-1, 24, 0, 0}, + {-1, 25, 0, 1}, + {-1, 47, 0, 23}, + {-1, 48, 1, 0}, + {-1, 49, 1, 1}, + + {-1, -1, -1, -1}, + {-1, -23, -1, -23}, + {-1, -24, -2, 0}, + {-1, -25, -2, -1}, + }; + } + + @Test(dataProvider="normalizedHoursToDays") + public void test_normalizedHoursToDays(int inputDays, int inputHours, int expectedDays, int expectedHours) { + assertPeriod(Period.of(0, 0, inputDays, inputHours, 0, 0, 0).normalizedHoursToDays(), 0, 0, expectedDays, expectedHours, 0, 0, 0); + } + + @Test(dataProvider="normalizedHoursToDays") + public void test_normalizedHoursToDays_yearsMonthsUnaffected(int inputDays, int inputHours, int expectedDays, int expectedHours) { + assertPeriod(Period.of(12, 6, inputDays, inputHours, 0, 0, 0).normalizedHoursToDays(), 12, 6, expectedDays, expectedHours, 0, 0, 0); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_normalizedHoursToDays_min() { + Period base = Period.of(0, 0, Integer.MIN_VALUE, -24, 0, 0, 0); + base.normalizedHoursToDays(); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_normalizedHoursToDays_max() { + Period base = Period.of(0, 0, Integer.MAX_VALUE, 24, 0, 0, 0); + base.normalizedHoursToDays(); + } + + //----------------------------------------------------------------------- + // normalizedDaysToHours() + //----------------------------------------------------------------------- + @DataProvider(name="normalizedDaysToHours") + Object[][] data_normalizedDaysToHours() { + return new Object[][] { + {0, 0, 0, 0, 0}, + + {1, 0, 0, 24, 0}, + {1, 1, 0, 25, 0}, + {1, 23, 0, 47, 0}, + {1, 24, 0, 48, 0}, + {1, 25, 0, 49, 0}, + {2, 23, 0, 71, 0}, + {2, 24, 0, 72, 0}, + {2, 25, 0, 73, 0}, + + {1, 0, 0, 24, 0}, + {1, -1, 0, 23, 0}, + {1, -23, 0, 1, 0}, + {1, -24, 0, 0, 0}, + {1, -25, 0, -1, 0}, + {2, -23, 0, 25, 0}, + {2, -24, 0, 24, 0}, + {2, -25, 0, 23, 0}, + + {-1, 0, 0, -24, 0}, + {-1, 1, 0, -23, 0}, + {-1, 23, 0, -1, 0}, + {-1, 24, 0, 0, 0}, + {-1, 25, 0, 1, 0}, + {-2, 23, 0, -25, 0}, + {-2, 24, 0, -24, 0}, + {-2, 25, 0, -23, 0}, + + {-1, 0, 0, -24, 0}, + {-1, -1, 0, -25, 0}, + {-1, -23, 0, -47, 0}, + {-1, -24, 0, -48, 0}, + {-1, -25, 0, -49, 0}, + + // minutes + {1, -1, -1, 22, 59}, + {1, -23, -1, 0, 59}, + {1, -24, -1, 0, -1}, + {1, -25, -1, -1, -1}, + {-1, 1, 1, -22, -59}, + {-1, 23, 1, 0, -59}, + {-1, 24, 1, 0, 1}, + {-1, 25, 1, 1, 1}, + }; + } + + @Test(dataProvider="normalizedDaysToHours") + public void test_normalizedDaysToHours(int inputDays, int inputHours, int inputMinutes, int expectedHours, int expectedMinutes) { + assertPeriod(Period.of(0, 0, inputDays, inputHours, inputMinutes, 0).normalizedDaysToHours(), 0, 0, 0, expectedHours, expectedMinutes, 0, 0); + } + + @Test(dataProvider="normalizedDaysToHours") + public void test_normalizedDaysToHours_yearsMonthsUnaffected(int inputDays, int inputHours, int inputMinutes, int expectedHours, int expectedMinutes) { + assertPeriod(Period.of(12, 6, inputDays, inputHours, inputMinutes, 0).normalizedDaysToHours(), 12, 6, 0, expectedHours, expectedMinutes, 0, 0); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_normalizedDaysToHours_min() { + Period base = Period.of(0, 0, -1_000, -10_000_000, 0, 0, 0); + base.normalizedDaysToHours(); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_normalizedDaysToHours_max() { + Period base = Period.of(0, 0, 1_000, 10_000_000, 0, 0, 0); + base.normalizedDaysToHours(); + } + + //----------------------------------------------------------------------- + // normalizedMonthsISO() + //----------------------------------------------------------------------- + @DataProvider(name="normalizedMonthsISO") + Object[][] data_normalizedMonthsISO() { + return new Object[][] { + {0, 0, 0, 0}, + {1, 0, 1, 0}, + {-1, 0, -1, 0}, + + {1, 1, 1, 1}, + {1, 2, 1, 2}, + {1, 11, 1, 11}, + {1, 12, 2, 0}, + {1, 13, 2, 1}, + {1, 23, 2, 11}, + {1, 24, 3, 0}, + {1, 25, 3, 1}, + + {1, -1, 0, 11}, + {1, -2, 0, 10}, + {1, -11, 0, 1}, + {1, -12, 0, 0}, + {1, -13, 0, -1}, + {1, -23, 0, -11}, + {1, -24, -1, 0}, + {1, -25, -1, -1}, + {1, -35, -1, -11}, + {1, -36, -2, 0}, + {1, -37, -2, -1}, + + {-1, 1, 0, -11}, + {-1, 11, 0, -1}, + {-1, 12, 0, 0}, + {-1, 13, 0, 1}, + {-1, 23, 0, 11}, + {-1, 24, 1, 0}, + {-1, 25, 1, 1}, + + {-1, -1, -1, -1}, + {-1, -11, -1, -11}, + {-1, -12, -2, 0}, + {-1, -13, -2, -1}, + }; + } + + @Test(dataProvider="normalizedMonthsISO") + public void test_normalizedMonthsISO(int inputYears, int inputMonths, int expectedYears, int expectedMonths) { + assertPeriod(Period.ofDate(inputYears, inputMonths, 0).normalizedMonthsISO(), expectedYears, expectedMonths, 0, 0, 0, 0, 0); + } + + @Test(dataProvider="normalizedMonthsISO") + public void test_normalizedMonthsISO_daysTimeUnaffected(int inputYears, int inputMonths, int expectedYears, int expectedMonths) { + assertPeriod(Period.of(inputYears, inputMonths, 5, 12, 30, 0, 0).normalizedMonthsISO(), expectedYears, expectedMonths, 5, 12, 30, 0, 0); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_normalizedMonthsISO_min() { + Period base = Period.ofDate(Integer.MIN_VALUE, -12, 0); + base.normalizedMonthsISO(); + } + + @Test(expectedExceptions=ArithmeticException.class) + public void test_normalizedMonthsISO_max() { + Period base = Period.ofDate(Integer.MAX_VALUE, 12, 0); + base.normalizedMonthsISO(); + } + + //----------------------------------------------------------------------- + // addTo() + //----------------------------------------------------------------------- + @DataProvider(name="addTo") + Object[][] data_addTo() { + return new Object[][] { + {pymd(0, 0, 0), date(2012, 6, 30), date(2012, 6, 30)}, + + {pymd(1, 0, 0), date(2012, 6, 10), date(2013, 6, 10)}, + {pymd(0, 1, 0), date(2012, 6, 10), date(2012, 7, 10)}, + {pymd(0, 0, 1), date(2012, 6, 10), date(2012, 6, 11)}, + + {pymd(-1, 0, 0), date(2012, 6, 10), date(2011, 6, 10)}, + {pymd(0, -1, 0), date(2012, 6, 10), date(2012, 5, 10)}, + {pymd(0, 0, -1), date(2012, 6, 10), date(2012, 6, 9)}, + + {pymd(1, 2, 3), date(2012, 6, 27), date(2013, 8, 30)}, + {pymd(1, 2, 3), date(2012, 6, 28), date(2013, 8, 31)}, + {pymd(1, 2, 3), date(2012, 6, 29), date(2013, 9, 1)}, + {pymd(1, 2, 3), date(2012, 6, 30), date(2013, 9, 2)}, + {pymd(1, 2, 3), date(2012, 7, 1), date(2013, 9, 4)}, + + {pymd(1, 0, 0), date(2011, 2, 28), date(2012, 2, 28)}, + {pymd(4, 0, 0), date(2011, 2, 28), date(2015, 2, 28)}, + {pymd(1, 0, 0), date(2012, 2, 29), date(2013, 2, 28)}, + {pymd(4, 0, 0), date(2012, 2, 29), date(2016, 2, 29)}, + + {pymd(1, 1, 0), date(2011, 1, 29), date(2012, 2, 29)}, + {pymd(1, 2, 0), date(2012, 2, 29), date(2013, 4, 29)}, + }; + } + + @Test(dataProvider="addTo") + public void test_addTo(Period period, LocalDate baseDate, LocalDate expected) { + assertEquals(period.addTo(baseDate), expected); + } + + @Test(dataProvider="addTo") + public void test_addTo_usingLocalDatePlus(Period period, LocalDate baseDate, LocalDate expected) { + assertEquals(baseDate.plus(period), expected); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_addTo_nullZero() { + Period.ZERO.addTo(null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_addTo_nullNonZero() { + Period.of(2, DAYS).addTo(null); + } + + //----------------------------------------------------------------------- + // subtractFrom() + //----------------------------------------------------------------------- + @DataProvider(name="subtractFrom") + Object[][] data_subtractFrom() { + return new Object[][] { + {pymd(0, 0, 0), date(2012, 6, 30), date(2012, 6, 30)}, + + {pymd(1, 0, 0), date(2012, 6, 10), date(2011, 6, 10)}, + {pymd(0, 1, 0), date(2012, 6, 10), date(2012, 5, 10)}, + {pymd(0, 0, 1), date(2012, 6, 10), date(2012, 6, 9)}, + + {pymd(-1, 0, 0), date(2012, 6, 10), date(2013, 6, 10)}, + {pymd(0, -1, 0), date(2012, 6, 10), date(2012, 7, 10)}, + {pymd(0, 0, -1), date(2012, 6, 10), date(2012, 6, 11)}, + + {pymd(1, 2, 3), date(2012, 8, 30), date(2011, 6, 27)}, + {pymd(1, 2, 3), date(2012, 8, 31), date(2011, 6, 27)}, + {pymd(1, 2, 3), date(2012, 9, 1), date(2011, 6, 28)}, + {pymd(1, 2, 3), date(2012, 9, 2), date(2011, 6, 29)}, + {pymd(1, 2, 3), date(2012, 9, 3), date(2011, 6, 30)}, + {pymd(1, 2, 3), date(2012, 9, 4), date(2011, 7, 1)}, + + {pymd(1, 0, 0), date(2011, 2, 28), date(2010, 2, 28)}, + {pymd(4, 0, 0), date(2011, 2, 28), date(2007, 2, 28)}, + {pymd(1, 0, 0), date(2012, 2, 29), date(2011, 2, 28)}, + {pymd(4, 0, 0), date(2012, 2, 29), date(2008, 2, 29)}, + + {pymd(1, 1, 0), date(2013, 3, 29), date(2012, 2, 29)}, + {pymd(1, 2, 0), date(2012, 2, 29), date(2010, 12, 29)}, + }; + } + + @Test(dataProvider="subtractFrom") + public void test_subtractFrom(Period period, LocalDate baseDate, LocalDate expected) { + assertEquals(period.subtractFrom(baseDate), expected); + } + + @Test(dataProvider="subtractFrom") + public void test_subtractFrom_usingLocalDateMinus(Period period, LocalDate baseDate, LocalDate expected) { + assertEquals(baseDate.minus(period), expected); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_subtractFrom_nullZero() { + Period.ZERO.subtractFrom(null); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_subtractFrom_nullNonZero() { + Period.of(2, DAYS).subtractFrom(null); + } + + //----------------------------------------------------------------------- + // toDuration() + //----------------------------------------------------------------------- + public void test_toDuration() { + assertEquals(Period.ZERO.toDuration(), Duration.of(0, SECONDS)); + assertEquals(Period.of(0, 0, 0, 4, 5, 6, 7).toDuration(), Duration.ofSeconds((4 * 60 + 5) * 60L + 6, 7)); + } + + public void test_toDuration_calculation() { + assertEquals(Period.of(0, 0, 0, 2, 0, 0, 0).toDuration(), Duration.ofSeconds(2 * 3600)); + assertEquals(Period.of(0, 0, 0, 0, 2, 0, 0).toDuration(), Duration.of(120, SECONDS)); + assertEquals(Period.of(0, 0, 0, 0, 0, 2, 0).toDuration(), Duration.of(2, SECONDS)); + + assertEquals(Period.of(0, 0, 0, 0, 0, 3, 1000000000L - 1).toDuration(), Duration.ofSeconds(3, 999999999)); + assertEquals(Period.of(0, 0, 0, 0, 0, 3, 1000000000L).toDuration(), Duration.ofSeconds(4, 0)); + } + + public void test_toDuration_negatives() { + assertEquals(Period.of(0, 0, 0, 0, 0, 2, 1).toDuration(), Duration.ofSeconds(2, 1)); + assertEquals(Period.of(0, 0, 0, 0, 0, 2, -1).toDuration(), Duration.ofSeconds(1, 999999999)); + assertEquals(Period.of(0, 0, 0, 0, 0, -2, 1).toDuration(), Duration.ofSeconds(-2, 1)); + assertEquals(Period.of(0, 0, 0, 0, 0, -2, -1).toDuration(), Duration.ofSeconds(-3, 999999999)); + } + + @Test(expectedExceptions=DateTimeException.class) + public void test_toDuration_years() { + Period.of(1, 0, 0, 4, 5, 6, 7).toDuration(); + } + + @Test(expectedExceptions=DateTimeException.class) + public void test_toDuration_months() { + Period.of(0, 1, 0, 4, 5, 6, 7).toDuration(); + } + + @Test(expectedExceptions=DateTimeException.class) + public void test_toDuration_days() { + Duration test = Period.of(0, 0, 1, 4, 5, 6, 7).toDuration(); + assertEquals(test, Duration.ofSeconds(101106, 7L)); + } + + //----------------------------------------------------------------------- + // equals() / hashCode() + //----------------------------------------------------------------------- + public void test_equals() { + assertEquals(Period.of(1, 0, 0, 0, 0, 0).equals(Period.of(1, YEARS)), true); + assertEquals(Period.of(0, 1, 0, 0, 0, 0).equals(Period.of(1, MONTHS)), true); + assertEquals(Period.of(0, 0, 1, 0, 0, 0).equals(Period.of(1, DAYS)), true); + assertEquals(Period.of(0, 0, 0, 1, 0, 0).equals(Period.of(1, HOURS)), true); + assertEquals(Period.of(0, 0, 0, 0, 1, 0).equals(Period.of(1, MINUTES)), true); + assertEquals(Period.of(0, 0, 0, 0, 0, 1).equals(Period.of(1, SECONDS)), true); + assertEquals(Period.of(1, 2, 3, 0, 0, 0).equals(Period.ofDate(1, 2, 3)), true); + assertEquals(Period.of(0, 0, 0, 1, 2, 3).equals(Period.ofTime(1, 2, 3)), true); + assertEquals(Period.of(1, 2, 3, 4, 5, 6).equals(Period.of(1, 2, 3, 4, 5, 6)), true); + + assertEquals(Period.of(1, YEARS).equals(Period.of(1, YEARS)), true); + assertEquals(Period.of(1, YEARS).equals(Period.of(2, YEARS)), false); + + assertEquals(Period.of(1, MONTHS).equals(Period.of(1, MONTHS)), true); + assertEquals(Period.of(1, MONTHS).equals(Period.of(2, MONTHS)), false); + + assertEquals(Period.of(1, DAYS).equals(Period.of(1, DAYS)), true); + assertEquals(Period.of(1, DAYS).equals(Period.of(2, DAYS)), false); + + assertEquals(Period.of(1, HOURS).equals(Period.of(1, HOURS)), true); + assertEquals(Period.of(1, HOURS).equals(Period.of(2, HOURS)), false); + + assertEquals(Period.of(1, MINUTES).equals(Period.of(1, MINUTES)), true); + assertEquals(Period.of(1, MINUTES).equals(Period.of(2, MINUTES)), false); + + assertEquals(Period.of(1, SECONDS).equals(Period.of(1, SECONDS)), true); + assertEquals(Period.of(1, SECONDS).equals(Period.of(2, SECONDS)), false); + + assertEquals(Period.ofDate(1, 2, 3).equals(Period.ofDate(1, 2, 3)), true); + assertEquals(Period.ofDate(1, 2, 3).equals(Period.ofDate(0, 2, 3)), false); + assertEquals(Period.ofDate(1, 2, 3).equals(Period.ofDate(1, 0, 3)), false); + assertEquals(Period.ofDate(1, 2, 3).equals(Period.ofDate(1, 2, 0)), false); + + assertEquals(Period.ofTime(1, 2, 3).equals(Period.ofTime(1, 2, 3)), true); + assertEquals(Period.ofTime(1, 2, 3).equals(Period.ofTime(0, 2, 3)), false); + assertEquals(Period.ofTime(1, 2, 3).equals(Period.ofTime(1, 0, 3)), false); + assertEquals(Period.ofTime(1, 2, 3).equals(Period.ofTime(1, 2, 0)), false); + } + + public void test_equals_self() { + Period test = Period.of(1, 2, 3, 4, 5, 6); + assertEquals(test.equals(test), true); + } + + public void test_equals_null() { + Period test = Period.of(1, 2, 3, 4, 5, 6); + assertEquals(test.equals(null), false); + } + + public void test_equals_otherClass() { + Period test = Period.of(1, 2, 3, 4, 5, 6); + assertEquals(test.equals(""), false); + } + + //----------------------------------------------------------------------- + public void test_hashCode() { + Period test5 = Period.of(5, DAYS); + Period test6 = Period.of(6, DAYS); + Period test5M = Period.of(5, MONTHS); + Period test5Y = Period.of(5, YEARS); + assertEquals(test5.hashCode() == test5.hashCode(), true); + assertEquals(test5.hashCode() == test6.hashCode(), false); + assertEquals(test5.hashCode() == test5M.hashCode(), false); + assertEquals(test5.hashCode() == test5Y.hashCode(), false); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @DataProvider(name="toStringAndParse") + Object[][] data_toString() { + return new Object[][] { + {Period.ZERO, "PT0S"}, + {Period.of(0, DAYS), "PT0S"}, + {Period.of(1, YEARS), "P1Y"}, + {Period.of(1, MONTHS), "P1M"}, + {Period.of(1, DAYS), "P1D"}, + {Period.of(1, HOURS), "PT1H"}, + {Period.of(1, MINUTES), "PT1M"}, + {Period.of(1, SECONDS), "PT1S"}, + {Period.of(12, SECONDS), "PT12S"}, + {Period.of(123, SECONDS), "PT2M3S"}, + {Period.of(1234, SECONDS), "PT20M34S"}, + {Period.of(-1, SECONDS), "PT-1S"}, + {Period.of(-12, SECONDS), "PT-12S"}, + {Period.of(-123, SECONDS), "PT-2M-3S"}, + {Period.of(-1234, SECONDS), "PT-20M-34S"}, + {Period.of(1, 2, 3, 4, 5, 6), "P1Y2M3DT4H5M6S"}, + {Period.of(1, 2, 3, 4, 5, 6, 700000000), "P1Y2M3DT4H5M6.7S"}, + {Period.of(0, 0, 0, 0, 0, 0, 100000000), "PT0.1S"}, + {Period.of(0, 0, 0, 0, 0, 0, -100000000), "PT-0.1S"}, + {Period.of(0, 0, 0, 0, 0, 1, -900000000), "PT0.1S"}, + {Period.of(0, 0, 0, 0, 0, -1, 900000000), "PT-0.1S"}, + {Period.of(0, 0, 0, 0, 0, 1, 100000000), "PT1.1S"}, + {Period.of(0, 0, 0, 0, 0, 1, -100000000), "PT0.9S"}, + {Period.of(0, 0, 0, 0, 0, -1, 100000000), "PT-0.9S"}, + {Period.of(0, 0, 0, 0, 0, -1, -100000000), "PT-1.1S"}, + {Period.of(0, 0, 0, 0, 0, 0, 10000000), "PT0.01S"}, + {Period.of(0, 0, 0, 0, 0, 0, -10000000), "PT-0.01S"}, + {Period.of(0, 0, 0, 0, 0, 0, 1000000), "PT0.001S"}, + {Period.of(0, 0, 0, 0, 0, 0, -1000000), "PT-0.001S"}, + {Period.of(0, 0, 0, 0, 0, 0, 1000), "PT0.000001S"}, + {Period.of(0, 0, 0, 0, 0, 0, -1000), "PT-0.000001S"}, + {Period.of(0, 0, 0, 0, 0, 0, 1), "PT0.000000001S"}, + {Period.of(0, 0, 0, 0, 0, 0, -1), "PT-0.000000001S"}, + }; + } + + @Test(groups="tck", dataProvider="toStringAndParse") + public void test_toString(Period input, String expected) { + assertEquals(input.toString(), expected); + } + + //----------------------------------------------------------------------- + private void assertPeriod(Period test, int y, int mo, int d, int h, int mn, int s, long n) { + assertEquals(test.getYears(), y, "years"); + assertEquals(test.getMonths(), mo, "months"); + assertEquals(test.getDays(), d, "days"); + assertEquals(test.getHours(), h, "hours"); + assertEquals(test.getMinutes(), mn, "mins"); + assertEquals(test.getSeconds(), s, "secs"); + assertEquals(test.getNanos(), n, "nanos"); + assertEquals(test.getTimeNanos(), (((h * 60L + mn) * 60 + s) * 1_000_000_000L + n), "total nanos"); + } + + private static Period pymd(int y, int m, int d) { + return Period.ofDate(y, m, d); + } + + private static LocalDate date(int y, int m, int d) { + return LocalDate.of(y, m, d); + } + +} diff --git a/jdk/test/java/time/test/java/time/TestPeriodParser.java b/jdk/test/java/time/test/java/time/TestPeriodParser.java new file mode 100644 index 00000000000..1d3a8abe373 --- /dev/null +++ b/jdk/test/java/time/test/java/time/TestPeriodParser.java @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2009-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time; + +import java.time.*; + +import static java.time.temporal.ChronoUnit.DAYS; +import static java.time.temporal.ChronoUnit.HOURS; +import static java.time.temporal.ChronoUnit.MINUTES; +import static java.time.temporal.ChronoUnit.MONTHS; +import static java.time.temporal.ChronoUnit.NANOS; +import static java.time.temporal.ChronoUnit.SECONDS; +import static java.time.temporal.ChronoUnit.YEARS; +import static org.testng.Assert.assertEquals; + +import java.time.format.DateTimeParseException; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test PeriodParser. + */ +@Test +public class TestPeriodParser { + + //----------------------------------------------------------------------- + // parse(String) + //----------------------------------------------------------------------- + @DataProvider(name="Parse") + Object[][] provider_factory_parse() { + return new Object[][] { + {"Pt0S", Period.ZERO}, + {"pT0S", Period.ZERO}, + {"PT0S", Period.ZERO}, + {"Pt0s", Period.ZERO}, + {"pt0s", Period.ZERO}, + {"P0Y0M0DT0H0M0.0S", Period.ZERO}, + + {"P1Y", Period.of(1, YEARS)}, + {"P100Y", Period.of(100, YEARS)}, + {"P-25Y", Period.of(-25, YEARS)}, + {"P" + Integer.MAX_VALUE + "Y", Period.of(Integer.MAX_VALUE, YEARS)}, + {"P" + Integer.MIN_VALUE + "Y", Period.of(Integer.MIN_VALUE, YEARS)}, + + {"P1M", Period.of(1, MONTHS)}, + {"P0M", Period.of(0, MONTHS)}, + {"P-1M", Period.of(-1, MONTHS)}, + {"P" + Integer.MAX_VALUE + "M", Period.of(Integer.MAX_VALUE, MONTHS)}, + {"P" + Integer.MIN_VALUE + "M", Period.of(Integer.MIN_VALUE, MONTHS)}, + + {"P1D", Period.of(1, DAYS)}, + {"P0D", Period.of(0, DAYS)}, + {"P-1D", Period.of(-1, DAYS)}, + {"P" + Integer.MAX_VALUE + "D", Period.of(Integer.MAX_VALUE, DAYS)}, + {"P" + Integer.MIN_VALUE + "D", Period.of(Integer.MIN_VALUE, DAYS)}, + + {"P2Y3M25D", Period.ofDate(2, 3, 25)}, + + {"PT1H", Period.of(1, HOURS)}, + {"PT-1H", Period.of(-1, HOURS)}, + {"PT24H", Period.of(24, HOURS)}, + {"PT-24H", Period.of(-24, HOURS)}, + {"PT" + Integer.MAX_VALUE / (3600 * 8) + "H", Period.of(Integer.MAX_VALUE / (3600 * 8), HOURS)}, + {"PT" + Integer.MIN_VALUE / (3600 * 8) + "H", Period.of(Integer.MIN_VALUE / (3600 * 8), HOURS)}, + + {"PT1M", Period.of(1, MINUTES)}, + {"PT-1M", Period.of(-1, MINUTES)}, + {"PT60M", Period.of(60, MINUTES)}, + {"PT-60M", Period.of(-60, MINUTES)}, + {"PT" + Integer.MAX_VALUE / (60 * 8) + "M", Period.of(Integer.MAX_VALUE / (60 * 8), MINUTES)}, + {"PT" + Integer.MIN_VALUE / (60 * 8) + "M", Period.of(Integer.MIN_VALUE / (60 * 8), MINUTES)}, + + {"PT1S", Period.of(1, SECONDS)}, + {"PT-1S", Period.of(-1, SECONDS)}, + {"PT60S", Period.of(60, SECONDS)}, + {"PT-60S", Period.of(-60, SECONDS)}, + {"PT" + Integer.MAX_VALUE + "S", Period.of(Integer.MAX_VALUE, SECONDS)}, + {"PT" + Integer.MIN_VALUE + "S", Period.of(Integer.MIN_VALUE, SECONDS)}, + + {"PT0.1S", Period.of( 0, 0, 0, 0, 0, 0, 100000000 ) }, + {"PT-0.1S", Period.of( 0, 0, 0, 0, 0, 0, -100000000 ) }, + {"PT1.1S", Period.of( 0, 0, 0, 0, 0, 1, 100000000 ) }, + {"PT-1.1S", Period.of( 0, 0, 0, 0, 0, -1, -100000000 ) }, + {"PT1.0001S", Period.of(1, SECONDS).plus( 100000, NANOS ) }, + {"PT1.0000001S", Period.of(1, SECONDS).plus( 100, NANOS ) }, + {"PT1.123456789S", Period.of( 0, 0, 0, 0, 0, 1, 123456789 ) }, + {"PT1.999999999S", Period.of( 0, 0, 0, 0, 0, 1, 999999999 ) }, + + }; + } + + @Test(dataProvider="Parse") + public void factory_parse(String text, Period expected) { + Period p = Period.parse(text); + assertEquals(p, expected); + } + + @Test(dataProvider="Parse") + public void factory_parse_comma(String text, Period expected) { + if (text.contains(".")) { + text = text.replace('.', ','); + Period p = Period.parse(text); + assertEquals(p, expected); + } + } + + @DataProvider(name="ParseFailures") + Object[][] provider_factory_parseFailures() { + return new Object[][] { + {"", 0}, + {"PTS", 2}, + {"AT0S", 0}, + {"PA0S", 1}, + {"PT0A", 3}, + + {"PT+S", 2}, + {"PT-S", 2}, + {"PT.S", 2}, + {"PTAS", 2}, + + {"PT+0S", 2}, + {"PT-0S", 2}, + {"PT+1S", 2}, + {"PT-.S", 2}, + + {"PT1ABC2S", 3}, + {"PT1.1ABC2S", 5}, + + {"PT123456789123456789123456789S", 2}, + {"PT0.1234567891S", 4}, + {"PT1.S", 2}, + {"PT.1S", 2}, + + {"PT2.-3S", 2}, + {"PT-2.-3S", 2}, + + {"P1Y1MT1DT1M1S", 7}, + {"P1Y1MT1HT1M1S", 8}, + {"P1YMD", 3}, + {"PT1ST1D", 4}, + {"P1Y2Y", 4}, + {"PT1M+3S", 4}, + + {"PT1S1", 4}, + {"PT1S.", 4}, + {"PT1SA", 4}, + {"PT1M1", 4}, + {"PT1M.", 4}, + {"PT1MA", 4}, + }; + } + + @Test(dataProvider="ParseFailures", expectedExceptions=DateTimeParseException.class) + public void factory_parseFailures(String text, int errPos) { + try { + Period.parse(text); + } catch (DateTimeParseException ex) { + assertEquals(ex.getParsedString(), text); + assertEquals(ex.getErrorIndex(), errPos); + throw ex; + } + } + + @Test(dataProvider="ParseFailures", expectedExceptions=DateTimeParseException.class) + public void factory_parseFailures_comma(String text, int errPos) { + text = text.replace('.', ','); + try { + Period.parse(text); + } catch (DateTimeParseException ex) { + assertEquals(ex.getParsedString(), text); + assertEquals(ex.getErrorIndex(), errPos); + throw ex; + } + } + + @Test(expectedExceptions=DateTimeParseException.class) + public void factory_parse_tooBig() { + String text = "PT" + Long.MAX_VALUE + "1S"; + Period.parse(text); + } + + @Test(expectedExceptions=DateTimeParseException.class) + public void factory_parse_tooBig_decimal() { + String text = "PT" + Long.MAX_VALUE + "1.1S"; + Period.parse(text); + } + + @Test(expectedExceptions=DateTimeParseException.class) + public void factory_parse_tooSmall() { + String text = "PT" + Long.MIN_VALUE + "1S"; + Period.parse(text); + } + + @Test(expectedExceptions=DateTimeParseException.class) + public void factory_parse_tooSmall_decimal() { + String text = "PT" + Long.MIN_VALUE + ".1S"; + Period.parse(text); + } + + @Test(expectedExceptions=NullPointerException.class) + public void factory_parse_null() { + Period.parse(null); + } + + @DataProvider(name="ParseSequenceFailures") + Object[][] provider_factory_parseSequenceFailures() { + return new Object[][] { + {"P0M0Y0DT0H0M0.0S"}, + {"P0M0D0YT0H0M0.0S"}, + {"P0S0D0YT0S0M0.0H"}, + {"PT0M0H0.0S"}, + {"PT0M0H"}, + {"PT0S0M"}, + {"PT0.0M2S"}, + }; + } + + @Test(dataProvider="ParseSequenceFailures", expectedExceptions=DateTimeParseException.class) + public void factory_parse_badSequence(String text) { + Period.parse(text); + } + +} diff --git a/jdk/test/java/time/test/java/time/TestZoneId.java b/jdk/test/java/time/test/java/time/TestZoneId.java new file mode 100644 index 00000000000..5e9380391df --- /dev/null +++ b/jdk/test/java/time/test/java/time/TestZoneId.java @@ -0,0 +1,1084 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time; + +import java.time.*; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.SimpleTimeZone; +import java.util.TimeZone; + +import java.time.temporal.Queries; +import java.time.temporal.TemporalQuery; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalField; +import java.time.format.TextStyle; +import java.time.zone.ZoneOffsetTransition; +import java.time.zone.ZoneRules; +import java.time.zone.ZoneRulesException; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test ZoneId. + */ +@Test +public class TestZoneId extends AbstractTest { + + private static final ZoneId ZONE_PARIS = ZoneId.of("Europe/Paris"); + public static final String LATEST_TZDB = "2010i"; + private static final int OVERLAP = 2; + private static final int GAP = 0; + + //----------------------------------------------------------------------- + // Basics + //----------------------------------------------------------------------- + public void test_immutable() { + Class cls = ZoneId.class; + assertTrue(Modifier.isPublic(cls.getModifiers())); + Field[] fields = cls.getDeclaredFields(); + for (Field field : fields) { + if (Modifier.isStatic(field.getModifiers()) == false) { + assertTrue(Modifier.isPrivate(field.getModifiers())); + assertTrue(Modifier.isFinal(field.getModifiers()) || + (Modifier.isVolatile(field.getModifiers()) && Modifier.isTransient(field.getModifiers()))); + } + } + } + + public void test_serialization_UTC() throws Exception { + ZoneId test = ZoneOffset.UTC; + assertSerializableAndSame(test); + } + + public void test_serialization_fixed() throws Exception { + ZoneId test = ZoneId.of("UTC+01:30"); + assertSerializable(test); + } + + public void test_serialization_Europe() throws Exception { + ZoneId test = ZoneId.of("Europe/London"); + assertSerializable(test); + } + + public void test_serialization_America() throws Exception { + ZoneId test = ZoneId.of("America/Chicago"); + assertSerializable(test); + } + + //----------------------------------------------------------------------- + // UTC + //----------------------------------------------------------------------- + public void test_constant_UTC() { + ZoneId test = ZoneOffset.UTC; + assertEquals(test.getId(), "Z"); + assertEquals(test.getText(TextStyle.FULL, Locale.UK), "Z"); + assertEquals(test.getRules().isFixedOffset(), true); + assertEquals(test.getRules().getOffset(Instant.ofEpochSecond(0L)), ZoneOffset.UTC); + checkOffset(test.getRules(), createLDT(2008, 6, 30), ZoneOffset.UTC, 1); + assertSame(test, ZoneId.of("UTC+00")); + } + + //----------------------------------------------------------------------- + // OLD_IDS_PRE_2005 + //----------------------------------------------------------------------- + public void test_constant_OLD_IDS_PRE_2005() { + Map ids = ZoneId.OLD_IDS_PRE_2005; + assertEquals(ids.get("EST"), "America/Indianapolis"); + assertEquals(ids.get("MST"), "America/Phoenix"); + assertEquals(ids.get("HST"), "Pacific/Honolulu"); + assertEquals(ids.get("ACT"), "Australia/Darwin"); + assertEquals(ids.get("AET"), "Australia/Sydney"); + assertEquals(ids.get("AGT"), "America/Argentina/Buenos_Aires"); + assertEquals(ids.get("ART"), "Africa/Cairo"); + assertEquals(ids.get("AST"), "America/Anchorage"); + assertEquals(ids.get("BET"), "America/Sao_Paulo"); + assertEquals(ids.get("BST"), "Asia/Dhaka"); + assertEquals(ids.get("CAT"), "Africa/Harare"); + assertEquals(ids.get("CNT"), "America/St_Johns"); + assertEquals(ids.get("CST"), "America/Chicago"); + assertEquals(ids.get("CTT"), "Asia/Shanghai"); + assertEquals(ids.get("EAT"), "Africa/Addis_Ababa"); + assertEquals(ids.get("ECT"), "Europe/Paris"); + assertEquals(ids.get("IET"), "America/Indiana/Indianapolis"); + assertEquals(ids.get("IST"), "Asia/Kolkata"); + assertEquals(ids.get("JST"), "Asia/Tokyo"); + assertEquals(ids.get("MIT"), "Pacific/Apia"); + assertEquals(ids.get("NET"), "Asia/Yerevan"); + assertEquals(ids.get("NST"), "Pacific/Auckland"); + assertEquals(ids.get("PLT"), "Asia/Karachi"); + assertEquals(ids.get("PNT"), "America/Phoenix"); + assertEquals(ids.get("PRT"), "America/Puerto_Rico"); + assertEquals(ids.get("PST"), "America/Los_Angeles"); + assertEquals(ids.get("SST"), "Pacific/Guadalcanal"); + assertEquals(ids.get("VST"), "Asia/Ho_Chi_Minh"); + } + + @Test(expectedExceptions=UnsupportedOperationException.class) + public void test_constant_OLD_IDS_PRE_2005_immutable() { + Map ids = ZoneId.OLD_IDS_PRE_2005; + ids.clear(); + } + + //----------------------------------------------------------------------- + // OLD_IDS_POST_2005 + //----------------------------------------------------------------------- + public void test_constant_OLD_IDS_POST_2005() { + Map ids = ZoneId.OLD_IDS_POST_2005; + assertEquals(ids.get("EST"), "-05:00"); + assertEquals(ids.get("MST"), "-07:00"); + assertEquals(ids.get("HST"), "-10:00"); + assertEquals(ids.get("ACT"), "Australia/Darwin"); + assertEquals(ids.get("AET"), "Australia/Sydney"); + assertEquals(ids.get("AGT"), "America/Argentina/Buenos_Aires"); + assertEquals(ids.get("ART"), "Africa/Cairo"); + assertEquals(ids.get("AST"), "America/Anchorage"); + assertEquals(ids.get("BET"), "America/Sao_Paulo"); + assertEquals(ids.get("BST"), "Asia/Dhaka"); + assertEquals(ids.get("CAT"), "Africa/Harare"); + assertEquals(ids.get("CNT"), "America/St_Johns"); + assertEquals(ids.get("CST"), "America/Chicago"); + assertEquals(ids.get("CTT"), "Asia/Shanghai"); + assertEquals(ids.get("EAT"), "Africa/Addis_Ababa"); + assertEquals(ids.get("ECT"), "Europe/Paris"); + assertEquals(ids.get("IET"), "America/Indiana/Indianapolis"); + assertEquals(ids.get("IST"), "Asia/Kolkata"); + assertEquals(ids.get("JST"), "Asia/Tokyo"); + assertEquals(ids.get("MIT"), "Pacific/Apia"); + assertEquals(ids.get("NET"), "Asia/Yerevan"); + assertEquals(ids.get("NST"), "Pacific/Auckland"); + assertEquals(ids.get("PLT"), "Asia/Karachi"); + assertEquals(ids.get("PNT"), "America/Phoenix"); + assertEquals(ids.get("PRT"), "America/Puerto_Rico"); + assertEquals(ids.get("PST"), "America/Los_Angeles"); + assertEquals(ids.get("SST"), "Pacific/Guadalcanal"); + assertEquals(ids.get("VST"), "Asia/Ho_Chi_Minh"); + } + + @Test(expectedExceptions=UnsupportedOperationException.class) + public void test_constant_OLD_IDS_POST_2005_immutable() { + Map ids = ZoneId.OLD_IDS_POST_2005; + ids.clear(); + } + + //----------------------------------------------------------------------- + // system default + //----------------------------------------------------------------------- + public void test_systemDefault() { + ZoneId test = ZoneId.systemDefault(); + assertEquals(test.getId(), TimeZone.getDefault().getID()); + } + + @Test(expectedExceptions = DateTimeException.class) + public void test_systemDefault_unableToConvert_badFormat() { + TimeZone current = TimeZone.getDefault(); + try { + TimeZone.setDefault(new SimpleTimeZone(127, "Something Weird")); + ZoneId.systemDefault(); + } finally { + TimeZone.setDefault(current); + } + } + + @Test(expectedExceptions = ZoneRulesException.class) + public void test_systemDefault_unableToConvert_unknownId() { + TimeZone current = TimeZone.getDefault(); + try { + TimeZone.setDefault(new SimpleTimeZone(127, "SomethingWeird")); + ZoneId.systemDefault(); + } finally { + TimeZone.setDefault(current); + } + } + + //----------------------------------------------------------------------- + // mapped factory + //----------------------------------------------------------------------- + public void test_of_string_Map() { + Map map = new HashMap(); + map.put("LONDON", "Europe/London"); + map.put("PARIS", "Europe/Paris"); + ZoneId test = ZoneId.of("LONDON", map); + assertEquals(test.getId(), "Europe/London"); + } + + public void test_of_string_Map_lookThrough() { + Map map = new HashMap(); + map.put("LONDON", "Europe/London"); + map.put("PARIS", "Europe/Paris"); + ZoneId test = ZoneId.of("Europe/Madrid", map); + assertEquals(test.getId(), "Europe/Madrid"); + } + + public void test_of_string_Map_emptyMap() { + Map map = new HashMap(); + ZoneId test = ZoneId.of("Europe/Madrid", map); + assertEquals(test.getId(), "Europe/Madrid"); + } + + @Test(expectedExceptions=DateTimeException.class) + public void test_of_string_Map_badFormat() { + Map map = new HashMap(); + ZoneId.of("Not kknown", map); + } + + @Test(expectedExceptions=ZoneRulesException.class) + public void test_of_string_Map_unknown() { + Map map = new HashMap(); + ZoneId.of("Unknown", map); + } + + //----------------------------------------------------------------------- + // regular factory + //----------------------------------------------------------------------- + @DataProvider(name="String_UTC") + Object[][] data_of_string_UTC() { + return new Object[][] { + {""}, {"Z"}, + {"+00"},{"+0000"},{"+00:00"},{"+000000"},{"+00:00:00"}, + {"-00"},{"-0000"},{"-00:00"},{"-000000"},{"-00:00:00"}, + }; + } + + @Test(dataProvider="String_UTC") + public void test_of_string_UTC(String id) { + ZoneId test = ZoneId.of("UTC" + id); + assertSame(test, ZoneOffset.UTC); + } + + @Test(dataProvider="String_UTC") + public void test_of_string_GMT(String id) { + ZoneId test = ZoneId.of("GMT" + id); + assertSame(test, ZoneOffset.UTC); + } + + //----------------------------------------------------------------------- + @DataProvider(name="String_Fixed") + Object[][] data_of_string_Fixed() { + return new Object[][] { + {"Z", "Z"}, + {"+0", "Z"}, + {"+5", "+05:00"}, + {"+01", "+01:00"}, + {"+0100", "+01:00"},{"+01:00", "+01:00"}, + {"+010000", "+01:00"},{"+01:00:00", "+01:00"}, + {"+12", "+12:00"}, + {"+1234", "+12:34"},{"+12:34", "+12:34"}, + {"+123456", "+12:34:56"},{"+12:34:56", "+12:34:56"}, + {"-02", "-02:00"}, + {"-5", "-05:00"}, + {"-0200", "-02:00"},{"-02:00", "-02:00"}, + {"-020000", "-02:00"},{"-02:00:00", "-02:00"}, + }; + } + + @Test(dataProvider="String_Fixed") + public void test_of_string_offset(String input, String id) { + ZoneId test = ZoneId.of(input); + assertEquals(test.getId(), id); + assertEquals(test.getText(TextStyle.FULL, Locale.UK), id); + assertEquals(test.getRules().isFixedOffset(), true); + ZoneOffset offset = ZoneOffset.of(id); + assertEquals(test.getRules().getOffset(Instant.ofEpochSecond(0L)), offset); + checkOffset(test.getRules(), createLDT(2008, 6, 30), offset, 1); + } + + @Test(dataProvider="String_Fixed") + public void test_of_string_FixedUTC(String input, String id) { + ZoneId test = ZoneId.of("UTC" + input); + assertEquals(test.getId(), id); + assertEquals(test.getText(TextStyle.FULL, Locale.UK), id); + assertEquals(test.getRules().isFixedOffset(), true); + ZoneOffset offset = ZoneOffset.of(id); + assertEquals(test.getRules().getOffset(Instant.ofEpochSecond(0L)), offset); + checkOffset(test.getRules(), createLDT(2008, 6, 30), offset, 1); + } + + @Test(dataProvider="String_Fixed") + public void test_of_string_FixedGMT(String input, String id) { + ZoneId test = ZoneId.of("GMT" + input); + assertEquals(test.getId(), id); + assertEquals(test.getText(TextStyle.FULL, Locale.UK), id); + assertEquals(test.getRules().isFixedOffset(), true); + ZoneOffset offset = ZoneOffset.of(id); + assertEquals(test.getRules().getOffset(Instant.ofEpochSecond(0L)), offset); + checkOffset(test.getRules(), createLDT(2008, 6, 30), offset, 1); + } + + //----------------------------------------------------------------------- + @DataProvider(name="String_UTC_Invalid") + Object[][] data_of_string_UTC_invalid() { + return new Object[][] { + {"A"}, {"B"}, {"C"}, {"D"}, {"E"}, {"F"}, {"G"}, {"H"}, {"I"}, {"J"}, {"K"}, {"L"}, {"M"}, + {"N"}, {"O"}, {"P"}, {"Q"}, {"R"}, {"S"}, {"T"}, {"U"}, {"V"}, {"W"}, {"X"}, {"Y"}, + {"+0:00"}, {"+00:0"}, {"+0:0"}, + {"+000"}, {"+00000"}, + {"+0:00:00"}, {"+00:0:00"}, {"+00:00:0"}, {"+0:0:0"}, {"+0:0:00"}, {"+00:0:0"}, {"+0:00:0"}, + {"+01_00"}, {"+01;00"}, {"+01@00"}, {"+01:AA"}, + {"+19"}, {"+19:00"}, {"+18:01"}, {"+18:00:01"}, {"+1801"}, {"+180001"}, + {"-0:00"}, {"-00:0"}, {"-0:0"}, + {"-000"}, {"-00000"}, + {"-0:00:00"}, {"-00:0:00"}, {"-00:00:0"}, {"-0:0:0"}, {"-0:0:00"}, {"-00:0:0"}, {"-0:00:0"}, + {"-19"}, {"-19:00"}, {"-18:01"}, {"-18:00:01"}, {"-1801"}, {"-180001"}, + {"-01_00"}, {"-01;00"}, {"-01@00"}, {"-01:AA"}, + {"@01:00"}, + }; + } + + @Test(dataProvider="String_UTC_Invalid", expectedExceptions=DateTimeException.class) + public void test_of_string_UTC_invalid(String id) { + ZoneId.of("UTC" + id); + } + + @Test(dataProvider="String_UTC_Invalid", expectedExceptions=DateTimeException.class) + public void test_of_string_GMT_invalid(String id) { + ZoneId.of("GMT" + id); + } + + //----------------------------------------------------------------------- + @DataProvider(name="String_Invalid") + Object[][] data_of_string_invalid() { + // \u00ef is a random unicode character + return new Object[][] { + {""}, {":"}, {"#"}, + {"\u00ef"}, {"`"}, {"!"}, {"\""}, {"\u00ef"}, {"$"}, {"^"}, {"&"}, {"*"}, {"("}, {")"}, {"="}, + {"\\"}, {"|"}, {","}, {"<"}, {">"}, {"?"}, {";"}, {"'"}, {"["}, {"]"}, {"{"}, {"}"}, + {"\u00ef:A"}, {"`:A"}, {"!:A"}, {"\":A"}, {"\u00ef:A"}, {"$:A"}, {"^:A"}, {"&:A"}, {"*:A"}, {"(:A"}, {"):A"}, {"=:A"}, {"+:A"}, + {"\\:A"}, {"|:A"}, {",:A"}, {"<:A"}, {">:A"}, {"?:A"}, {";:A"}, {"::A"}, {"':A"}, {"@:A"}, {"~:A"}, {"[:A"}, {"]:A"}, {"{:A"}, {"}:A"}, + {"A:B#\u00ef"}, {"A:B#`"}, {"A:B#!"}, {"A:B#\""}, {"A:B#\u00ef"}, {"A:B#$"}, {"A:B#^"}, {"A:B#&"}, {"A:B#*"}, + {"A:B#("}, {"A:B#)"}, {"A:B#="}, {"A:B#+"}, + {"A:B#\\"}, {"A:B#|"}, {"A:B#,"}, {"A:B#<"}, {"A:B#>"}, {"A:B#?"}, {"A:B#;"}, {"A:B#:"}, + {"A:B#'"}, {"A:B#@"}, {"A:B#~"}, {"A:B#["}, {"A:B#]"}, {"A:B#{"}, {"A:B#}"}, + }; + } + + @Test(dataProvider="String_Invalid", expectedExceptions=DateTimeException.class) + public void test_of_string_invalid(String id) { + ZoneId.of(id); + } + + //----------------------------------------------------------------------- + public void test_of_string_GMT0() { + ZoneId test = ZoneId.of("GMT0"); + assertEquals(test.getId(), "Z"); + assertEquals(test.getRules().isFixedOffset(), true); + } + + //----------------------------------------------------------------------- + public void test_of_string_London() { + ZoneId test = ZoneId.of("Europe/London"); + assertEquals(test.getId(), "Europe/London"); + assertEquals(test.getRules().isFixedOffset(), false); + } + + //----------------------------------------------------------------------- + @Test(expectedExceptions=NullPointerException.class) + public void test_of_string_null() { + ZoneId.of((String) null); + } + + @Test(expectedExceptions=ZoneRulesException.class) + public void test_of_string_unknown_simple() { + ZoneId.of("Unknown"); + } + + //------------------------------------------------------------------------- + // TODO: test by deserialization +// public void test_ofUnchecked_string_invalidNotChecked() { +// ZoneRegion test = ZoneRegion.ofLenient("Unknown"); +// assertEquals(test.getId(), "Unknown"); +// } +// +// public void test_ofUnchecked_string_invalidNotChecked_unusualCharacters() { +// ZoneRegion test = ZoneRegion.ofLenient("QWERTYUIOPASDFGHJKLZXCVBNM~/._+-"); +// assertEquals(test.getId(), "QWERTYUIOPASDFGHJKLZXCVBNM~/._+-"); +// } + + //----------------------------------------------------------------------- + // from(TemporalAccessor) + //----------------------------------------------------------------------- + public void test_factory_from_DateTimeAccessor_zoneId() { + TemporalAccessor mock = new TemporalAccessor() { + @Override + public boolean isSupported(TemporalField field) { + return false; + } + + @Override + public long getLong(TemporalField field) { + throw new DateTimeException("Mock"); + } + + @Override + public R query(TemporalQuery query) { + if (query == Queries.zoneId()) { + return (R) ZONE_PARIS; + } + return TemporalAccessor.super.query(query); + } + }; + assertEquals(ZoneId.from(mock), ZONE_PARIS); + } + + public void test_factory_from_DateTimeAccessor_offset() { + ZoneOffset offset = ZoneOffset.ofHours(1); + assertEquals(ZoneId.from(offset), offset); + } + + @Test(expectedExceptions=DateTimeException.class) + public void test_factory_from_DateTimeAccessor_invalid_noDerive() { + ZoneId.from(LocalTime.of(12, 30)); + } + + @Test(expectedExceptions=NullPointerException.class) + public void test_factory_from_DateTimeAccessor_null() { + ZoneId.from((TemporalAccessor) null); + } + + //----------------------------------------------------------------------- + // Europe/London + //----------------------------------------------------------------------- + public void test_London() { + ZoneId test = ZoneId.of("Europe/London"); + assertEquals(test.getId(), "Europe/London"); + assertEquals(test.getRules().isFixedOffset(), false); + } + + public void test_London_getOffset() { + ZoneId test = ZoneId.of("Europe/London"); + assertEquals(test.getRules().getOffset(createInstant(2008, 1, 1, ZoneOffset.UTC)), ZoneOffset.ofHours(0)); + assertEquals(test.getRules().getOffset(createInstant(2008, 2, 1, ZoneOffset.UTC)), ZoneOffset.ofHours(0)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 1, ZoneOffset.UTC)), ZoneOffset.ofHours(0)); + assertEquals(test.getRules().getOffset(createInstant(2008, 4, 1, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 5, 1, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 6, 1, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 7, 1, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 8, 1, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 9, 1, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 10, 1, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 11, 1, ZoneOffset.UTC)), ZoneOffset.ofHours(0)); + assertEquals(test.getRules().getOffset(createInstant(2008, 12, 1, ZoneOffset.UTC)), ZoneOffset.ofHours(0)); + } + + public void test_London_getOffset_toDST() { + ZoneId test = ZoneId.of("Europe/London"); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 24, ZoneOffset.UTC)), ZoneOffset.ofHours(0)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 25, ZoneOffset.UTC)), ZoneOffset.ofHours(0)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 26, ZoneOffset.UTC)), ZoneOffset.ofHours(0)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 27, ZoneOffset.UTC)), ZoneOffset.ofHours(0)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 28, ZoneOffset.UTC)), ZoneOffset.ofHours(0)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 29, ZoneOffset.UTC)), ZoneOffset.ofHours(0)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 30, ZoneOffset.UTC)), ZoneOffset.ofHours(0)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 31, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + // cutover at 01:00Z + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 30, 0, 59, 59, 999999999, ZoneOffset.UTC)), ZoneOffset.ofHours(0)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 30, 1, 0, 0, 0, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + } + + public void test_London_getOffset_fromDST() { + ZoneId test = ZoneId.of("Europe/London"); + assertEquals(test.getRules().getOffset(createInstant(2008, 10, 24, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 10, 25, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 10, 26, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 10, 27, ZoneOffset.UTC)), ZoneOffset.ofHours(0)); + assertEquals(test.getRules().getOffset(createInstant(2008, 10, 28, ZoneOffset.UTC)), ZoneOffset.ofHours(0)); + assertEquals(test.getRules().getOffset(createInstant(2008, 10, 29, ZoneOffset.UTC)), ZoneOffset.ofHours(0)); + assertEquals(test.getRules().getOffset(createInstant(2008, 10, 30, ZoneOffset.UTC)), ZoneOffset.ofHours(0)); + assertEquals(test.getRules().getOffset(createInstant(2008, 10, 31, ZoneOffset.UTC)), ZoneOffset.ofHours(0)); + // cutover at 01:00Z + assertEquals(test.getRules().getOffset(createInstant(2008, 10, 26, 0, 59, 59, 999999999, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 10, 26, 1, 0, 0, 0, ZoneOffset.UTC)), ZoneOffset.ofHours(0)); + } + + public void test_London_getOffsetInfo() { + ZoneId test = ZoneId.of("Europe/London"); + checkOffset(test.getRules(), createLDT(2008, 1, 1), ZoneOffset.ofHours(0), 1); + checkOffset(test.getRules(), createLDT(2008, 2, 1), ZoneOffset.ofHours(0), 1); + checkOffset(test.getRules(), createLDT(2008, 3, 1), ZoneOffset.ofHours(0), 1); + checkOffset(test.getRules(), createLDT(2008, 4, 1), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 5, 1), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 6, 1), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 7, 1), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 8, 1), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 9, 1), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 10, 1), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 11, 1), ZoneOffset.ofHours(0), 1); + checkOffset(test.getRules(), createLDT(2008, 12, 1), ZoneOffset.ofHours(0), 1); + } + + public void test_London_getOffsetInfo_toDST() { + ZoneId test = ZoneId.of("Europe/London"); + checkOffset(test.getRules(), createLDT(2008, 3, 24), ZoneOffset.ofHours(0), 1); + checkOffset(test.getRules(), createLDT(2008, 3, 25), ZoneOffset.ofHours(0), 1); + checkOffset(test.getRules(), createLDT(2008, 3, 26), ZoneOffset.ofHours(0), 1); + checkOffset(test.getRules(), createLDT(2008, 3, 27), ZoneOffset.ofHours(0), 1); + checkOffset(test.getRules(), createLDT(2008, 3, 28), ZoneOffset.ofHours(0), 1); + checkOffset(test.getRules(), createLDT(2008, 3, 29), ZoneOffset.ofHours(0), 1); + checkOffset(test.getRules(), createLDT(2008, 3, 30), ZoneOffset.ofHours(0), 1); + checkOffset(test.getRules(), createLDT(2008, 3, 31), ZoneOffset.ofHours(1), 1); + // cutover at 01:00Z + checkOffset(test.getRules(), LocalDateTime.of(2008, 3, 30, 0, 59, 59, 999999999), ZoneOffset.ofHours(0), 1); + checkOffset(test.getRules(), LocalDateTime.of(2008, 3, 30, 1, 30, 0, 0), ZoneOffset.ofHours(0), GAP); + checkOffset(test.getRules(), LocalDateTime.of(2008, 3, 30, 2, 0, 0, 0), ZoneOffset.ofHours(1), 1); + } + + public void test_London_getOffsetInfo_fromDST() { + ZoneId test = ZoneId.of("Europe/London"); + checkOffset(test.getRules(), createLDT(2008, 10, 24), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 10, 25), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 10, 26), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 10, 27), ZoneOffset.ofHours(0), 1); + checkOffset(test.getRules(), createLDT(2008, 10, 28), ZoneOffset.ofHours(0), 1); + checkOffset(test.getRules(), createLDT(2008, 10, 29), ZoneOffset.ofHours(0), 1); + checkOffset(test.getRules(), createLDT(2008, 10, 30), ZoneOffset.ofHours(0), 1); + checkOffset(test.getRules(), createLDT(2008, 10, 31), ZoneOffset.ofHours(0), 1); + // cutover at 01:00Z + checkOffset(test.getRules(), LocalDateTime.of(2008, 10, 26, 0, 59, 59, 999999999), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), LocalDateTime.of(2008, 10, 26, 1, 30, 0, 0), ZoneOffset.ofHours(1), OVERLAP); + checkOffset(test.getRules(), LocalDateTime.of(2008, 10, 26, 2, 0, 0, 0), ZoneOffset.ofHours(0), 1); + } + + public void test_London_getOffsetInfo_gap() { + ZoneId test = ZoneId.of("Europe/London"); + final LocalDateTime dateTime = LocalDateTime.of(2008, 3, 30, 1, 0, 0, 0); + ZoneOffsetTransition trans = checkOffset(test.getRules(), dateTime, ZoneOffset.ofHours(0), GAP); + assertEquals(trans.isGap(), true); + assertEquals(trans.isOverlap(), false); + assertEquals(trans.getOffsetBefore(), ZoneOffset.ofHours(0)); + assertEquals(trans.getOffsetAfter(), ZoneOffset.ofHours(1)); + assertEquals(trans.getInstant(), dateTime.toInstant(ZoneOffset.UTC)); + assertEquals(trans.getDateTimeBefore(), LocalDateTime.of(2008, 3, 30, 1, 0)); + assertEquals(trans.getDateTimeAfter(), LocalDateTime.of(2008, 3, 30, 2, 0)); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(-1)), false); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(0)), false); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(1)), false); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(2)), false); + assertEquals(trans.toString(), "Transition[Gap at 2008-03-30T01:00Z to +01:00]"); + + assertFalse(trans.equals(null)); + assertFalse(trans.equals(ZoneOffset.ofHours(0))); + assertTrue(trans.equals(trans)); + + final ZoneOffsetTransition otherTrans = test.getRules().getTransition(dateTime); + assertTrue(trans.equals(otherTrans)); + assertEquals(trans.hashCode(), otherTrans.hashCode()); + } + + public void test_London_getOffsetInfo_overlap() { + ZoneId test = ZoneId.of("Europe/London"); + final LocalDateTime dateTime = LocalDateTime.of(2008, 10, 26, 1, 0, 0, 0); + ZoneOffsetTransition trans = checkOffset(test.getRules(), dateTime, ZoneOffset.ofHours(1), OVERLAP); + assertEquals(trans.isGap(), false); + assertEquals(trans.isOverlap(), true); + assertEquals(trans.getOffsetBefore(), ZoneOffset.ofHours(1)); + assertEquals(trans.getOffsetAfter(), ZoneOffset.ofHours(0)); + assertEquals(trans.getInstant(), dateTime.toInstant(ZoneOffset.UTC)); + assertEquals(trans.getDateTimeBefore(), LocalDateTime.of(2008, 10, 26, 2, 0)); + assertEquals(trans.getDateTimeAfter(), LocalDateTime.of(2008, 10, 26, 1, 0)); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(-1)), false); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(0)), true); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(1)), true); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(2)), false); + assertEquals(trans.toString(), "Transition[Overlap at 2008-10-26T02:00+01:00 to Z]"); + + assertFalse(trans.equals(null)); + assertFalse(trans.equals(ZoneOffset.ofHours(1))); + assertTrue(trans.equals(trans)); + + final ZoneOffsetTransition otherTrans = test.getRules().getTransition(dateTime); + assertTrue(trans.equals(otherTrans)); + assertEquals(trans.hashCode(), otherTrans.hashCode()); + } + + //----------------------------------------------------------------------- + // Europe/Paris + //----------------------------------------------------------------------- + public void test_Paris() { + ZoneId test = ZoneId.of("Europe/Paris"); + assertEquals(test.getId(), "Europe/Paris"); + assertEquals(test.getRules().isFixedOffset(), false); + } + + public void test_Paris_getOffset() { + ZoneId test = ZoneId.of("Europe/Paris"); + assertEquals(test.getRules().getOffset(createInstant(2008, 1, 1, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 2, 1, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 1, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 4, 1, ZoneOffset.UTC)), ZoneOffset.ofHours(2)); + assertEquals(test.getRules().getOffset(createInstant(2008, 5, 1, ZoneOffset.UTC)), ZoneOffset.ofHours(2)); + assertEquals(test.getRules().getOffset(createInstant(2008, 6, 1, ZoneOffset.UTC)), ZoneOffset.ofHours(2)); + assertEquals(test.getRules().getOffset(createInstant(2008, 7, 1, ZoneOffset.UTC)), ZoneOffset.ofHours(2)); + assertEquals(test.getRules().getOffset(createInstant(2008, 8, 1, ZoneOffset.UTC)), ZoneOffset.ofHours(2)); + assertEquals(test.getRules().getOffset(createInstant(2008, 9, 1, ZoneOffset.UTC)), ZoneOffset.ofHours(2)); + assertEquals(test.getRules().getOffset(createInstant(2008, 10, 1, ZoneOffset.UTC)), ZoneOffset.ofHours(2)); + assertEquals(test.getRules().getOffset(createInstant(2008, 11, 1, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 12, 1, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + } + + public void test_Paris_getOffset_toDST() { + ZoneId test = ZoneId.of("Europe/Paris"); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 24, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 25, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 26, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 27, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 28, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 29, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 30, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 31, ZoneOffset.UTC)), ZoneOffset.ofHours(2)); + // cutover at 01:00Z + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 30, 0, 59, 59, 999999999, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 30, 1, 0, 0, 0, ZoneOffset.UTC)), ZoneOffset.ofHours(2)); + } + + public void test_Paris_getOffset_fromDST() { + ZoneId test = ZoneId.of("Europe/Paris"); + assertEquals(test.getRules().getOffset(createInstant(2008, 10, 24, ZoneOffset.UTC)), ZoneOffset.ofHours(2)); + assertEquals(test.getRules().getOffset(createInstant(2008, 10, 25, ZoneOffset.UTC)), ZoneOffset.ofHours(2)); + assertEquals(test.getRules().getOffset(createInstant(2008, 10, 26, ZoneOffset.UTC)), ZoneOffset.ofHours(2)); + assertEquals(test.getRules().getOffset(createInstant(2008, 10, 27, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 10, 28, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 10, 29, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 10, 30, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + assertEquals(test.getRules().getOffset(createInstant(2008, 10, 31, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + // cutover at 01:00Z + assertEquals(test.getRules().getOffset(createInstant(2008, 10, 26, 0, 59, 59, 999999999, ZoneOffset.UTC)), ZoneOffset.ofHours(2)); + assertEquals(test.getRules().getOffset(createInstant(2008, 10, 26, 1, 0, 0, 0, ZoneOffset.UTC)), ZoneOffset.ofHours(1)); + } + + public void test_Paris_getOffsetInfo() { + ZoneId test = ZoneId.of("Europe/Paris"); + checkOffset(test.getRules(), createLDT(2008, 1, 1), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 2, 1), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 3, 1), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 4, 1), ZoneOffset.ofHours(2), 1); + checkOffset(test.getRules(), createLDT(2008, 5, 1), ZoneOffset.ofHours(2), 1); + checkOffset(test.getRules(), createLDT(2008, 6, 1), ZoneOffset.ofHours(2), 1); + checkOffset(test.getRules(), createLDT(2008, 7, 1), ZoneOffset.ofHours(2), 1); + checkOffset(test.getRules(), createLDT(2008, 8, 1), ZoneOffset.ofHours(2), 1); + checkOffset(test.getRules(), createLDT(2008, 9, 1), ZoneOffset.ofHours(2), 1); + checkOffset(test.getRules(), createLDT(2008, 10, 1), ZoneOffset.ofHours(2), 1); + checkOffset(test.getRules(), createLDT(2008, 11, 1), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 12, 1), ZoneOffset.ofHours(1), 1); + } + + public void test_Paris_getOffsetInfo_toDST() { + ZoneId test = ZoneId.of("Europe/Paris"); + checkOffset(test.getRules(), createLDT(2008, 3, 24), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 3, 25), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 3, 26), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 3, 27), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 3, 28), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 3, 29), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 3, 30), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 3, 31), ZoneOffset.ofHours(2), 1); + // cutover at 01:00Z which is 02:00+01:00(local Paris time) + checkOffset(test.getRules(), LocalDateTime.of(2008, 3, 30, 1, 59, 59, 999999999), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), LocalDateTime.of(2008, 3, 30, 2, 30, 0, 0), ZoneOffset.ofHours(1), GAP); + checkOffset(test.getRules(), LocalDateTime.of(2008, 3, 30, 3, 0, 0, 0), ZoneOffset.ofHours(2), 1); + } + + public void test_Paris_getOffsetInfo_fromDST() { + ZoneId test = ZoneId.of("Europe/Paris"); + checkOffset(test.getRules(), createLDT(2008, 10, 24), ZoneOffset.ofHours(2), 1); + checkOffset(test.getRules(), createLDT(2008, 10, 25), ZoneOffset.ofHours(2), 1); + checkOffset(test.getRules(), createLDT(2008, 10, 26), ZoneOffset.ofHours(2), 1); + checkOffset(test.getRules(), createLDT(2008, 10, 27), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 10, 28), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 10, 29), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 10, 30), ZoneOffset.ofHours(1), 1); + checkOffset(test.getRules(), createLDT(2008, 10, 31), ZoneOffset.ofHours(1), 1); + // cutover at 01:00Z which is 02:00+01:00(local Paris time) + checkOffset(test.getRules(), LocalDateTime.of(2008, 10, 26, 1, 59, 59, 999999999), ZoneOffset.ofHours(2), 1); + checkOffset(test.getRules(), LocalDateTime.of(2008, 10, 26, 2, 30, 0, 0), ZoneOffset.ofHours(2), OVERLAP); + checkOffset(test.getRules(), LocalDateTime.of(2008, 10, 26, 3, 0, 0, 0), ZoneOffset.ofHours(1), 1); + } + + public void test_Paris_getOffsetInfo_gap() { + ZoneId test = ZoneId.of("Europe/Paris"); + final LocalDateTime dateTime = LocalDateTime.of(2008, 3, 30, 2, 0, 0, 0); + ZoneOffsetTransition trans = checkOffset(test.getRules(), dateTime, ZoneOffset.ofHours(1), GAP); + assertEquals(trans.isGap(), true); + assertEquals(trans.isOverlap(), false); + assertEquals(trans.getOffsetBefore(), ZoneOffset.ofHours(1)); + assertEquals(trans.getOffsetAfter(), ZoneOffset.ofHours(2)); + assertEquals(trans.getInstant(), createInstant(2008, 3, 30, 1, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(0)), false); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(1)), false); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(2)), false); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(3)), false); + assertEquals(trans.toString(), "Transition[Gap at 2008-03-30T02:00+01:00 to +02:00]"); + + assertFalse(trans.equals(null)); + assertFalse(trans.equals(ZoneOffset.ofHours(1))); + assertTrue(trans.equals(trans)); + + final ZoneOffsetTransition otherDis = test.getRules().getTransition(dateTime); + assertTrue(trans.equals(otherDis)); + assertEquals(trans.hashCode(), otherDis.hashCode()); + } + + public void test_Paris_getOffsetInfo_overlap() { + ZoneId test = ZoneId.of("Europe/Paris"); + final LocalDateTime dateTime = LocalDateTime.of(2008, 10, 26, 2, 0, 0, 0); + ZoneOffsetTransition trans = checkOffset(test.getRules(), dateTime, ZoneOffset.ofHours(2), OVERLAP); + assertEquals(trans.isGap(), false); + assertEquals(trans.isOverlap(), true); + assertEquals(trans.getOffsetBefore(), ZoneOffset.ofHours(2)); + assertEquals(trans.getOffsetAfter(), ZoneOffset.ofHours(1)); + assertEquals(trans.getInstant(), createInstant(2008, 10, 26, 1, 0, 0, 0, ZoneOffset.UTC)); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(0)), false); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(1)), true); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(2)), true); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(3)), false); + assertEquals(trans.toString(), "Transition[Overlap at 2008-10-26T03:00+02:00 to +01:00]"); + + assertFalse(trans.equals(null)); + assertFalse(trans.equals(ZoneOffset.ofHours(2))); + assertTrue(trans.equals(trans)); + + final ZoneOffsetTransition otherDis = test.getRules().getTransition(dateTime); + assertTrue(trans.equals(otherDis)); + assertEquals(trans.hashCode(), otherDis.hashCode()); + } + + //----------------------------------------------------------------------- + // America/New_York + //----------------------------------------------------------------------- + public void test_NewYork() { + ZoneId test = ZoneId.of("America/New_York"); + assertEquals(test.getId(), "America/New_York"); + assertEquals(test.getRules().isFixedOffset(), false); + } + + public void test_NewYork_getOffset() { + ZoneId test = ZoneId.of("America/New_York"); + ZoneOffset offset = ZoneOffset.ofHours(-5); + assertEquals(test.getRules().getOffset(createInstant(2008, 1, 1, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getRules().getOffset(createInstant(2008, 2, 1, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 1, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getRules().getOffset(createInstant(2008, 4, 1, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getRules().getOffset(createInstant(2008, 5, 1, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getRules().getOffset(createInstant(2008, 6, 1, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getRules().getOffset(createInstant(2008, 7, 1, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getRules().getOffset(createInstant(2008, 8, 1, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getRules().getOffset(createInstant(2008, 9, 1, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getRules().getOffset(createInstant(2008, 10, 1, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getRules().getOffset(createInstant(2008, 11, 1, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getRules().getOffset(createInstant(2008, 12, 1, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getRules().getOffset(createInstant(2008, 1, 28, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getRules().getOffset(createInstant(2008, 2, 28, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 28, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getRules().getOffset(createInstant(2008, 4, 28, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getRules().getOffset(createInstant(2008, 5, 28, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getRules().getOffset(createInstant(2008, 6, 28, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getRules().getOffset(createInstant(2008, 7, 28, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getRules().getOffset(createInstant(2008, 8, 28, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getRules().getOffset(createInstant(2008, 9, 28, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getRules().getOffset(createInstant(2008, 10, 28, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getRules().getOffset(createInstant(2008, 11, 28, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getRules().getOffset(createInstant(2008, 12, 28, offset)), ZoneOffset.ofHours(-5)); + } + + public void test_NewYork_getOffset_toDST() { + ZoneId test = ZoneId.of("America/New_York"); + ZoneOffset offset = ZoneOffset.ofHours(-5); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 8, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 9, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 10, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 11, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 12, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 13, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 14, offset)), ZoneOffset.ofHours(-4)); + // cutover at 02:00 local + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 9, 1, 59, 59, 999999999, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getRules().getOffset(createInstant(2008, 3, 9, 2, 0, 0, 0, offset)), ZoneOffset.ofHours(-4)); + } + + public void test_NewYork_getOffset_fromDST() { + ZoneId test = ZoneId.of("America/New_York"); + ZoneOffset offset = ZoneOffset.ofHours(-4); + assertEquals(test.getRules().getOffset(createInstant(2008, 11, 1, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getRules().getOffset(createInstant(2008, 11, 2, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getRules().getOffset(createInstant(2008, 11, 3, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getRules().getOffset(createInstant(2008, 11, 4, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getRules().getOffset(createInstant(2008, 11, 5, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getRules().getOffset(createInstant(2008, 11, 6, offset)), ZoneOffset.ofHours(-5)); + assertEquals(test.getRules().getOffset(createInstant(2008, 11, 7, offset)), ZoneOffset.ofHours(-5)); + // cutover at 02:00 local + assertEquals(test.getRules().getOffset(createInstant(2008, 11, 2, 1, 59, 59, 999999999, offset)), ZoneOffset.ofHours(-4)); + assertEquals(test.getRules().getOffset(createInstant(2008, 11, 2, 2, 0, 0, 0, offset)), ZoneOffset.ofHours(-5)); + } + + public void test_NewYork_getOffsetInfo() { + ZoneId test = ZoneId.of("America/New_York"); + checkOffset(test.getRules(), createLDT(2008, 1, 1), ZoneOffset.ofHours(-5), 1); + checkOffset(test.getRules(), createLDT(2008, 2, 1), ZoneOffset.ofHours(-5), 1); + checkOffset(test.getRules(), createLDT(2008, 3, 1), ZoneOffset.ofHours(-5), 1); + checkOffset(test.getRules(), createLDT(2008, 4, 1), ZoneOffset.ofHours(-4), 1); + checkOffset(test.getRules(), createLDT(2008, 5, 1), ZoneOffset.ofHours(-4), 1); + checkOffset(test.getRules(), createLDT(2008, 6, 1), ZoneOffset.ofHours(-4), 1); + checkOffset(test.getRules(), createLDT(2008, 7, 1), ZoneOffset.ofHours(-4), 1); + checkOffset(test.getRules(), createLDT(2008, 8, 1), ZoneOffset.ofHours(-4), 1); + checkOffset(test.getRules(), createLDT(2008, 9, 1), ZoneOffset.ofHours(-4), 1); + checkOffset(test.getRules(), createLDT(2008, 10, 1), ZoneOffset.ofHours(-4), 1); + checkOffset(test.getRules(), createLDT(2008, 11, 1), ZoneOffset.ofHours(-4), 1); + checkOffset(test.getRules(), createLDT(2008, 12, 1), ZoneOffset.ofHours(-5), 1); + checkOffset(test.getRules(), createLDT(2008, 1, 28), ZoneOffset.ofHours(-5), 1); + checkOffset(test.getRules(), createLDT(2008, 2, 28), ZoneOffset.ofHours(-5), 1); + checkOffset(test.getRules(), createLDT(2008, 3, 28), ZoneOffset.ofHours(-4), 1); + checkOffset(test.getRules(), createLDT(2008, 4, 28), ZoneOffset.ofHours(-4), 1); + checkOffset(test.getRules(), createLDT(2008, 5, 28), ZoneOffset.ofHours(-4), 1); + checkOffset(test.getRules(), createLDT(2008, 6, 28), ZoneOffset.ofHours(-4), 1); + checkOffset(test.getRules(), createLDT(2008, 7, 28), ZoneOffset.ofHours(-4), 1); + checkOffset(test.getRules(), createLDT(2008, 8, 28), ZoneOffset.ofHours(-4), 1); + checkOffset(test.getRules(), createLDT(2008, 9, 28), ZoneOffset.ofHours(-4), 1); + checkOffset(test.getRules(), createLDT(2008, 10, 28), ZoneOffset.ofHours(-4), 1); + checkOffset(test.getRules(), createLDT(2008, 11, 28), ZoneOffset.ofHours(-5), 1); + checkOffset(test.getRules(), createLDT(2008, 12, 28), ZoneOffset.ofHours(-5), 1); + } + + public void test_NewYork_getOffsetInfo_toDST() { + ZoneId test = ZoneId.of("America/New_York"); + checkOffset(test.getRules(), createLDT(2008, 3, 8), ZoneOffset.ofHours(-5), 1); + checkOffset(test.getRules(), createLDT(2008, 3, 9), ZoneOffset.ofHours(-5), 1); + checkOffset(test.getRules(), createLDT(2008, 3, 10), ZoneOffset.ofHours(-4), 1); + checkOffset(test.getRules(), createLDT(2008, 3, 11), ZoneOffset.ofHours(-4), 1); + checkOffset(test.getRules(), createLDT(2008, 3, 12), ZoneOffset.ofHours(-4), 1); + checkOffset(test.getRules(), createLDT(2008, 3, 13), ZoneOffset.ofHours(-4), 1); + checkOffset(test.getRules(), createLDT(2008, 3, 14), ZoneOffset.ofHours(-4), 1); + // cutover at 02:00 local + checkOffset(test.getRules(), LocalDateTime.of(2008, 3, 9, 1, 59, 59, 999999999), ZoneOffset.ofHours(-5), 1); + checkOffset(test.getRules(), LocalDateTime.of(2008, 3, 9, 2, 30, 0, 0), ZoneOffset.ofHours(-5), GAP); + checkOffset(test.getRules(), LocalDateTime.of(2008, 3, 9, 3, 0, 0, 0), ZoneOffset.ofHours(-4), 1); + } + + public void test_NewYork_getOffsetInfo_fromDST() { + ZoneId test = ZoneId.of("America/New_York"); + checkOffset(test.getRules(), createLDT(2008, 11, 1), ZoneOffset.ofHours(-4), 1); + checkOffset(test.getRules(), createLDT(2008, 11, 2), ZoneOffset.ofHours(-4), 1); + checkOffset(test.getRules(), createLDT(2008, 11, 3), ZoneOffset.ofHours(-5), 1); + checkOffset(test.getRules(), createLDT(2008, 11, 4), ZoneOffset.ofHours(-5), 1); + checkOffset(test.getRules(), createLDT(2008, 11, 5), ZoneOffset.ofHours(-5), 1); + checkOffset(test.getRules(), createLDT(2008, 11, 6), ZoneOffset.ofHours(-5), 1); + checkOffset(test.getRules(), createLDT(2008, 11, 7), ZoneOffset.ofHours(-5), 1); + // cutover at 02:00 local + checkOffset(test.getRules(), LocalDateTime.of(2008, 11, 2, 0, 59, 59, 999999999), ZoneOffset.ofHours(-4), 1); + checkOffset(test.getRules(), LocalDateTime.of(2008, 11, 2, 1, 30, 0, 0), ZoneOffset.ofHours(-4), OVERLAP); + checkOffset(test.getRules(), LocalDateTime.of(2008, 11, 2, 2, 0, 0, 0), ZoneOffset.ofHours(-5), 1); + } + + public void test_NewYork_getOffsetInfo_gap() { + ZoneId test = ZoneId.of("America/New_York"); + final LocalDateTime dateTime = LocalDateTime.of(2008, 3, 9, 2, 0, 0, 0); + ZoneOffsetTransition trans = checkOffset(test.getRules(), dateTime, ZoneOffset.ofHours(-5), GAP); + assertEquals(trans.getOffsetBefore(), ZoneOffset.ofHours(-5)); + assertEquals(trans.getOffsetAfter(), ZoneOffset.ofHours(-4)); + assertEquals(trans.getInstant(), createInstant(2008, 3, 9, 2, 0, 0, 0, ZoneOffset.ofHours(-5))); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(-6)), false); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(-5)), false); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(-4)), false); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(-3)), false); + assertEquals(trans.toString(), "Transition[Gap at 2008-03-09T02:00-05:00 to -04:00]"); + + assertFalse(trans.equals(null)); + assertFalse(trans.equals(ZoneOffset.ofHours(-5))); + assertTrue(trans.equals(trans)); + + final ZoneOffsetTransition otherTrans = test.getRules().getTransition(dateTime); + assertTrue(trans.equals(otherTrans)); + + assertEquals(trans.hashCode(), otherTrans.hashCode()); + } + + public void test_NewYork_getOffsetInfo_overlap() { + ZoneId test = ZoneId.of("America/New_York"); + final LocalDateTime dateTime = LocalDateTime.of(2008, 11, 2, 1, 0, 0, 0); + ZoneOffsetTransition trans = checkOffset(test.getRules(), dateTime, ZoneOffset.ofHours(-4), OVERLAP); + assertEquals(trans.getOffsetBefore(), ZoneOffset.ofHours(-4)); + assertEquals(trans.getOffsetAfter(), ZoneOffset.ofHours(-5)); + assertEquals(trans.getInstant(), createInstant(2008, 11, 2, 2, 0, 0, 0, ZoneOffset.ofHours(-4))); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(-1)), false); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(-5)), true); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(-4)), true); + assertEquals(trans.isValidOffset(ZoneOffset.ofHours(2)), false); + assertEquals(trans.toString(), "Transition[Overlap at 2008-11-02T02:00-04:00 to -05:00]"); + + assertFalse(trans.equals(null)); + assertFalse(trans.equals(ZoneOffset.ofHours(-4))); + assertTrue(trans.equals(trans)); + + final ZoneOffsetTransition otherTrans = test.getRules().getTransition(dateTime); + assertTrue(trans.equals(otherTrans)); + + assertEquals(trans.hashCode(), otherTrans.hashCode()); + } + + //----------------------------------------------------------------------- + // getXxx() isXxx() + //----------------------------------------------------------------------- + public void test_get_Tzdb() { + ZoneId test = ZoneId.of("Europe/London"); + assertEquals(test.getId(), "Europe/London"); + assertEquals(test.getRules().isFixedOffset(), false); + } + + public void test_get_TzdbFixed() { + ZoneId test = ZoneId.of("+01:30"); + assertEquals(test.getId(), "+01:30"); + assertEquals(test.getRules().isFixedOffset(), true); + } + + //----------------------------------------------------------------------- + // equals() / hashCode() + //----------------------------------------------------------------------- + public void test_equals() { + ZoneId test1 = ZoneId.of("Europe/London"); + ZoneId test2 = ZoneId.of("Europe/Paris"); + ZoneId test2b = ZoneId.of("Europe/Paris"); + assertEquals(test1.equals(test2), false); + assertEquals(test2.equals(test1), false); + + assertEquals(test1.equals(test1), true); + assertEquals(test2.equals(test2), true); + assertEquals(test2.equals(test2b), true); + + assertEquals(test1.hashCode() == test1.hashCode(), true); + assertEquals(test2.hashCode() == test2.hashCode(), true); + assertEquals(test2.hashCode() == test2b.hashCode(), true); + } + + public void test_equals_null() { + assertEquals(ZoneId.of("Europe/London").equals(null), false); + } + + public void test_equals_notTimeZone() { + assertEquals(ZoneId.of("Europe/London").equals("Europe/London"), false); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + @DataProvider(name="ToString") + Object[][] data_toString() { + return new Object[][] { + {"Europe/London", "Europe/London"}, + {"Europe/Paris", "Europe/Paris"}, + {"Europe/Berlin", "Europe/Berlin"}, + {"UTC", "Z"}, + {"UTC+01:00", "+01:00"}, + }; + } + + @Test(dataProvider="ToString") + public void test_toString(String id, String expected) { + ZoneId test = ZoneId.of(id); + assertEquals(test.toString(), expected); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + private Instant createInstant(int year, int month, int day, ZoneOffset offset) { + return LocalDateTime.of(year, month, day, 0, 0).toInstant(offset); + } + + private Instant createInstant(int year, int month, int day, int hour, int min, int sec, int nano, ZoneOffset offset) { + return LocalDateTime.of(year, month, day, hour, min, sec, nano).toInstant(offset); + } + + private ZonedDateTime createZDT(int year, int month, int day, int hour, int min, int sec, int nano, ZoneId zone) { + return LocalDateTime.of(year, month, day, hour, min, sec, nano).atZone(zone); + } + + private LocalDateTime createLDT(int year, int month, int day) { + return LocalDateTime.of(year, month, day, 0, 0); + } + + private ZoneOffsetTransition checkOffset(ZoneRules rules, LocalDateTime dateTime, ZoneOffset offset, int type) { + List validOffsets = rules.getValidOffsets(dateTime); + assertEquals(validOffsets.size(), type); + assertEquals(rules.getOffset(dateTime), offset); + if (type == 1) { + assertEquals(validOffsets.get(0), offset); + return null; + } else { + ZoneOffsetTransition zot = rules.getTransition(dateTime); + assertNotNull(zot); + assertEquals(zot.isOverlap(), type == 2); + assertEquals(zot.isGap(), type == 0); + assertEquals(zot.isValidOffset(offset), type == 2); + return zot; + } + } + +} diff --git a/jdk/test/java/time/test/java/time/TestZoneOffset.java b/jdk/test/java/time/test/java/time/TestZoneOffset.java new file mode 100644 index 00000000000..b6df12f1abf --- /dev/null +++ b/jdk/test/java/time/test/java/time/TestZoneOffset.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time; + +import static org.testng.Assert.assertSame; + +import java.time.ZoneOffset; + +import org.testng.annotations.Test; + +/** + * Test ZoneOffset. + */ +@Test +public class TestZoneOffset extends AbstractTest { + + @Test + public void test_immutable() { + assertImmutable(ZoneOffset.class); + } + + @Test + public void test_factory_ofTotalSecondsSame() { + assertSame(ZoneOffset.ofTotalSeconds(0), ZoneOffset.UTC); + } + +} diff --git a/jdk/test/java/time/test/java/time/TestZonedDateTime.java b/jdk/test/java/time/test/java/time/TestZonedDateTime.java new file mode 100644 index 00000000000..ee9bbaef8a3 --- /dev/null +++ b/jdk/test/java/time/test/java/time/TestZonedDateTime.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time; + +import java.time.ZonedDateTime; + +import org.testng.annotations.Test; + +/** + * Test ZonedDateTime. + */ +@Test +public class TestZonedDateTime extends AbstractTest { + + @Test + public void test_immutable() { + assertImmutable(ZonedDateTime.class); + } + +} diff --git a/jdk/test/java/time/test/java/time/format/AbstractTestPrinterParser.java b/jdk/test/java/time/test/java/time/format/AbstractTestPrinterParser.java new file mode 100644 index 00000000000..c1b0ad2a18e --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/AbstractTestPrinterParser.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2011-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.format; + +import java.time.format.*; + +import java.util.Locale; + +import java.time.DateTimeException; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalField; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * Abstract PrinterParser test. + */ +@Test +public class AbstractTestPrinterParser { + + protected StringBuilder buf; + protected DateTimeFormatterBuilder builder; + protected TemporalAccessor dta; + protected Locale locale; + protected DateTimeFormatSymbols symbols; + + + @BeforeMethod(groups={"tck"}) + public void setUp() { + buf = new StringBuilder(); + builder = new DateTimeFormatterBuilder(); + dta = ZonedDateTime.of(LocalDateTime.of(2011, 6, 30, 12, 30, 40, 0), ZoneId.of("Europe/Paris")); + locale = Locale.ENGLISH; + symbols = DateTimeFormatSymbols.STANDARD; + } + + protected void setCaseSensitive(boolean caseSensitive) { + if (caseSensitive) { + builder.parseCaseSensitive(); + } else { + builder.parseCaseInsensitive(); + } + } + + protected void setStrict(boolean strict) { + if (strict) { + builder.parseStrict(); + } else { + builder.parseLenient(); + } + } + + protected DateTimeFormatter getFormatter() { + return builder.toFormatter(locale).withSymbols(symbols); + } + + protected DateTimeFormatter getFormatter(char c) { + return builder.appendLiteral(c).toFormatter(locale).withSymbols(symbols); + } + + protected DateTimeFormatter getFormatter(String s) { + return builder.appendLiteral(s).toFormatter(locale).withSymbols(symbols); + } + + protected DateTimeFormatter getFormatter(TemporalField field, TextStyle style) { + return builder.appendText(field, style).toFormatter(locale).withSymbols(symbols); + } + + protected DateTimeFormatter getFormatter(TemporalField field, int minWidth, int maxWidth, SignStyle signStyle) { + return builder.appendValue(field, minWidth, maxWidth, signStyle).toFormatter(locale).withSymbols(symbols); + } + + protected DateTimeFormatter getFormatter(String pattern, String noOffsetText) { + return builder.appendOffset(pattern, noOffsetText).toFormatter(locale).withSymbols(symbols); + } + + protected static final TemporalAccessor EMPTY_DTA = new TemporalAccessor() { + public boolean isSupported(TemporalField field) { + return true; + } + @Override + public long getLong(TemporalField field) { + throw new DateTimeException("Mock"); + } + }; +} diff --git a/jdk/test/java/time/test/java/time/format/MockIOExceptionAppendable.java b/jdk/test/java/time/test/java/time/format/MockIOExceptionAppendable.java new file mode 100644 index 00000000000..8cdedccdf8f --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/MockIOExceptionAppendable.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008,2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.format; + +import java.time.format.*; + +import java.io.IOException; + +/** + * Mock Appendable that throws IOException. + */ +public class MockIOExceptionAppendable implements Appendable { + + public Appendable append(CharSequence csq) throws IOException { + throw new IOException(); + } + + public Appendable append(char c) throws IOException { + throw new IOException(); + } + + public Appendable append(CharSequence csq, int start, int end) + throws IOException { + throw new IOException(); + } + +} diff --git a/jdk/test/java/time/test/java/time/format/TestCharLiteralParser.java b/jdk/test/java/time/test/java/time/format/TestCharLiteralParser.java new file mode 100644 index 00000000000..c2fe6290a5d --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/TestCharLiteralParser.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.format; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.time.format.DateTimeBuilder; +import java.text.ParsePosition; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test CharLiteralPrinterParser. + */ +@Test(groups={"implementation"}) +public class TestCharLiteralParser extends AbstractTestPrinterParser { + + @DataProvider(name="success") + Object[][] data_success() { + return new Object[][] { + // match + {'a', true, "a", 0, 1}, + {'a', true, "aOTHER", 0, 1}, + {'a', true, "OTHERaOTHER", 5, 6}, + {'a', true, "OTHERa", 5, 6}, + + // no match + {'a', true, "", 0, 0}, + {'a', true, "a", 1, 1}, + {'a', true, "A", 0, 0}, + {'a', true, "b", 0, 0}, + {'a', true, "OTHERbOTHER", 5, 5}, + {'a', true, "OTHERb", 5, 5}, + + // case insensitive + {'a', false, "a", 0, 1}, + {'a', false, "A", 0, 1}, + }; + } + + @Test(dataProvider="success") + public void test_parse_success(char c, boolean caseSensitive, + String text, int pos, int expectedPos) { + setCaseSensitive(caseSensitive); + ParsePosition ppos = new ParsePosition(pos); + DateTimeBuilder result = + getFormatter(c).parseToBuilder(text, ppos); + if (ppos.getErrorIndex() != -1) { + assertEquals(ppos.getIndex(), expectedPos); + } else { + assertEquals(ppos.getIndex(), expectedPos); + assertEquals(result.getCalendricalList().size(), 0); + } + } + + //----------------------------------------------------------------------- + @DataProvider(name="error") + Object[][] data_error() { + return new Object[][] { + {'a', "a", -1, IndexOutOfBoundsException.class}, + {'a', "a", 2, IndexOutOfBoundsException.class}, + }; + } + + @Test(dataProvider="error") + public void test_parse_error(char c, String text, int pos, Class expected) { + try { + DateTimeBuilder result = + getFormatter(c).parseToBuilder(text, new ParsePosition(pos)); + assertTrue(false); + } catch (RuntimeException ex) { + assertTrue(expected.isInstance(ex)); + } + } +} diff --git a/jdk/test/java/time/test/java/time/format/TestCharLiteralPrinter.java b/jdk/test/java/time/test/java/time/format/TestCharLiteralPrinter.java new file mode 100644 index 00000000000..f2c1f7d65b1 --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/TestCharLiteralPrinter.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.format; + +import java.time.format.*; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +/** + * Test CharLiteralPrinterParser. + */ +@Test(groups={"implementation"}) +public class TestCharLiteralPrinter extends AbstractTestPrinterParser { + + //----------------------------------------------------------------------- + public void test_print_emptyCalendrical() throws Exception { + buf.append("EXISTING"); + getFormatter('a').printTo(EMPTY_DTA, buf); + assertEquals(buf.toString(), "EXISTINGa"); + } + + public void test_print_dateTime() throws Exception { + buf.append("EXISTING"); + getFormatter('a').printTo(dta, buf); + assertEquals(buf.toString(), "EXISTINGa"); + } + + public void test_print_emptyAppendable() throws Exception { + getFormatter('a').printTo(dta, buf); + assertEquals(buf.toString(), "a"); + } + + //----------------------------------------------------------------------- + public void test_toString() throws Exception { + assertEquals(getFormatter('a').toString(), "'a'"); + } + + //----------------------------------------------------------------------- + public void test_toString_apos() throws Exception { + assertEquals(getFormatter('\'').toString(), "''"); + } + +} diff --git a/jdk/test/java/time/test/java/time/format/TestDateTimeFormatSymbols.java b/jdk/test/java/time/test/java/time/format/TestDateTimeFormatSymbols.java new file mode 100644 index 00000000000..fe2d408a6f9 --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/TestDateTimeFormatSymbols.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2011-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.format; + +import java.time.format.*; + +import static org.testng.Assert.assertSame; + +import java.util.Locale; + +import org.testng.annotations.Test; + +/** + * Test DateTimeFormatSymbols. + */ +@Test +public class TestDateTimeFormatSymbols { + + @Test(groups={"implementation"}) + public void test_of_Locale_cached() { + DateTimeFormatSymbols loc1 = DateTimeFormatSymbols.of(Locale.CANADA); + DateTimeFormatSymbols loc2 = DateTimeFormatSymbols.of(Locale.CANADA); + assertSame(loc1, loc2); + } + + //----------------------------------------------------------------------- + @Test(groups={"implementation"}) + public void test_ofDefaultLocale_cached() { + DateTimeFormatSymbols loc1 = DateTimeFormatSymbols.ofDefaultLocale(); + DateTimeFormatSymbols loc2 = DateTimeFormatSymbols.ofDefaultLocale(); + assertSame(loc1, loc2); + } + +} diff --git a/jdk/test/java/time/test/java/time/format/TestDateTimeFormatter.java b/jdk/test/java/time/test/java/time/format/TestDateTimeFormatter.java new file mode 100644 index 00000000000..61b0806f958 --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/TestDateTimeFormatter.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.format; + +import java.time.format.*; + +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static org.testng.Assert.assertSame; + +import java.util.Locale; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * Test DateTimeFormatter. + */ +@Test +public class TestDateTimeFormatter { + // TODO these tests are not tck, as they refer to a non-public class + // rewrite whole test case to use BASIC_FORMATTER or similar + + @BeforeMethod(groups={"tck"}) + public void setUp() { + } + + @Test(groups={"implementation"}) + public void test_withLocale_same() throws Exception { + DateTimeFormatter base = + new DateTimeFormatterBuilder().appendLiteral("ONE") + .appendValue(DAY_OF_MONTH, 1, 2, SignStyle.NOT_NEGATIVE) + .toFormatter(Locale.ENGLISH) + .withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = base.withLocale(Locale.ENGLISH); + assertSame(test, base); + } + +} diff --git a/jdk/test/java/time/test/java/time/format/TestDateTimeFormatters.java b/jdk/test/java/time/test/java/time/format/TestDateTimeFormatters.java new file mode 100644 index 00000000000..5e7ab3ef7cb --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/TestDateTimeFormatters.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.format; + +import java.time.format.*; + +import static org.testng.Assert.assertTrue; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.util.Collections; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +/** + * Test DateTimeFormatters. + */ +@Test +public class TestDateTimeFormatters { + + @BeforeMethod + public void setUp() { + } + + @Test(groups={"implementation"}) + @SuppressWarnings("rawtypes") + public void test_constructor() throws Exception { + for (Constructor constructor : DateTimeFormatters.class.getDeclaredConstructors()) { + assertTrue(Modifier.isPrivate(constructor.getModifiers())); + //constructor.setAccessible(true); + //constructor.newInstance(Collections.nCopies(constructor.getParameterTypes().length, null).toArray()); + } + } + +} diff --git a/jdk/test/java/time/test/java/time/format/TestDateTimePrintException.java b/jdk/test/java/time/test/java/time/format/TestDateTimePrintException.java new file mode 100644 index 00000000000..3a737f33ed2 --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/TestDateTimePrintException.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.format; + +import java.time.format.*; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertSame; + +import java.io.IOException; + +import org.testng.annotations.Test; + +/** + * Test DateTimePrintException. + */ +@Test +public class TestDateTimePrintException { + + @Test(groups={"implementation"}) + public void test_constructor_StringThrowable_notIOException_same() throws Exception { + IllegalArgumentException iaex = new IllegalArgumentException("INNER"); + DateTimePrintException ex = new DateTimePrintException("TEST", iaex); + assertEquals(ex.getMessage(), "TEST"); + assertSame(ex.getCause(), iaex); + ex.rethrowIOException(); // no effect + } + + @Test(expectedExceptions=IOException.class, groups={"implementation"}) + public void test_constructor_StringThrowable_IOException_same() throws Exception { + IOException ioex = new IOException("INNER"); + DateTimePrintException ex = new DateTimePrintException("TEST", ioex); + assertEquals(ex.getMessage(), "TEST"); + assertSame(ex.getCause(), ioex); + ex.rethrowIOException(); // rethrows + } + +} diff --git a/jdk/test/java/time/test/java/time/format/TestDateTimeTextProvider.java b/jdk/test/java/time/test/java/time/format/TestDateTimeTextProvider.java new file mode 100644 index 00000000000..ab8cec39822 --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/TestDateTimeTextProvider.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2011-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.format; + +import java.time.format.*; + +import static java.time.temporal.ChronoField.AMPM_OF_DAY; +import static java.time.temporal.ChronoField.DAY_OF_WEEK; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static org.testng.Assert.assertEquals; + +import java.util.Locale; + +import java.time.ZonedDateTime; +import java.time.temporal.TemporalField; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test SimpleDateTimeTextProvider. + */ +@Test(groups={"implementation"}) +public class TestDateTimeTextProvider extends AbstractTestPrinterParser { + + Locale enUS = new Locale("en", "US"); + Locale ptBR = new Locale("pt", "BR"); + + //----------------------------------------------------------------------- + @DataProvider(name = "Text") + Object[][] data_text() { + return new Object[][] { + {DAY_OF_WEEK, 1, TextStyle.SHORT, enUS, "Mon"}, + {DAY_OF_WEEK, 2, TextStyle.SHORT, enUS, "Tue"}, + {DAY_OF_WEEK, 3, TextStyle.SHORT, enUS, "Wed"}, + {DAY_OF_WEEK, 4, TextStyle.SHORT, enUS, "Thu"}, + {DAY_OF_WEEK, 5, TextStyle.SHORT, enUS, "Fri"}, + {DAY_OF_WEEK, 6, TextStyle.SHORT, enUS, "Sat"}, + {DAY_OF_WEEK, 7, TextStyle.SHORT, enUS, "Sun"}, + + {DAY_OF_WEEK, 1, TextStyle.SHORT, ptBR, "Seg"}, + {DAY_OF_WEEK, 2, TextStyle.SHORT, ptBR, "Ter"}, + {DAY_OF_WEEK, 3, TextStyle.SHORT, ptBR, "Qua"}, + {DAY_OF_WEEK, 4, TextStyle.SHORT, ptBR, "Qui"}, + {DAY_OF_WEEK, 5, TextStyle.SHORT, ptBR, "Sex"}, + {DAY_OF_WEEK, 6, TextStyle.SHORT, ptBR, "S\u00E1b"}, + {DAY_OF_WEEK, 7, TextStyle.SHORT, ptBR, "Dom"}, + + {DAY_OF_WEEK, 1, TextStyle.FULL, enUS, "Monday"}, + {DAY_OF_WEEK, 2, TextStyle.FULL, enUS, "Tuesday"}, + {DAY_OF_WEEK, 3, TextStyle.FULL, enUS, "Wednesday"}, + {DAY_OF_WEEK, 4, TextStyle.FULL, enUS, "Thursday"}, + {DAY_OF_WEEK, 5, TextStyle.FULL, enUS, "Friday"}, + {DAY_OF_WEEK, 6, TextStyle.FULL, enUS, "Saturday"}, + {DAY_OF_WEEK, 7, TextStyle.FULL, enUS, "Sunday"}, + + {DAY_OF_WEEK, 1, TextStyle.FULL, ptBR, "Segunda-feira"}, + {DAY_OF_WEEK, 2, TextStyle.FULL, ptBR, "Ter\u00E7a-feira"}, + {DAY_OF_WEEK, 3, TextStyle.FULL, ptBR, "Quarta-feira"}, + {DAY_OF_WEEK, 4, TextStyle.FULL, ptBR, "Quinta-feira"}, + {DAY_OF_WEEK, 5, TextStyle.FULL, ptBR, "Sexta-feira"}, + {DAY_OF_WEEK, 6, TextStyle.FULL, ptBR, "S\u00E1bado"}, + {DAY_OF_WEEK, 7, TextStyle.FULL, ptBR, "Domingo"}, + + {MONTH_OF_YEAR, 1, TextStyle.SHORT, enUS, "Jan"}, + {MONTH_OF_YEAR, 2, TextStyle.SHORT, enUS, "Feb"}, + {MONTH_OF_YEAR, 3, TextStyle.SHORT, enUS, "Mar"}, + {MONTH_OF_YEAR, 4, TextStyle.SHORT, enUS, "Apr"}, + {MONTH_OF_YEAR, 5, TextStyle.SHORT, enUS, "May"}, + {MONTH_OF_YEAR, 6, TextStyle.SHORT, enUS, "Jun"}, + {MONTH_OF_YEAR, 7, TextStyle.SHORT, enUS, "Jul"}, + {MONTH_OF_YEAR, 8, TextStyle.SHORT, enUS, "Aug"}, + {MONTH_OF_YEAR, 9, TextStyle.SHORT, enUS, "Sep"}, + {MONTH_OF_YEAR, 10, TextStyle.SHORT, enUS, "Oct"}, + {MONTH_OF_YEAR, 11, TextStyle.SHORT, enUS, "Nov"}, + {MONTH_OF_YEAR, 12, TextStyle.SHORT, enUS, "Dec"}, + + {MONTH_OF_YEAR, 1, TextStyle.SHORT, ptBR, "Jan"}, + {MONTH_OF_YEAR, 2, TextStyle.SHORT, ptBR, "Fev"}, + {MONTH_OF_YEAR, 3, TextStyle.SHORT, ptBR, "Mar"}, + {MONTH_OF_YEAR, 4, TextStyle.SHORT, ptBR, "Abr"}, + {MONTH_OF_YEAR, 5, TextStyle.SHORT, ptBR, "Mai"}, + {MONTH_OF_YEAR, 6, TextStyle.SHORT, ptBR, "Jun"}, + {MONTH_OF_YEAR, 7, TextStyle.SHORT, ptBR, "Jul"}, + {MONTH_OF_YEAR, 8, TextStyle.SHORT, ptBR, "Ago"}, + {MONTH_OF_YEAR, 9, TextStyle.SHORT, ptBR, "Set"}, + {MONTH_OF_YEAR, 10, TextStyle.SHORT, ptBR, "Out"}, + {MONTH_OF_YEAR, 11, TextStyle.SHORT, ptBR, "Nov"}, + {MONTH_OF_YEAR, 12, TextStyle.SHORT, ptBR, "Dez"}, + + {MONTH_OF_YEAR, 1, TextStyle.FULL, enUS, "January"}, + {MONTH_OF_YEAR, 2, TextStyle.FULL, enUS, "February"}, + {MONTH_OF_YEAR, 3, TextStyle.FULL, enUS, "March"}, + {MONTH_OF_YEAR, 4, TextStyle.FULL, enUS, "April"}, + {MONTH_OF_YEAR, 5, TextStyle.FULL, enUS, "May"}, + {MONTH_OF_YEAR, 6, TextStyle.FULL, enUS, "June"}, + {MONTH_OF_YEAR, 7, TextStyle.FULL, enUS, "July"}, + {MONTH_OF_YEAR, 8, TextStyle.FULL, enUS, "August"}, + {MONTH_OF_YEAR, 9, TextStyle.FULL, enUS, "September"}, + {MONTH_OF_YEAR, 10, TextStyle.FULL, enUS, "October"}, + {MONTH_OF_YEAR, 11, TextStyle.FULL, enUS, "November"}, + {MONTH_OF_YEAR, 12, TextStyle.FULL, enUS, "December"}, + + {MONTH_OF_YEAR, 1, TextStyle.FULL, ptBR, "Janeiro"}, + {MONTH_OF_YEAR, 2, TextStyle.FULL, ptBR, "Fevereiro"}, + {MONTH_OF_YEAR, 3, TextStyle.FULL, ptBR, "Mar\u00E7o"}, + {MONTH_OF_YEAR, 4, TextStyle.FULL, ptBR, "Abril"}, + {MONTH_OF_YEAR, 5, TextStyle.FULL, ptBR, "Maio"}, + {MONTH_OF_YEAR, 6, TextStyle.FULL, ptBR, "Junho"}, + {MONTH_OF_YEAR, 7, TextStyle.FULL, ptBR, "Julho"}, + {MONTH_OF_YEAR, 8, TextStyle.FULL, ptBR, "Agosto"}, + {MONTH_OF_YEAR, 9, TextStyle.FULL, ptBR, "Setembro"}, + {MONTH_OF_YEAR, 10, TextStyle.FULL, ptBR, "Outubro"}, + {MONTH_OF_YEAR, 11, TextStyle.FULL, ptBR, "Novembro"}, + {MONTH_OF_YEAR, 12, TextStyle.FULL, ptBR, "Dezembro"}, + + {AMPM_OF_DAY, 0, TextStyle.SHORT, enUS, "AM"}, + {AMPM_OF_DAY, 1, TextStyle.SHORT, enUS, "PM"}, + + }; + } + + @Test(dataProvider = "Text") + public void test_getText(TemporalField field, Number value, TextStyle style, Locale locale, String expected) { + DateTimeFormatter fmt = getFormatter(field, style).withLocale(locale); + assertEquals(fmt.print(ZonedDateTime.now().with(field, value.longValue())), expected); + } + +} diff --git a/jdk/test/java/time/test/java/time/format/TestFractionPrinterParser.java b/jdk/test/java/time/test/java/time/format/TestFractionPrinterParser.java new file mode 100644 index 00000000000..4be3b6cce46 --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/TestFractionPrinterParser.java @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.format; + +import java.time.format.*; + +import static java.time.temporal.ChronoField.NANO_OF_SECOND; +import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +import java.text.ParsePosition; +import java.time.DateTimeException; +import java.time.LocalTime; +import java.time.format.DateTimeBuilder; +import java.time.temporal.TemporalField; + +import test.java.time.temporal.MockFieldValue; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test FractionPrinterParser. + */ +@Test(groups={"implementation"}) +public class TestFractionPrinterParser extends AbstractTestPrinterParser { + + private DateTimeFormatter getFormatter(TemporalField field, int minWidth, int maxWidth, boolean decimalPoint) { + return builder.appendFraction(field, minWidth, maxWidth, decimalPoint).toFormatter(locale).withSymbols(symbols); + } + + //----------------------------------------------------------------------- + // print + //----------------------------------------------------------------------- + @Test(expectedExceptions=DateTimeException.class) + public void test_print_emptyCalendrical() throws Exception { + getFormatter(NANO_OF_SECOND, 0, 9, true).printTo(EMPTY_DTA, buf); + } + + public void test_print_append() throws Exception { + buf.append("EXISTING"); + getFormatter(NANO_OF_SECOND, 0, 9, true).printTo(LocalTime.of(12, 30, 40, 3), buf); + assertEquals(buf.toString(), "EXISTING.000000003"); + } + + //----------------------------------------------------------------------- + @DataProvider(name="Nanos") + Object[][] provider_nanos() { + return new Object[][] { + {0, 9, 0, ""}, + {0, 9, 2, ".000000002"}, + {0, 9, 20, ".00000002"}, + {0, 9, 200, ".0000002"}, + {0, 9, 2000, ".000002"}, + {0, 9, 20000, ".00002"}, + {0, 9, 200000, ".0002"}, + {0, 9, 2000000, ".002"}, + {0, 9, 20000000, ".02"}, + {0, 9, 200000000, ".2"}, + {0, 9, 1, ".000000001"}, + {0, 9, 12, ".000000012"}, + {0, 9, 123, ".000000123"}, + {0, 9, 1234, ".000001234"}, + {0, 9, 12345, ".000012345"}, + {0, 9, 123456, ".000123456"}, + {0, 9, 1234567, ".001234567"}, + {0, 9, 12345678, ".012345678"}, + {0, 9, 123456789, ".123456789"}, + + {1, 9, 0, ".0"}, + {1, 9, 2, ".000000002"}, + {1, 9, 20, ".00000002"}, + {1, 9, 200, ".0000002"}, + {1, 9, 2000, ".000002"}, + {1, 9, 20000, ".00002"}, + {1, 9, 200000, ".0002"}, + {1, 9, 2000000, ".002"}, + {1, 9, 20000000, ".02"}, + {1, 9, 200000000, ".2"}, + + {2, 3, 0, ".00"}, + {2, 3, 2, ".000"}, + {2, 3, 20, ".000"}, + {2, 3, 200, ".000"}, + {2, 3, 2000, ".000"}, + {2, 3, 20000, ".000"}, + {2, 3, 200000, ".000"}, + {2, 3, 2000000, ".002"}, + {2, 3, 20000000, ".02"}, + {2, 3, 200000000, ".20"}, + {2, 3, 1, ".000"}, + {2, 3, 12, ".000"}, + {2, 3, 123, ".000"}, + {2, 3, 1234, ".000"}, + {2, 3, 12345, ".000"}, + {2, 3, 123456, ".000"}, + {2, 3, 1234567, ".001"}, + {2, 3, 12345678, ".012"}, + {2, 3, 123456789, ".123"}, + + {6, 6, 0, ".000000"}, + {6, 6, 2, ".000000"}, + {6, 6, 20, ".000000"}, + {6, 6, 200, ".000000"}, + {6, 6, 2000, ".000002"}, + {6, 6, 20000, ".000020"}, + {6, 6, 200000, ".000200"}, + {6, 6, 2000000, ".002000"}, + {6, 6, 20000000, ".020000"}, + {6, 6, 200000000, ".200000"}, + {6, 6, 1, ".000000"}, + {6, 6, 12, ".000000"}, + {6, 6, 123, ".000000"}, + {6, 6, 1234, ".000001"}, + {6, 6, 12345, ".000012"}, + {6, 6, 123456, ".000123"}, + {6, 6, 1234567, ".001234"}, + {6, 6, 12345678, ".012345"}, + {6, 6, 123456789, ".123456"}, + }; + } + + @Test(dataProvider="Nanos") + public void test_print_nanos(int minWidth, int maxWidth, int value, String result) throws Exception { + getFormatter(NANO_OF_SECOND, minWidth, maxWidth, true).printTo(new MockFieldValue(NANO_OF_SECOND, value), buf); + if (result == null) { + fail("Expected exception"); + } + assertEquals(buf.toString(), result); + } + + @Test(dataProvider="Nanos") + public void test_print_nanos_noDecimalPoint(int minWidth, int maxWidth, int value, String result) throws Exception { + getFormatter(NANO_OF_SECOND, minWidth, maxWidth, false).printTo(new MockFieldValue(NANO_OF_SECOND, value), buf); + if (result == null) { + fail("Expected exception"); + } + assertEquals(buf.toString(), (result.startsWith(".") ? result.substring(1) : result)); + } + + //----------------------------------------------------------------------- + @DataProvider(name="Seconds") + Object[][] provider_seconds() { + return new Object[][] { + {0, 9, 0, ""}, + {0, 9, 3, ".05"}, + {0, 9, 6, ".1"}, + {0, 9, 9, ".15"}, + {0, 9, 12, ".2"}, + {0, 9, 15, ".25"}, + {0, 9, 30, ".5"}, + {0, 9, 45, ".75"}, + + {2, 2, 0, ".00"}, + {2, 2, 3, ".05"}, + {2, 2, 6, ".10"}, + {2, 2, 9, ".15"}, + {2, 2, 12, ".20"}, + {2, 2, 15, ".25"}, + {2, 2, 30, ".50"}, + {2, 2, 45, ".75"}, + }; + } + + @Test(dataProvider="Seconds") + public void test_print_seconds(int minWidth, int maxWidth, int value, String result) throws Exception { + getFormatter(SECOND_OF_MINUTE, minWidth, maxWidth, true).printTo(new MockFieldValue(SECOND_OF_MINUTE, value), buf); + if (result == null) { + fail("Expected exception"); + } + assertEquals(buf.toString(), result); + } + + @Test(dataProvider="Seconds") + public void test_print_seconds_noDecimalPoint(int minWidth, int maxWidth, int value, String result) throws Exception { + getFormatter(SECOND_OF_MINUTE, minWidth, maxWidth, false).printTo(new MockFieldValue(SECOND_OF_MINUTE, value), buf); + if (result == null) { + fail("Expected exception"); + } + assertEquals(buf.toString(), (result.startsWith(".") ? result.substring(1) : result)); + } + + //----------------------------------------------------------------------- + // parse + //----------------------------------------------------------------------- + @Test(dataProvider="Nanos") + public void test_reverseParse(int minWidth, int maxWidth, int value, String result) throws Exception { + ParsePosition pos = new ParsePosition(0); + int expectedValue = fixParsedValue(maxWidth, value); + DateTimeBuilder dtb = getFormatter(NANO_OF_SECOND, minWidth, maxWidth, true).parseToBuilder(result, pos); + assertEquals(pos.getIndex(), result.length()); + assertParsed(dtb, NANO_OF_SECOND, value == 0 && minWidth == 0 ? null : (long) expectedValue); + } + + @Test(dataProvider="Nanos") + public void test_reverseParse_noDecimalPoint(int minWidth, int maxWidth, int value, String result) throws Exception { + ParsePosition pos = new ParsePosition((result.startsWith(".") ? 1 : 0)); + DateTimeBuilder dtb = getFormatter(NANO_OF_SECOND, minWidth, maxWidth, false).parseToBuilder(result, pos); + assertEquals(pos.getIndex(), result.length()); + int expectedValue = fixParsedValue(maxWidth, value); + assertParsed(dtb, NANO_OF_SECOND, value == 0 && minWidth == 0 ? null : (long) expectedValue); + } + + @Test(dataProvider="Nanos") + public void test_reverseParse_followedByNonDigit(int minWidth, int maxWidth, int value, String result) throws Exception { + ParsePosition pos = new ParsePosition(0); + int expectedValue = fixParsedValue(maxWidth, value); + DateTimeBuilder dtb = getFormatter(NANO_OF_SECOND, minWidth, maxWidth, true).parseToBuilder(result + " ", pos); + assertEquals(pos.getIndex(), result.length()); + assertParsed(dtb, NANO_OF_SECOND, value == 0 && minWidth == 0 ? null : (long) expectedValue); + } + +// @Test(dataProvider="Nanos") +// public void test_reverseParse_followedByNonDigit_noDecimalPoint(int minWidth, int maxWidth, int value, String result) throws Exception { +// FractionPrinterParser pp = new FractionPrinterParser(NANO_OF_SECOND, minWidth, maxWidth, false); +// int newPos = pp.parse(parseContext, result + " ", (result.startsWith(".") ? 1 : 0)); +// assertEquals(newPos, result.length()); +// int expectedValue = fixParsedValue(maxWidth, value); +// assertParsed(parseContext, NANO_OF_SECOND, value == 0 && minWidth == 0 ? null : (long) expectedValue); +// } + + @Test(dataProvider="Nanos") + public void test_reverseParse_preceededByNonDigit(int minWidth, int maxWidth, int value, String result) throws Exception { + ParsePosition pos = new ParsePosition(1); + int expectedValue = fixParsedValue(maxWidth, value); + DateTimeBuilder dtb = getFormatter(NANO_OF_SECOND, minWidth, maxWidth, true).parseToBuilder(" " + result, pos); + assertEquals(pos.getIndex(), result.length() + 1); + assertParsed(dtb, NANO_OF_SECOND, value == 0 && minWidth == 0 ? null : (long) expectedValue); + } + + private int fixParsedValue(int maxWidth, int value) { + if (maxWidth < 9) { + int power = (int) Math.pow(10, (9 - maxWidth)); + value = (value / power) * power; + } + return value; + } + + @Test(dataProvider="Seconds") + public void test_reverseParse_seconds(int minWidth, int maxWidth, int value, String result) throws Exception { + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder dtb = getFormatter(SECOND_OF_MINUTE, minWidth, maxWidth, true).parseToBuilder(result, pos); + assertEquals(pos.getIndex(), result.length()); + assertParsed(dtb, SECOND_OF_MINUTE, value == 0 && minWidth == 0 ? null : (long) value); + } + + private void assertParsed(DateTimeBuilder dtb, TemporalField field, Long value) { + if (value == null) { + assertEquals(dtb.containsFieldValue(field), false); + } else { + assertEquals(dtb.getLong(field), (long)value); + } + } + + //----------------------------------------------------------------------- + @DataProvider(name="ParseNothing") + Object[][] provider_parseNothing() { + return new Object[][] { + {NANO_OF_SECOND, 3, 6, true, "", 0, 0}, + {NANO_OF_SECOND, 3, 6, true, "A", 0, 0}, + {NANO_OF_SECOND, 3, 6, true, ".", 0, 1}, + {NANO_OF_SECOND, 3, 6, true, ".5", 0, 1}, + {NANO_OF_SECOND, 3, 6, true, ".51", 0, 1}, + {NANO_OF_SECOND, 3, 6, true, ".A23456", 0, 1}, + {NANO_OF_SECOND, 3, 6, true, ".1A3456", 0, 1}, + }; + } + + @Test(dataProvider = "ParseNothing") + public void test_parse_nothing(TemporalField field, int min, int max, boolean decimalPoint, String text, int pos, int expected) { + ParsePosition ppos = new ParsePosition(pos); + DateTimeBuilder dtb = getFormatter(field, min, max, decimalPoint).parseToBuilder(text, ppos); + assertEquals(ppos.getErrorIndex(), expected); + } + + //----------------------------------------------------------------------- + public void test_toString() throws Exception { + assertEquals(getFormatter(NANO_OF_SECOND, 3, 6, true).toString(), "Fraction(NanoOfSecond,3,6,DecimalPoint)"); + } + + public void test_toString_noDecimalPoint() throws Exception { + assertEquals(getFormatter(NANO_OF_SECOND, 3, 6, false).toString(), "Fraction(NanoOfSecond,3,6)"); + } + +} diff --git a/jdk/test/java/time/test/java/time/format/TestNumberParser.java b/jdk/test/java/time/test/java/time/format/TestNumberParser.java new file mode 100644 index 00000000000..af4b3b6e940 --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/TestNumberParser.java @@ -0,0 +1,547 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.format; + +import java.time.format.*; + +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.DAY_OF_WEEK; +import static java.time.temporal.ChronoField.DAY_OF_YEAR; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.text.ParsePosition; +import java.time.format.DateTimeBuilder; +import java.time.temporal.TemporalField; +import java.time.format.DateTimeFormatter; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test NumberPrinterParser. + */ +@Test(groups={"implementation"}) +public class TestNumberParser extends AbstractTestPrinterParser { + + //----------------------------------------------------------------------- + @DataProvider(name="error") + Object[][] data_error() { + return new Object[][] { + {DAY_OF_MONTH, 1, 2, SignStyle.NEVER, "12", -1, IndexOutOfBoundsException.class}, + {DAY_OF_MONTH, 1, 2, SignStyle.NEVER, "12", 3, IndexOutOfBoundsException.class}, + }; + } + + @Test(dataProvider="error") + public void test_parse_error(TemporalField field, int min, int max, SignStyle style, String text, int pos, Class expected) { + try { + getFormatter(field, min, max, style).parseToBuilder(text, new ParsePosition(pos)); + assertTrue(false); + } catch (RuntimeException ex) { + assertTrue(expected.isInstance(ex)); + } + } + + //----------------------------------------------------------------------- + @DataProvider(name="parseData") + Object[][] provider_parseData() { + return new Object[][] { + // normal + {1, 2, SignStyle.NEVER, 0, "12", 0, 2, 12L}, // normal + {1, 2, SignStyle.NEVER, 0, "Xxx12Xxx", 3, 5, 12L}, // parse in middle + {1, 2, SignStyle.NEVER, 0, "99912999", 3, 5, 12L}, // parse in middle + {2, 4, SignStyle.NEVER, 0, "12345", 0, 4, 1234L}, // stops at max width + {2, 4, SignStyle.NEVER, 0, "12-45", 0, 2, 12L}, // stops at dash + {2, 4, SignStyle.NEVER, 0, "123-5", 0, 3, 123L}, // stops at dash + {1, 10, SignStyle.NORMAL, 0, "2147483647", 0, 10, Integer.MAX_VALUE}, + {1, 10, SignStyle.NORMAL, 0, "-2147483648", 0, 11, Integer.MIN_VALUE}, + {1, 10, SignStyle.NORMAL, 0, "2147483648", 0, 10, 2147483648L}, + {1, 10, SignStyle.NORMAL, 0, "-2147483649", 0, 11, -2147483649L}, + {1, 10, SignStyle.NORMAL, 0, "987659876598765", 0, 10, 9876598765L}, + {1, 19, SignStyle.NORMAL, 0, "999999999999999999", 0, 18, 999999999999999999L}, + {1, 19, SignStyle.NORMAL, 0, "-999999999999999999", 0, 19, -999999999999999999L}, + {1, 19, SignStyle.NORMAL, 0, "1000000000000000000", 0, 19, 1000000000000000000L}, + {1, 19, SignStyle.NORMAL, 0, "-1000000000000000000", 0, 20, -1000000000000000000L}, + {1, 19, SignStyle.NORMAL, 0, "000000000000000000", 0, 18, 0L}, + {1, 19, SignStyle.NORMAL, 0, "0000000000000000000", 0, 19, 0L}, + {1, 19, SignStyle.NORMAL, 0, "9223372036854775807", 0, 19, Long.MAX_VALUE}, + {1, 19, SignStyle.NORMAL, 0, "-9223372036854775808", 0, 20, Long.MIN_VALUE}, + {1, 19, SignStyle.NORMAL, 0, "9223372036854775808", 0, 18, 922337203685477580L}, // last digit not parsed + {1, 19, SignStyle.NORMAL, 0, "-9223372036854775809", 0, 19, -922337203685477580L}, // last digit not parsed + // no match + {1, 2, SignStyle.NEVER, 1, "A1", 0, 0, 0}, + {1, 2, SignStyle.NEVER, 1, " 1", 0, 0, 0}, + {1, 2, SignStyle.NEVER, 1, " 1", 1, 1, 0}, + {2, 2, SignStyle.NEVER, 1, "1", 0, 0, 0}, + {2, 2, SignStyle.NEVER, 1, "Xxx1", 0, 0, 0}, + {2, 2, SignStyle.NEVER, 1, "1", 1, 1, 0}, + {2, 2, SignStyle.NEVER, 1, "Xxx1", 4, 4, 0}, + {2, 2, SignStyle.NEVER, 1, "1-2", 0, 0, 0}, + {1, 19, SignStyle.NORMAL, 0, "-000000000000000000", 0, 0, 0}, + {1, 19, SignStyle.NORMAL, 0, "-0000000000000000000", 0, 0, 0}, + // parse reserving space 1 (adjacent-parsing) + {1, 1, SignStyle.NEVER, 1, "12", 0, 1, 1L}, + {1, 19, SignStyle.NEVER, 1, "12", 0, 1, 1L}, + {1, 19, SignStyle.NEVER, 1, "12345", 0, 4, 1234L}, + {1, 19, SignStyle.NEVER, 1, "12345678901", 0, 10, 1234567890L}, + {1, 19, SignStyle.NEVER, 1, "123456789012345678901234567890", 0, 19, 1234567890123456789L}, + {1, 19, SignStyle.NEVER, 1, "1", 0, 1, 1L}, // error from next field + {2, 2, SignStyle.NEVER, 1, "12", 0, 2, 12L}, // error from next field + {2, 19, SignStyle.NEVER, 1, "1", 0, 0, 0}, + // parse reserving space 2 (adjacent-parsing) + {1, 1, SignStyle.NEVER, 2, "123", 0, 1, 1L}, + {1, 19, SignStyle.NEVER, 2, "123", 0, 1, 1L}, + {1, 19, SignStyle.NEVER, 2, "12345", 0, 3, 123L}, + {1, 19, SignStyle.NEVER, 2, "12345678901", 0, 9, 123456789L}, + {1, 19, SignStyle.NEVER, 2, "123456789012345678901234567890", 0, 19, 1234567890123456789L}, + {1, 19, SignStyle.NEVER, 2, "1", 0, 1, 1L}, // error from next field + {1, 19, SignStyle.NEVER, 2, "12", 0, 1, 1L}, // error from next field + {2, 2, SignStyle.NEVER, 2, "12", 0, 2, 12L}, // error from next field + {2, 19, SignStyle.NEVER, 2, "1", 0, 0, 0}, + {2, 19, SignStyle.NEVER, 2, "1AAAAABBBBBCCCCC", 0, 0, 0}, + }; + } + + //----------------------------------------------------------------------- + @Test(dataProvider="parseData") + public void test_parse_fresh(int minWidth, int maxWidth, SignStyle signStyle, int subsequentWidth, String text, int pos, int expectedPos, long expectedValue) { + ParsePosition ppos = new ParsePosition(pos); + DateTimeFormatter dtf = getFormatter(DAY_OF_MONTH, minWidth, maxWidth, signStyle); + if (subsequentWidth > 0) { + // hacky, to reserve space + dtf = builder.appendValue(DAY_OF_YEAR, subsequentWidth).toFormatter(locale).withSymbols(symbols); + } + DateTimeBuilder dtb = dtf.parseToBuilder(text, ppos); + if (ppos.getErrorIndex() != -1) { + assertEquals(ppos.getErrorIndex(), expectedPos); + } else { + assertTrue(subsequentWidth >= 0); + assertEquals(ppos.getIndex(), expectedPos + subsequentWidth); + assertEquals(dtb.getLong(DAY_OF_MONTH), expectedValue); + } + } + + @Test(dataProvider="parseData") + public void test_parse_textField(int minWidth, int maxWidth, SignStyle signStyle, int subsequentWidth, String text, int pos, int expectedPos, long expectedValue) { + ParsePosition ppos = new ParsePosition(pos); + DateTimeFormatter dtf = getFormatter(DAY_OF_WEEK, minWidth, maxWidth, signStyle); + if (subsequentWidth > 0) { + // hacky, to reserve space + dtf = builder.appendValue(DAY_OF_YEAR, subsequentWidth).toFormatter(locale).withSymbols(symbols); + } + DateTimeBuilder dtb = dtf.parseToBuilder(text, ppos); + if (ppos.getErrorIndex() != -1) { + assertEquals(ppos.getErrorIndex(), expectedPos); + } else { + assertTrue(subsequentWidth >= 0); + assertEquals(ppos.getIndex(), expectedPos + subsequentWidth); + assertEquals(dtb.getLong(DAY_OF_WEEK), expectedValue); + } + } + + //----------------------------------------------------------------------- + @DataProvider(name="parseSignsStrict") + Object[][] provider_parseSignsStrict() { + return new Object[][] { + // basics + {"0", 1, 2, SignStyle.NEVER, 1, 0}, + {"1", 1, 2, SignStyle.NEVER, 1, 1}, + {"2", 1, 2, SignStyle.NEVER, 1, 2}, + {"3", 1, 2, SignStyle.NEVER, 1, 3}, + {"4", 1, 2, SignStyle.NEVER, 1, 4}, + {"5", 1, 2, SignStyle.NEVER, 1, 5}, + {"6", 1, 2, SignStyle.NEVER, 1, 6}, + {"7", 1, 2, SignStyle.NEVER, 1, 7}, + {"8", 1, 2, SignStyle.NEVER, 1, 8}, + {"9", 1, 2, SignStyle.NEVER, 1, 9}, + {"10", 1, 2, SignStyle.NEVER, 2, 10}, + {"100", 1, 2, SignStyle.NEVER, 2, 10}, + {"100", 1, 3, SignStyle.NEVER, 3, 100}, + + // never + {"0", 1, 2, SignStyle.NEVER, 1, 0}, + {"5", 1, 2, SignStyle.NEVER, 1, 5}, + {"50", 1, 2, SignStyle.NEVER, 2, 50}, + {"500", 1, 2, SignStyle.NEVER, 2, 50}, + {"-0", 1, 2, SignStyle.NEVER, 0, null}, + {"-5", 1, 2, SignStyle.NEVER, 0, null}, + {"-50", 1, 2, SignStyle.NEVER, 0, null}, + {"-500", 1, 2, SignStyle.NEVER, 0, null}, + {"-AAA", 1, 2, SignStyle.NEVER, 0, null}, + {"+0", 1, 2, SignStyle.NEVER, 0, null}, + {"+5", 1, 2, SignStyle.NEVER, 0, null}, + {"+50", 1, 2, SignStyle.NEVER, 0, null}, + {"+500", 1, 2, SignStyle.NEVER, 0, null}, + {"+AAA", 1, 2, SignStyle.NEVER, 0, null}, + + // not negative + {"0", 1, 2, SignStyle.NOT_NEGATIVE, 1, 0}, + {"5", 1, 2, SignStyle.NOT_NEGATIVE, 1, 5}, + {"50", 1, 2, SignStyle.NOT_NEGATIVE, 2, 50}, + {"500", 1, 2, SignStyle.NOT_NEGATIVE, 2, 50}, + {"-0", 1, 2, SignStyle.NOT_NEGATIVE, 0, null}, + {"-5", 1, 2, SignStyle.NOT_NEGATIVE, 0, null}, + {"-50", 1, 2, SignStyle.NOT_NEGATIVE, 0, null}, + {"-500", 1, 2, SignStyle.NOT_NEGATIVE, 0, null}, + {"-AAA", 1, 2, SignStyle.NOT_NEGATIVE, 0, null}, + {"+0", 1, 2, SignStyle.NOT_NEGATIVE, 0, null}, + {"+5", 1, 2, SignStyle.NOT_NEGATIVE, 0, null}, + {"+50", 1, 2, SignStyle.NOT_NEGATIVE, 0, null}, + {"+500", 1, 2, SignStyle.NOT_NEGATIVE, 0, null}, + {"+AAA", 1, 2, SignStyle.NOT_NEGATIVE, 0, null}, + + // normal + {"0", 1, 2, SignStyle.NORMAL, 1, 0}, + {"5", 1, 2, SignStyle.NORMAL, 1, 5}, + {"50", 1, 2, SignStyle.NORMAL, 2, 50}, + {"500", 1, 2, SignStyle.NORMAL, 2, 50}, + {"-0", 1, 2, SignStyle.NORMAL, 0, null}, + {"-5", 1, 2, SignStyle.NORMAL, 2, -5}, + {"-50", 1, 2, SignStyle.NORMAL, 3, -50}, + {"-500", 1, 2, SignStyle.NORMAL, 3, -50}, + {"-AAA", 1, 2, SignStyle.NORMAL, 1, null}, + {"+0", 1, 2, SignStyle.NORMAL, 0, null}, + {"+5", 1, 2, SignStyle.NORMAL, 0, null}, + {"+50", 1, 2, SignStyle.NORMAL, 0, null}, + {"+500", 1, 2, SignStyle.NORMAL, 0, null}, + {"+AAA", 1, 2, SignStyle.NORMAL, 0, null}, + + // always + {"0", 1, 2, SignStyle.ALWAYS, 0, null}, + {"5", 1, 2, SignStyle.ALWAYS, 0, null}, + {"50", 1, 2, SignStyle.ALWAYS, 0, null}, + {"500", 1, 2, SignStyle.ALWAYS, 0, null}, + {"-0", 1, 2, SignStyle.ALWAYS, 0, null}, + {"-5", 1, 2, SignStyle.ALWAYS, 2, -5}, + {"-50", 1, 2, SignStyle.ALWAYS, 3, -50}, + {"-500", 1, 2, SignStyle.ALWAYS, 3, -50}, + {"-AAA", 1, 2, SignStyle.ALWAYS, 1, null}, + {"+0", 1, 2, SignStyle.ALWAYS, 2, 0}, + {"+5", 1, 2, SignStyle.ALWAYS, 2, 5}, + {"+50", 1, 2, SignStyle.ALWAYS, 3, 50}, + {"+500", 1, 2, SignStyle.ALWAYS, 3, 50}, + {"+AAA", 1, 2, SignStyle.ALWAYS, 1, null}, + + // exceeds pad + {"0", 1, 2, SignStyle.EXCEEDS_PAD, 1, 0}, + {"5", 1, 2, SignStyle.EXCEEDS_PAD, 1, 5}, + {"50", 1, 2, SignStyle.EXCEEDS_PAD, 0, null}, + {"500", 1, 2, SignStyle.EXCEEDS_PAD, 0, null}, + {"-0", 1, 2, SignStyle.EXCEEDS_PAD, 0, null}, + {"-5", 1, 2, SignStyle.EXCEEDS_PAD, 2, -5}, + {"-50", 1, 2, SignStyle.EXCEEDS_PAD, 3, -50}, + {"-500", 1, 2, SignStyle.EXCEEDS_PAD, 3, -50}, + {"-AAA", 1, 2, SignStyle.EXCEEDS_PAD, 1, null}, + {"+0", 1, 2, SignStyle.EXCEEDS_PAD, 0, null}, + {"+5", 1, 2, SignStyle.EXCEEDS_PAD, 0, null}, + {"+50", 1, 2, SignStyle.EXCEEDS_PAD, 3, 50}, + {"+500", 1, 2, SignStyle.EXCEEDS_PAD, 3, 50}, + {"+AAA", 1, 2, SignStyle.EXCEEDS_PAD, 1, null}, + }; + } + + @Test(dataProvider="parseSignsStrict") + public void test_parseSignsStrict(String input, int min, int max, SignStyle style, int parseLen, Integer parseVal) throws Exception { + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder dtb = getFormatter(DAY_OF_MONTH, min, max, style).parseToBuilder(input, pos); + if (pos.getErrorIndex() != -1) { + assertEquals(pos.getErrorIndex(), parseLen); + } else { + assertEquals(pos.getIndex(), parseLen); + assertEquals(dtb.getLong(DAY_OF_MONTH), (long)parseVal); + } + } + + //----------------------------------------------------------------------- + @DataProvider(name="parseSignsLenient") + Object[][] provider_parseSignsLenient() { + return new Object[][] { + // never + {"0", 1, 2, SignStyle.NEVER, 1, 0}, + {"5", 1, 2, SignStyle.NEVER, 1, 5}, + {"50", 1, 2, SignStyle.NEVER, 2, 50}, + {"500", 1, 2, SignStyle.NEVER, 2, 50}, + {"-0", 1, 2, SignStyle.NEVER, 2, 0}, + {"-5", 1, 2, SignStyle.NEVER, 2, -5}, + {"-50", 1, 2, SignStyle.NEVER, 3, -50}, + {"-500", 1, 2, SignStyle.NEVER, 3, -50}, + {"-AAA", 1, 2, SignStyle.NEVER, 1, null}, + {"+0", 1, 2, SignStyle.NEVER, 2, 0}, + {"+5", 1, 2, SignStyle.NEVER, 2, 5}, + {"+50", 1, 2, SignStyle.NEVER, 3, 50}, + {"+500", 1, 2, SignStyle.NEVER, 3, 50}, + {"+AAA", 1, 2, SignStyle.NEVER, 1, null}, + {"50", 2, 2, SignStyle.NEVER, 2, 50}, + {"-50", 2, 2, SignStyle.NEVER, 0, null}, + {"+50", 2, 2, SignStyle.NEVER, 0, null}, + + // not negative + {"0", 1, 2, SignStyle.NOT_NEGATIVE, 1, 0}, + {"5", 1, 2, SignStyle.NOT_NEGATIVE, 1, 5}, + {"50", 1, 2, SignStyle.NOT_NEGATIVE, 2, 50}, + {"500", 1, 2, SignStyle.NOT_NEGATIVE, 2, 50}, + {"-0", 1, 2, SignStyle.NOT_NEGATIVE, 2, 0}, + {"-5", 1, 2, SignStyle.NOT_NEGATIVE, 2, -5}, + {"-50", 1, 2, SignStyle.NOT_NEGATIVE, 3, -50}, + {"-500", 1, 2, SignStyle.NOT_NEGATIVE, 3, -50}, + {"-AAA", 1, 2, SignStyle.NOT_NEGATIVE, 1, null}, + {"+0", 1, 2, SignStyle.NOT_NEGATIVE, 2, 0}, + {"+5", 1, 2, SignStyle.NOT_NEGATIVE, 2, 5}, + {"+50", 1, 2, SignStyle.NOT_NEGATIVE, 3, 50}, + {"+500", 1, 2, SignStyle.NOT_NEGATIVE, 3, 50}, + {"+AAA", 1, 2, SignStyle.NOT_NEGATIVE, 1, null}, + {"50", 2, 2, SignStyle.NOT_NEGATIVE, 2, 50}, + {"-50", 2, 2, SignStyle.NOT_NEGATIVE, 0, null}, + {"+50", 2, 2, SignStyle.NOT_NEGATIVE, 0, null}, + + // normal + {"0", 1, 2, SignStyle.NORMAL, 1, 0}, + {"5", 1, 2, SignStyle.NORMAL, 1, 5}, + {"50", 1, 2, SignStyle.NORMAL, 2, 50}, + {"500", 1, 2, SignStyle.NORMAL, 2, 50}, + {"-0", 1, 2, SignStyle.NORMAL, 2, 0}, + {"-5", 1, 2, SignStyle.NORMAL, 2, -5}, + {"-50", 1, 2, SignStyle.NORMAL, 3, -50}, + {"-500", 1, 2, SignStyle.NORMAL, 3, -50}, + {"-AAA", 1, 2, SignStyle.NORMAL, 1, null}, + {"+0", 1, 2, SignStyle.NORMAL, 2, 0}, + {"+5", 1, 2, SignStyle.NORMAL, 2, 5}, + {"+50", 1, 2, SignStyle.NORMAL, 3, 50}, + {"+500", 1, 2, SignStyle.NORMAL, 3, 50}, + {"+AAA", 1, 2, SignStyle.NORMAL, 1, null}, + {"50", 2, 2, SignStyle.NORMAL, 2, 50}, + {"-50", 2, 2, SignStyle.NORMAL, 3, -50}, + {"+50", 2, 2, SignStyle.NORMAL, 3, 50}, + + // always + {"0", 1, 2, SignStyle.ALWAYS, 1, 0}, + {"5", 1, 2, SignStyle.ALWAYS, 1, 5}, + {"50", 1, 2, SignStyle.ALWAYS, 2, 50}, + {"500", 1, 2, SignStyle.ALWAYS, 2, 50}, + {"-0", 1, 2, SignStyle.ALWAYS, 2, 0}, + {"-5", 1, 2, SignStyle.ALWAYS, 2, -5}, + {"-50", 1, 2, SignStyle.ALWAYS, 3, -50}, + {"-500", 1, 2, SignStyle.ALWAYS, 3, -50}, + {"-AAA", 1, 2, SignStyle.ALWAYS, 1, null}, + {"+0", 1, 2, SignStyle.ALWAYS, 2, 0}, + {"+5", 1, 2, SignStyle.ALWAYS, 2, 5}, + {"+50", 1, 2, SignStyle.ALWAYS, 3, 50}, + {"+500", 1, 2, SignStyle.ALWAYS, 3, 50}, + {"+AAA", 1, 2, SignStyle.ALWAYS, 1, null}, + + // exceeds pad + {"0", 1, 2, SignStyle.EXCEEDS_PAD, 1, 0}, + {"5", 1, 2, SignStyle.EXCEEDS_PAD, 1, 5}, + {"50", 1, 2, SignStyle.EXCEEDS_PAD, 2, 50}, + {"500", 1, 2, SignStyle.EXCEEDS_PAD, 2, 50}, + {"-0", 1, 2, SignStyle.EXCEEDS_PAD, 2, 0}, + {"-5", 1, 2, SignStyle.EXCEEDS_PAD, 2, -5}, + {"-50", 1, 2, SignStyle.EXCEEDS_PAD, 3, -50}, + {"-500", 1, 2, SignStyle.EXCEEDS_PAD, 3, -50}, + {"-AAA", 1, 2, SignStyle.EXCEEDS_PAD, 1, null}, + {"+0", 1, 2, SignStyle.EXCEEDS_PAD, 2, 0}, + {"+5", 1, 2, SignStyle.EXCEEDS_PAD, 2, 5}, + {"+50", 1, 2, SignStyle.EXCEEDS_PAD, 3, 50}, + {"+500", 1, 2, SignStyle.EXCEEDS_PAD, 3, 50}, + {"+AAA", 1, 2, SignStyle.EXCEEDS_PAD, 1, null}, + }; + } + + @Test(dataProvider="parseSignsLenient") + public void test_parseSignsLenient(String input, int min, int max, SignStyle style, int parseLen, Integer parseVal) throws Exception { + setStrict(false); + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder dtb = getFormatter(DAY_OF_MONTH, min, max, style).parseToBuilder(input, pos); + if (pos.getErrorIndex() != -1) { + assertEquals(pos.getErrorIndex(), parseLen); + } else { + assertEquals(pos.getIndex(), parseLen); + assertEquals(dtb.getLong(DAY_OF_MONTH), (long)parseVal); + } + } + + //----------------------------------------------------------------------- + @DataProvider(name="parseDigitsLenient") + Object[][] provider_parseDigitsLenient() { + return new Object[][] { + // never + {"5", 1, 2, SignStyle.NEVER, 1, 5}, + {"5", 2, 2, SignStyle.NEVER, 1, 5}, + {"54", 1, 3, SignStyle.NEVER, 2, 54}, + {"54", 2, 3, SignStyle.NEVER, 2, 54}, + {"54", 3, 3, SignStyle.NEVER, 2, 54}, + {"543", 1, 3, SignStyle.NEVER, 3, 543}, + {"543", 2, 3, SignStyle.NEVER, 3, 543}, + {"543", 3, 3, SignStyle.NEVER, 3, 543}, + {"5432", 1, 3, SignStyle.NEVER, 3, 543}, + {"5432", 2, 3, SignStyle.NEVER, 3, 543}, + {"5432", 3, 3, SignStyle.NEVER, 3, 543}, + {"5AAA", 2, 3, SignStyle.NEVER, 1, 5}, + + // not negative + {"5", 1, 2, SignStyle.NOT_NEGATIVE, 1, 5}, + {"5", 2, 2, SignStyle.NOT_NEGATIVE, 1, 5}, + {"54", 1, 3, SignStyle.NOT_NEGATIVE, 2, 54}, + {"54", 2, 3, SignStyle.NOT_NEGATIVE, 2, 54}, + {"54", 3, 3, SignStyle.NOT_NEGATIVE, 2, 54}, + {"543", 1, 3, SignStyle.NOT_NEGATIVE, 3, 543}, + {"543", 2, 3, SignStyle.NOT_NEGATIVE, 3, 543}, + {"543", 3, 3, SignStyle.NOT_NEGATIVE, 3, 543}, + {"5432", 1, 3, SignStyle.NOT_NEGATIVE, 3, 543}, + {"5432", 2, 3, SignStyle.NOT_NEGATIVE, 3, 543}, + {"5432", 3, 3, SignStyle.NOT_NEGATIVE, 3, 543}, + {"5AAA", 2, 3, SignStyle.NOT_NEGATIVE, 1, 5}, + + // normal + {"5", 1, 2, SignStyle.NORMAL, 1, 5}, + {"5", 2, 2, SignStyle.NORMAL, 1, 5}, + {"54", 1, 3, SignStyle.NORMAL, 2, 54}, + {"54", 2, 3, SignStyle.NORMAL, 2, 54}, + {"54", 3, 3, SignStyle.NORMAL, 2, 54}, + {"543", 1, 3, SignStyle.NORMAL, 3, 543}, + {"543", 2, 3, SignStyle.NORMAL, 3, 543}, + {"543", 3, 3, SignStyle.NORMAL, 3, 543}, + {"5432", 1, 3, SignStyle.NORMAL, 3, 543}, + {"5432", 2, 3, SignStyle.NORMAL, 3, 543}, + {"5432", 3, 3, SignStyle.NORMAL, 3, 543}, + {"5AAA", 2, 3, SignStyle.NORMAL, 1, 5}, + + // always + {"5", 1, 2, SignStyle.ALWAYS, 1, 5}, + {"5", 2, 2, SignStyle.ALWAYS, 1, 5}, + {"54", 1, 3, SignStyle.ALWAYS, 2, 54}, + {"54", 2, 3, SignStyle.ALWAYS, 2, 54}, + {"54", 3, 3, SignStyle.ALWAYS, 2, 54}, + {"543", 1, 3, SignStyle.ALWAYS, 3, 543}, + {"543", 2, 3, SignStyle.ALWAYS, 3, 543}, + {"543", 3, 3, SignStyle.ALWAYS, 3, 543}, + {"5432", 1, 3, SignStyle.ALWAYS, 3, 543}, + {"5432", 2, 3, SignStyle.ALWAYS, 3, 543}, + {"5432", 3, 3, SignStyle.ALWAYS, 3, 543}, + {"5AAA", 2, 3, SignStyle.ALWAYS, 1, 5}, + + // exceeds pad + {"5", 1, 2, SignStyle.EXCEEDS_PAD, 1, 5}, + {"5", 2, 2, SignStyle.EXCEEDS_PAD, 1, 5}, + {"54", 1, 3, SignStyle.EXCEEDS_PAD, 2, 54}, + {"54", 2, 3, SignStyle.EXCEEDS_PAD, 2, 54}, + {"54", 3, 3, SignStyle.EXCEEDS_PAD, 2, 54}, + {"543", 1, 3, SignStyle.EXCEEDS_PAD, 3, 543}, + {"543", 2, 3, SignStyle.EXCEEDS_PAD, 3, 543}, + {"543", 3, 3, SignStyle.EXCEEDS_PAD, 3, 543}, + {"5432", 1, 3, SignStyle.EXCEEDS_PAD, 3, 543}, + {"5432", 2, 3, SignStyle.EXCEEDS_PAD, 3, 543}, + {"5432", 3, 3, SignStyle.EXCEEDS_PAD, 3, 543}, + {"5AAA", 2, 3, SignStyle.EXCEEDS_PAD, 1, 5}, + }; + } + + @Test(dataProvider="parseDigitsLenient") + public void test_parseDigitsLenient(String input, int min, int max, SignStyle style, int parseLen, Integer parseVal) throws Exception { + setStrict(false); + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder dtb = getFormatter(DAY_OF_MONTH, min, max, style).parseToBuilder(input, pos); + if (pos.getErrorIndex() != -1) { + assertEquals(pos.getErrorIndex(), parseLen); + } else { + assertEquals(pos.getIndex(), parseLen); + assertEquals(dtb.getLong(DAY_OF_MONTH), (long)parseVal); + } + } + + //----------------------------------------------------------------------- + @DataProvider(name="parseDigitsAdjacentLenient") + Object[][] provider_parseDigitsAdjacentLenient() { + return new Object[][] { + // never + {"5", 1, null, null}, + {"54", 1, null, null}, + + {"543", 3, 5, 43}, + {"543A", 3, 5, 43}, + + {"5432", 4, 54, 32}, + {"5432A", 4, 54, 32}, + + {"54321", 4, 54, 32}, + {"54321A", 4, 54, 32}, + }; + } + + @Test(dataProvider="parseDigitsAdjacentLenient") + public void test_parseDigitsAdjacentLenient(String input, int parseLen, Integer parseMonth, Integer parsedDay) throws Exception { + setStrict(false); + ParsePosition pos = new ParsePosition(0); + DateTimeFormatter f = builder + .appendValue(MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL) + .appendValue(DAY_OF_MONTH, 2).toFormatter(locale).withSymbols(symbols); + DateTimeBuilder dtb = f.parseToBuilder(input, pos); + if (pos.getErrorIndex() != -1) { + assertEquals(pos.getErrorIndex(), parseLen); + } else { + assertEquals(pos.getIndex(), parseLen); + assertEquals(dtb.getLong(MONTH_OF_YEAR), (long) parseMonth); + assertEquals(dtb.getLong(DAY_OF_MONTH), (long) parsedDay); + } + } + +} diff --git a/jdk/test/java/time/test/java/time/format/TestNumberPrinter.java b/jdk/test/java/time/test/java/time/format/TestNumberPrinter.java new file mode 100644 index 00000000000..80c05b8eb9f --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/TestNumberPrinter.java @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.format; + +import java.time.format.*; + +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.HOUR_OF_DAY; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +import java.time.DateTimeException; +import java.time.LocalDate; +import test.java.time.temporal.MockFieldValue; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test SimpleNumberPrinterParser. + */ +@Test +public class TestNumberPrinter extends AbstractTestPrinterParser { + + //----------------------------------------------------------------------- + @Test(expectedExceptions=DateTimeException.class) + public void test_print_emptyCalendrical() throws Exception { + getFormatter(DAY_OF_MONTH, 1, 2, SignStyle.NEVER).printTo(EMPTY_DTA, buf); + } + + public void test_print_append() throws Exception { + buf.append("EXISTING"); + getFormatter(DAY_OF_MONTH, 1, 2, SignStyle.NEVER).printTo(LocalDate.of(2012, 1, 3), buf); + assertEquals(buf.toString(), "EXISTING3"); + } + + //----------------------------------------------------------------------- + @DataProvider(name="Pad") + Object[][] provider_pad() { + return new Object[][] { + {1, 1, -10, null}, + {1, 1, -9, "9"}, + {1, 1, -1, "1"}, + {1, 1, 0, "0"}, + {1, 1, 3, "3"}, + {1, 1, 9, "9"}, + {1, 1, 10, null}, + + {1, 2, -100, null}, + {1, 2, -99, "99"}, + {1, 2, -10, "10"}, + {1, 2, -9, "9"}, + {1, 2, -1, "1"}, + {1, 2, 0, "0"}, + {1, 2, 3, "3"}, + {1, 2, 9, "9"}, + {1, 2, 10, "10"}, + {1, 2, 99, "99"}, + {1, 2, 100, null}, + + {2, 2, -100, null}, + {2, 2, -99, "99"}, + {2, 2, -10, "10"}, + {2, 2, -9, "09"}, + {2, 2, -1, "01"}, + {2, 2, 0, "00"}, + {2, 2, 3, "03"}, + {2, 2, 9, "09"}, + {2, 2, 10, "10"}, + {2, 2, 99, "99"}, + {2, 2, 100, null}, + + {1, 3, -1000, null}, + {1, 3, -999, "999"}, + {1, 3, -100, "100"}, + {1, 3, -99, "99"}, + {1, 3, -10, "10"}, + {1, 3, -9, "9"}, + {1, 3, -1, "1"}, + {1, 3, 0, "0"}, + {1, 3, 3, "3"}, + {1, 3, 9, "9"}, + {1, 3, 10, "10"}, + {1, 3, 99, "99"}, + {1, 3, 100, "100"}, + {1, 3, 999, "999"}, + {1, 3, 1000, null}, + + {2, 3, -1000, null}, + {2, 3, -999, "999"}, + {2, 3, -100, "100"}, + {2, 3, -99, "99"}, + {2, 3, -10, "10"}, + {2, 3, -9, "09"}, + {2, 3, -1, "01"}, + {2, 3, 0, "00"}, + {2, 3, 3, "03"}, + {2, 3, 9, "09"}, + {2, 3, 10, "10"}, + {2, 3, 99, "99"}, + {2, 3, 100, "100"}, + {2, 3, 999, "999"}, + {2, 3, 1000, null}, + + {3, 3, -1000, null}, + {3, 3, -999, "999"}, + {3, 3, -100, "100"}, + {3, 3, -99, "099"}, + {3, 3, -10, "010"}, + {3, 3, -9, "009"}, + {3, 3, -1, "001"}, + {3, 3, 0, "000"}, + {3, 3, 3, "003"}, + {3, 3, 9, "009"}, + {3, 3, 10, "010"}, + {3, 3, 99, "099"}, + {3, 3, 100, "100"}, + {3, 3, 999, "999"}, + {3, 3, 1000, null}, + + {1, 10, Integer.MAX_VALUE - 1, "2147483646"}, + {1, 10, Integer.MAX_VALUE, "2147483647"}, + {1, 10, Integer.MIN_VALUE + 1, "2147483647"}, + {1, 10, Integer.MIN_VALUE, "2147483648"}, + }; + } + + @Test(dataProvider="Pad") + public void test_pad_NOT_NEGATIVE(int minPad, int maxPad, long value, String result) throws Exception { + try { + getFormatter(DAY_OF_MONTH, minPad, maxPad, SignStyle.NOT_NEGATIVE).printTo(new MockFieldValue(DAY_OF_MONTH, value), buf); + if (result == null || value < 0) { + fail("Expected exception"); + } + assertEquals(buf.toString(), result); + } catch (DateTimePrintException ex) { + if (result == null || value < 0) { + assertEquals(ex.getMessage().contains(DAY_OF_MONTH.getName()), true); + } else { + throw ex; + } + } + } + + @Test(dataProvider="Pad") + public void test_pad_NEVER(int minPad, int maxPad, long value, String result) throws Exception { + try { + getFormatter(DAY_OF_MONTH, minPad, maxPad, SignStyle.NEVER).printTo(new MockFieldValue(DAY_OF_MONTH, value), buf); + if (result == null) { + fail("Expected exception"); + } + assertEquals(buf.toString(), result); + } catch (DateTimePrintException ex) { + if (result != null) { + throw ex; + } + assertEquals(ex.getMessage().contains(DAY_OF_MONTH.getName()), true); + } + } + + @Test(dataProvider="Pad") + public void test_pad_NORMAL(int minPad, int maxPad, long value, String result) throws Exception { + try { + getFormatter(DAY_OF_MONTH, minPad, maxPad, SignStyle.NORMAL).printTo(new MockFieldValue(DAY_OF_MONTH, value), buf); + if (result == null) { + fail("Expected exception"); + } + assertEquals(buf.toString(), (value < 0 ? "-" + result : result)); + } catch (DateTimePrintException ex) { + if (result != null) { + throw ex; + } + assertEquals(ex.getMessage().contains(DAY_OF_MONTH.getName()), true); + } + } + + @Test(dataProvider="Pad") + public void test_pad_ALWAYS(int minPad, int maxPad, long value, String result) throws Exception { + try { + getFormatter(DAY_OF_MONTH, minPad, maxPad, SignStyle.ALWAYS).printTo(new MockFieldValue(DAY_OF_MONTH, value), buf); + if (result == null) { + fail("Expected exception"); + } + assertEquals(buf.toString(), (value < 0 ? "-" + result : "+" + result)); + } catch (DateTimePrintException ex) { + if (result != null) { + throw ex; + } + assertEquals(ex.getMessage().contains(DAY_OF_MONTH.getName()), true); + } + } + + @Test(dataProvider="Pad") + public void test_pad_EXCEEDS_PAD(int minPad, int maxPad, long value, String result) throws Exception { + try { + getFormatter(DAY_OF_MONTH, minPad, maxPad, SignStyle.EXCEEDS_PAD).printTo(new MockFieldValue(DAY_OF_MONTH, value), buf); + if (result == null) { + fail("Expected exception"); + return; // unreachable + } + if (result.length() > minPad || value < 0) { + result = (value < 0 ? "-" + result : "+" + result); + } + assertEquals(buf.toString(), result); + } catch (DateTimePrintException ex) { + if (result != null) { + throw ex; + } + assertEquals(ex.getMessage().contains(DAY_OF_MONTH.getName()), true); + } + } + + //----------------------------------------------------------------------- + public void test_toString1() throws Exception { + assertEquals(getFormatter(HOUR_OF_DAY, 1, 19, SignStyle.NORMAL).toString(), "Value(HourOfDay)"); + } + + public void test_toString2() throws Exception { + assertEquals(getFormatter(HOUR_OF_DAY, 2, 2, SignStyle.NOT_NEGATIVE).toString(), "Value(HourOfDay,2)"); + } + + public void test_toString3() throws Exception { + assertEquals(getFormatter(HOUR_OF_DAY, 1, 2, SignStyle.NOT_NEGATIVE).toString(), "Value(HourOfDay,1,2,NOT_NEGATIVE)"); + } + +} diff --git a/jdk/test/java/time/test/java/time/format/TestPadParserDecorator.java b/jdk/test/java/time/test/java/time/format/TestPadParserDecorator.java new file mode 100644 index 00000000000..08a9e24544d --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/TestPadParserDecorator.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.format; + +import java.time.format.*; + +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static org.testng.Assert.assertEquals; + +import java.text.ParsePosition; +import java.time.format.DateTimeBuilder; + +import org.testng.annotations.Test; + +/** + * Test PadPrinterParserDecorator. + */ +@Test(groups={"implementation"}) +public class TestPadParserDecorator extends AbstractTestPrinterParser { + + //----------------------------------------------------------------------- + @Test(expectedExceptions=IndexOutOfBoundsException.class) + public void test_parse_negativePosition() throws Exception { + builder.padNext(3, '-').appendLiteral('Z'); + getFormatter().parseToBuilder("--Z", new ParsePosition(-1)); + } + + @Test(expectedExceptions=IndexOutOfBoundsException.class) + public void test_parse_offEndPosition() throws Exception { + builder.padNext(3, '-').appendLiteral('Z'); + getFormatter().parseToBuilder("--Z", new ParsePosition(4)); + } + + //----------------------------------------------------------------------- + public void test_parse() throws Exception { + ParsePosition pos = new ParsePosition(0); + builder.padNext(3, '-').appendValue(MONTH_OF_YEAR, 1, 3, SignStyle.NEVER); + DateTimeBuilder dtb = getFormatter().parseToBuilder("--2", pos); + assertEquals(pos.getIndex(), 3); + assertEquals(dtb.getFieldValueMap().size(), 1); + assertEquals(dtb.getLong(MONTH_OF_YEAR), 2L); + } + + public void test_parse_noReadBeyond() throws Exception { + ParsePosition pos = new ParsePosition(0); + builder.padNext(3, '-').appendValue(MONTH_OF_YEAR, 1, 3, SignStyle.NEVER); + DateTimeBuilder dtb = getFormatter().parseToBuilder("--22", pos); + assertEquals(pos.getIndex(), 3); + assertEquals(dtb.getFieldValueMap().size(), 1); + assertEquals(dtb.getLong(MONTH_OF_YEAR), 2L); + } + + public void test_parse_textLessThanPadWidth() throws Exception { + ParsePosition pos = new ParsePosition(0); + builder.padNext(3, '-').appendValue(MONTH_OF_YEAR, 1, 3, SignStyle.NEVER); + DateTimeBuilder dtb = getFormatter().parseToBuilder("-1", pos); + assertEquals(pos.getErrorIndex(), 0); + } + + public void test_parse_decoratedErrorPassedBack() throws Exception { + ParsePosition pos = new ParsePosition(0); + builder.padNext(3, '-').appendValue(MONTH_OF_YEAR, 1, 3, SignStyle.NEVER); + DateTimeBuilder dtb = getFormatter().parseToBuilder("--A", pos); + assertEquals(pos.getErrorIndex(), 2); + } + + public void test_parse_decoratedDidNotParseToPadWidth() throws Exception { + ParsePosition pos = new ParsePosition(0); + builder.padNext(3, '-').appendValue(MONTH_OF_YEAR, 1, 3, SignStyle.NEVER); + DateTimeBuilder dtb = getFormatter().parseToBuilder("-1X", pos); + assertEquals(pos.getErrorIndex(), 0); + } + + //----------------------------------------------------------------------- + public void test_parse_decoratedStartsWithPad() throws Exception { + ParsePosition pos = new ParsePosition(0); + builder.padNext(8, '-').appendLiteral("-HELLO-"); + DateTimeBuilder dtb = getFormatter().parseToBuilder("--HELLO-", pos); + assertEquals(pos.getIndex(), 8); + assertEquals(dtb.getFieldValueMap().size(), 0); + } + +} diff --git a/jdk/test/java/time/test/java/time/format/TestPadPrinterDecorator.java b/jdk/test/java/time/test/java/time/format/TestPadPrinterDecorator.java new file mode 100644 index 00000000000..d6302aee4d7 --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/TestPadPrinterDecorator.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.format; + +import java.time.format.*; + +import static org.testng.Assert.assertEquals; + +import java.time.LocalDate; + +import org.testng.annotations.Test; + +/** + * Test PadPrinterDecorator. + */ +@Test(groups={"implementation"}) +public class TestPadPrinterDecorator extends AbstractTestPrinterParser { + + //----------------------------------------------------------------------- + public void test_print_emptyCalendrical() throws Exception { + builder.padNext(3, '-').appendLiteral('Z'); + getFormatter().printTo(EMPTY_DTA, buf); + assertEquals(buf.toString(), "--Z"); + } + + public void test_print_fullDateTime() throws Exception { + builder.padNext(3, '-').appendLiteral('Z'); + getFormatter().printTo(LocalDate.of(2008, 12, 3), buf); + assertEquals(buf.toString(), "--Z"); + } + + public void test_print_append() throws Exception { + buf.append("EXISTING"); + builder.padNext(3, '-').appendLiteral('Z'); + getFormatter().printTo(EMPTY_DTA, buf); + assertEquals(buf.toString(), "EXISTING--Z"); + } + + //----------------------------------------------------------------------- + public void test_print_noPadRequiredSingle() throws Exception { + builder.padNext(1, '-').appendLiteral('Z'); + getFormatter().printTo(EMPTY_DTA, buf); + assertEquals(buf.toString(), "Z"); + } + + public void test_print_padRequiredSingle() throws Exception { + builder.padNext(5, '-').appendLiteral('Z'); + getFormatter().printTo(EMPTY_DTA, buf); + assertEquals(buf.toString(), "----Z"); + } + + public void test_print_noPadRequiredMultiple() throws Exception { + builder.padNext(4, '-').appendLiteral("WXYZ"); + getFormatter().printTo(EMPTY_DTA, buf); + assertEquals(buf.toString(), "WXYZ"); + } + + public void test_print_padRequiredMultiple() throws Exception { + builder.padNext(5, '-').appendLiteral("WXYZ"); + getFormatter().printTo(EMPTY_DTA, buf); + assertEquals(buf.toString(), "-WXYZ"); + } + + @Test(expectedExceptions=DateTimePrintException.class) + public void test_print_overPad() throws Exception { + builder.padNext(3, '-').appendLiteral("WXYZ"); + getFormatter().printTo(EMPTY_DTA, buf); + } + + //----------------------------------------------------------------------- + public void test_toString1() throws Exception { + builder.padNext(5, ' ').appendLiteral('Y'); + assertEquals(getFormatter().toString(), "Pad('Y',5)"); + } + + public void test_toString2() throws Exception { + builder.padNext(5, '-').appendLiteral('Y'); + assertEquals(getFormatter().toString(), "Pad('Y',5,'-')"); + } + +} diff --git a/jdk/test/java/time/test/java/time/format/TestReducedParser.java b/jdk/test/java/time/test/java/time/format/TestReducedParser.java new file mode 100644 index 00000000000..bcd383039ff --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/TestReducedParser.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2010-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.format; + +import java.time.format.*; + +import static java.time.temporal.ChronoField.DAY_OF_YEAR; +import static java.time.temporal.ChronoField.YEAR; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.text.ParsePosition; +import java.time.format.DateTimeBuilder; +import java.time.temporal.TemporalField; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test ReducedPrinterParser. + */ +@Test(groups={"implementation"}) +public class TestReducedParser extends AbstractTestPrinterParser { + + private DateTimeFormatter getFormatter0(TemporalField field, int width, int baseValue) { + return builder.appendValueReduced(field, width, baseValue).toFormatter(locale).withSymbols(symbols); + } + + //----------------------------------------------------------------------- + @DataProvider(name="error") + Object[][] data_error() { + return new Object[][] { + {YEAR, 2, 2010, "12", -1, IndexOutOfBoundsException.class}, + {YEAR, 2, 2010, "12", 3, IndexOutOfBoundsException.class}, + }; + } + + @Test(dataProvider="error") + public void test_parse_error(TemporalField field, int width, int baseValue, String text, int pos, Class expected) { + try { + getFormatter0(field, width, baseValue).parseToBuilder(text, new ParsePosition(pos)); + } catch (RuntimeException ex) { + assertTrue(expected.isInstance(ex)); + } + } + + //----------------------------------------------------------------------- + public void test_parse_fieldRangeIgnored() throws Exception { + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder dtb = getFormatter0(DAY_OF_YEAR, 3, 10).parseToBuilder("456", pos); + assertEquals(pos.getIndex(), 3); + assertParsed(dtb, DAY_OF_YEAR, 456L); // parsed dayOfYear=456 + } + + //----------------------------------------------------------------------- + @DataProvider(name="Parse") + Object[][] provider_parse() { + return new Object[][] { + // negative zero + {YEAR, 1, 2010, "-0", 0, 0, null}, + + // general + {YEAR, 2, 2010, "Xxx12Xxx", 3, 5, 2012}, + {YEAR, 2, 2010, "12345", 0, 2, 2012}, + {YEAR, 2, 2010, "12-45", 0, 2, 2012}, + + // insufficient digits + {YEAR, 2, 2010, "0", 0, 0, null}, + {YEAR, 2, 2010, "1", 0, 0, null}, + {YEAR, 2, 2010, "1", 1, 1, null}, + {YEAR, 2, 2010, "1-2", 0, 0, null}, + {YEAR, 2, 2010, "9", 0, 0, null}, + + // other junk + {YEAR, 2, 2010, "A0", 0, 0, null}, + {YEAR, 2, 2010, "0A", 0, 0, null}, + {YEAR, 2, 2010, " 1", 0, 0, null}, + {YEAR, 2, 2010, "-1", 0, 0, null}, + {YEAR, 2, 2010, "-10", 0, 0, null}, + + // parse OK 1 + {YEAR, 1, 2010, "0", 0, 1, 2010}, + {YEAR, 1, 2010, "9", 0, 1, 2019}, + {YEAR, 1, 2010, "10", 0, 1, 2011}, + + {YEAR, 1, 2005, "0", 0, 1, 2010}, + {YEAR, 1, 2005, "4", 0, 1, 2014}, + {YEAR, 1, 2005, "5", 0, 1, 2005}, + {YEAR, 1, 2005, "9", 0, 1, 2009}, + {YEAR, 1, 2005, "10", 0, 1, 2011}, + + // parse OK 2 + {YEAR, 2, 2010, "00", 0, 2, 2100}, + {YEAR, 2, 2010, "09", 0, 2, 2109}, + {YEAR, 2, 2010, "10", 0, 2, 2010}, + {YEAR, 2, 2010, "99", 0, 2, 2099}, + {YEAR, 2, 2010, "100", 0, 2, 2010}, + + // parse OK 2 + {YEAR, 2, -2005, "05", 0, 2, -2005}, + {YEAR, 2, -2005, "00", 0, 2, -2000}, + {YEAR, 2, -2005, "99", 0, 2, -1999}, + {YEAR, 2, -2005, "06", 0, 2, -1906}, + {YEAR, 2, -2005, "100", 0, 2, -1910}, + }; + } + + @Test(dataProvider="Parse") + public void test_parse(TemporalField field, int width, int baseValue, String input, int pos, int parseLen, Integer parseVal) { + ParsePosition ppos = new ParsePosition(pos); + DateTimeBuilder dtb = getFormatter0(field, width, baseValue).parseToBuilder(input, ppos); + if (ppos.getErrorIndex() != -1) { + assertEquals(ppos.getErrorIndex(), parseLen); + } else { + assertEquals(ppos.getIndex(), parseLen); + assertParsed(dtb, YEAR, parseVal != null ? (long) parseVal : null); + } + } + + @Test(dataProvider="Parse") + public void test_parseLenient(TemporalField field, int width, int baseValue, String input, int pos, int parseLen, Integer parseVal) { + setStrict(false); + ParsePosition ppos = new ParsePosition(pos); + DateTimeBuilder dtb = getFormatter0(field, width, baseValue).parseToBuilder(input, ppos); + if (ppos.getErrorIndex() != -1) { + assertEquals(ppos.getErrorIndex(), parseLen); + } else { + assertEquals(ppos.getIndex(), parseLen); + assertParsed(dtb, YEAR, parseVal != null ? (long) parseVal : null); + } + } + + private void assertParsed(DateTimeBuilder dtb, TemporalField field, Long value) { + if (value == null) { + assertEquals(dtb, null); + } else { + assertEquals(dtb.getLong(field), (long)value); + } + } + +} diff --git a/jdk/test/java/time/test/java/time/format/TestReducedPrinter.java b/jdk/test/java/time/test/java/time/format/TestReducedPrinter.java new file mode 100644 index 00000000000..698496dc57e --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/TestReducedPrinter.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2010-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.format; + +import java.time.format.*; + +import static java.time.temporal.ChronoField.YEAR; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.temporal.TemporalField; +import test.java.time.temporal.MockFieldValue; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test ReducedPrinterParser. + */ +@Test(groups={"implementation"}) +public class TestReducedPrinter extends AbstractTestPrinterParser { + + private DateTimeFormatter getFormatter0(TemporalField field, int width, int baseValue) { + return builder.appendValueReduced(field, width, baseValue).toFormatter(locale).withSymbols(symbols); + } + + //----------------------------------------------------------------------- + @Test(expectedExceptions=DateTimeException.class) + public void test_print_emptyCalendrical() throws Exception { + getFormatter0(YEAR, 2, 2010).printTo(EMPTY_DTA, buf); + } + + //----------------------------------------------------------------------- + public void test_print_append() throws Exception { + buf.append("EXISTING"); + getFormatter0(YEAR, 2, 2010).printTo(LocalDate.of(2012, 1, 1), buf); + assertEquals(buf.toString(), "EXISTING12"); + } + + //----------------------------------------------------------------------- + @DataProvider(name="Pivot") + Object[][] provider_pivot() { + return new Object[][] { + {1, 2010, 2010, "0"}, + {1, 2010, 2011, "1"}, + {1, 2010, 2012, "2"}, + {1, 2010, 2013, "3"}, + {1, 2010, 2014, "4"}, + {1, 2010, 2015, "5"}, + {1, 2010, 2016, "6"}, + {1, 2010, 2017, "7"}, + {1, 2010, 2018, "8"}, + {1, 2010, 2019, "9"}, + {1, 2010, 2009, "9"}, + {1, 2010, 2020, "0"}, + + {2, 2010, 2010, "10"}, + {2, 2010, 2011, "11"}, + {2, 2010, 2021, "21"}, + {2, 2010, 2099, "99"}, + {2, 2010, 2100, "00"}, + {2, 2010, 2109, "09"}, + {2, 2010, 2009, "09"}, + {2, 2010, 2110, "10"}, + + {2, 2005, 2005, "05"}, + {2, 2005, 2099, "99"}, + {2, 2005, 2100, "00"}, + {2, 2005, 2104, "04"}, + {2, 2005, 2004, "04"}, + {2, 2005, 2105, "05"}, + + {3, 2005, 2005, "005"}, + {3, 2005, 2099, "099"}, + {3, 2005, 2100, "100"}, + {3, 2005, 2999, "999"}, + {3, 2005, 3000, "000"}, + {3, 2005, 3004, "004"}, + {3, 2005, 2004, "004"}, + {3, 2005, 3005, "005"}, + + {9, 2005, 2005, "000002005"}, + {9, 2005, 2099, "000002099"}, + {9, 2005, 2100, "000002100"}, + {9, 2005, 999999999, "999999999"}, + {9, 2005, 1000000000, "000000000"}, + {9, 2005, 1000002004, "000002004"}, + {9, 2005, 2004, "000002004"}, + {9, 2005, 1000002005, "000002005"}, + + {2, -2005, -2005, "05"}, + {2, -2005, -2000, "00"}, + {2, -2005, -1999, "99"}, + {2, -2005, -1904, "04"}, + {2, -2005, -2006, "06"}, + {2, -2005, -1905, "05"}, + }; + } + + @Test(dataProvider="Pivot") + public void test_pivot(int width, int baseValue, int value, String result) throws Exception { + try { + getFormatter0(YEAR, width, baseValue).printTo(new MockFieldValue(YEAR, value), buf); + if (result == null) { + fail("Expected exception"); + } + assertEquals(buf.toString(), result); + } catch (DateTimePrintException ex) { + if (result == null || value < 0) { + assertEquals(ex.getMessage().contains(YEAR.getName()), true); + } else { + throw ex; + } + } + } + + //----------------------------------------------------------------------- + public void test_toString() throws Exception { + assertEquals(getFormatter0(YEAR, 2, 2005).toString(), "ReducedValue(Year,2,2005)"); + } + +} diff --git a/jdk/test/java/time/test/java/time/format/TestSettingsParser.java b/jdk/test/java/time/test/java/time/format/TestSettingsParser.java new file mode 100644 index 00000000000..4b639aa56d4 --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/TestSettingsParser.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2009-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.format; + +import java.time.format.*; + +import static org.testng.Assert.assertEquals; + +import java.text.ParsePosition; +import java.time.ZoneOffset; + +import org.testng.annotations.Test; + +/** + * Test SettingsParser. + */ +@Test(groups={"implementation"}) +public class TestSettingsParser extends AbstractTestPrinterParser { + + //----------------------------------------------------------------------- + public void test_print_sensitive() throws Exception { + setCaseSensitive(true); + getFormatter().printTo(dta, buf); + assertEquals(buf.toString(), ""); + } + + public void test_print_strict() throws Exception { + setStrict(true); + getFormatter().printTo(dta, buf); + assertEquals(buf.toString(), ""); + } + + /* + public void test_print_nulls() throws Exception { + setCaseSensitive(true); + getFormatter().printTo(null, null); + } + */ + + //----------------------------------------------------------------------- + public void test_parse_changeStyle_sensitive() throws Exception { + setCaseSensitive(true); + ParsePosition pos = new ParsePosition(0); + getFormatter().parseToBuilder("a", pos); + assertEquals(pos.getIndex(), 0); + } + + public void test_parse_changeStyle_insensitive() throws Exception { + setCaseSensitive(false); + ParsePosition pos = new ParsePosition(0); + getFormatter().parseToBuilder("a", pos); + assertEquals(pos.getIndex(), 0); + } + + public void test_parse_changeStyle_strict() throws Exception { + setStrict(true); + ParsePosition pos = new ParsePosition(0); + getFormatter().parseToBuilder("a", pos); + assertEquals(pos.getIndex(), 0); + } + + public void test_parse_changeStyle_lenient() throws Exception { + setStrict(false); + ParsePosition pos = new ParsePosition(0); + getFormatter().parseToBuilder("a", pos); + assertEquals(pos.getIndex(), 0); + } + + //----------------------------------------------------------------------- + public void test_toString_sensitive() throws Exception { + setCaseSensitive(true); + assertEquals(getFormatter().toString(), "ParseCaseSensitive(true)"); + } + + public void test_toString_insensitive() throws Exception { + setCaseSensitive(false); + assertEquals(getFormatter().toString(), "ParseCaseSensitive(false)"); + } + + public void test_toString_strict() throws Exception { + setStrict(true); + assertEquals(getFormatter().toString(), "ParseStrict(true)"); + } + + public void test_toString_lenient() throws Exception { + setStrict(false); + assertEquals(getFormatter().toString(), "ParseStrict(false)"); + } + +} diff --git a/jdk/test/java/time/test/java/time/format/TestStringLiteralParser.java b/jdk/test/java/time/test/java/time/format/TestStringLiteralParser.java new file mode 100644 index 00000000000..bd67eab39f3 --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/TestStringLiteralParser.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.format; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.time.format.DateTimeBuilder; +import java.text.ParsePosition; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test StringLiteralPrinterParser. + */ +@Test(groups={"implementation"}) +public class TestStringLiteralParser extends AbstractTestPrinterParser { + + @DataProvider(name="success") + Object[][] data_success() { + return new Object[][] { + // match + {"hello", true, "hello", 0, 5}, + {"hello", true, "helloOTHER", 0, 5}, + {"hello", true, "OTHERhelloOTHER", 5, 10}, + {"hello", true, "OTHERhello", 5, 10}, + + // no match + {"hello", true, "", 0, 0}, + {"hello", true, "a", 1, 1}, + {"hello", true, "HELLO", 0, 0}, + {"hello", true, "hlloo", 0, 0}, + {"hello", true, "OTHERhllooOTHER", 5, 5}, + {"hello", true, "OTHERhlloo", 5, 5}, + {"hello", true, "h", 0, 0}, + {"hello", true, "OTHERh", 5, 5}, + + // case insensitive + {"hello", false, "hello", 0, 5}, + {"hello", false, "HELLO", 0, 5}, + {"hello", false, "HelLo", 0, 5}, + {"hello", false, "HelLO", 0, 5}, + }; + } + + @Test(dataProvider="success") + public void test_parse_success(String s, boolean caseSensitive, String text, int pos, int expectedPos) { + setCaseSensitive(caseSensitive); + ParsePosition ppos = new ParsePosition(pos); + DateTimeBuilder result = + getFormatter(s).parseToBuilder(text, ppos); + if (ppos.getErrorIndex() != -1) { + assertEquals(ppos.getIndex(), expectedPos); + } else { + assertEquals(ppos.getIndex(), expectedPos); + assertEquals(result.getCalendricalList().size(), 0); + } + } + + //----------------------------------------------------------------------- + @DataProvider(name="error") + Object[][] data_error() { + return new Object[][] { + {"hello", "hello", -1, IndexOutOfBoundsException.class}, + {"hello", "hello", 6, IndexOutOfBoundsException.class}, + }; + } + + @Test(dataProvider="error") + public void test_parse_error(String s, String text, int pos, Class expected) { + try { + DateTimeBuilder result = + getFormatter(s).parseToBuilder(text, new ParsePosition(pos)); + assertTrue(false); + } catch (RuntimeException ex) { + assertTrue(expected.isInstance(ex)); + } + } +} diff --git a/jdk/test/java/time/test/java/time/format/TestStringLiteralPrinter.java b/jdk/test/java/time/test/java/time/format/TestStringLiteralPrinter.java new file mode 100644 index 00000000000..df018701b3c --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/TestStringLiteralPrinter.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.format; + +import java.time.format.*; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +/** + * Test StringLiteralPrinterParser. + */ +@Test(groups={"implementation"}) +public class TestStringLiteralPrinter extends AbstractTestPrinterParser { + + //----------------------------------------------------------------------- + public void test_print_emptyCalendrical() throws Exception { + buf.append("EXISTING"); + getFormatter("hello").printTo(EMPTY_DTA, buf); + assertEquals(buf.toString(), "EXISTINGhello"); + } + + public void test_print_dateTime() throws Exception { + buf.append("EXISTING"); + getFormatter("hello").printTo(dta, buf); + assertEquals(buf.toString(), "EXISTINGhello"); + } + + + + + public void test_print_emptyAppendable() throws Exception { + getFormatter("hello").printTo(dta, buf); + assertEquals(buf.toString(), "hello"); + } + + //----------------------------------------------------------------------- + public void test_toString() throws Exception { + assertEquals(getFormatter("hello").toString(), "'hello'"); + } + + public void test_toString_apos() throws Exception { + assertEquals(getFormatter("o'clock").toString(), "'o''clock'"); + } + +} diff --git a/jdk/test/java/time/test/java/time/format/TestTextParser.java b/jdk/test/java/time/test/java/time/format/TestTextParser.java new file mode 100644 index 00000000000..7961b27ac8b --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/TestTextParser.java @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.format; + +import java.time.format.*; + +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.DAY_OF_WEEK; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.text.ParsePosition; +import java.util.Locale; +import java.time.format.DateTimeBuilder; +import java.time.temporal.TemporalField; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test TextPrinterParser. + */ +@Test(groups={"implementation"}) +public class TestTextParser extends AbstractTestPrinterParser { + + //----------------------------------------------------------------------- + @DataProvider(name="error") + Object[][] data_error() { + return new Object[][] { + {DAY_OF_WEEK, TextStyle.FULL, "Monday", -1, IndexOutOfBoundsException.class}, + {DAY_OF_WEEK, TextStyle.FULL, "Monday", 7, IndexOutOfBoundsException.class}, + }; + } + + @Test(dataProvider="error") + public void test_parse_error(TemporalField field, TextStyle style, String text, int pos, Class expected) { + try { + getFormatter(field, style).parseToBuilder(text, new ParsePosition(pos)); + } catch (RuntimeException ex) { + assertTrue(expected.isInstance(ex)); + } + } + + //----------------------------------------------------------------------- + public void test_parse_midStr() throws Exception { + ParsePosition pos = new ParsePosition(3); + assertEquals(getFormatter(DAY_OF_WEEK, TextStyle.FULL) + .parseToBuilder("XxxMondayXxx", pos) + .getLong(DAY_OF_WEEK), 1L); + assertEquals(pos.getIndex(), 9); + } + + public void test_parse_remainderIgnored() throws Exception { + ParsePosition pos = new ParsePosition(0); + assertEquals(getFormatter(DAY_OF_WEEK, TextStyle.SHORT) + .parseToBuilder("Wednesday", pos) + .getLong(DAY_OF_WEEK), 3L); + assertEquals(pos.getIndex(), 3); + } + + //----------------------------------------------------------------------- + public void test_parse_noMatch1() throws Exception { + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder dtb = + getFormatter(DAY_OF_WEEK, TextStyle.FULL).parseToBuilder("Munday", pos); + assertEquals(pos.getErrorIndex(), 0); + } + + public void test_parse_noMatch2() throws Exception { + ParsePosition pos = new ParsePosition(3); + DateTimeBuilder dtb = + getFormatter(DAY_OF_WEEK, TextStyle.FULL).parseToBuilder("Monday", pos); + assertEquals(pos.getErrorIndex(), 3); + } + + public void test_parse_noMatch_atEnd() throws Exception { + ParsePosition pos = new ParsePosition(6); + DateTimeBuilder dtb = + getFormatter(DAY_OF_WEEK, TextStyle.FULL).parseToBuilder("Monday", pos); + assertEquals(pos.getErrorIndex(), 6); + } + + //----------------------------------------------------------------------- + @DataProvider(name="parseText") + Object[][] provider_text() { + return new Object[][] { + {DAY_OF_WEEK, TextStyle.FULL, 1, "Monday"}, + {DAY_OF_WEEK, TextStyle.FULL, 2, "Tuesday"}, + {DAY_OF_WEEK, TextStyle.FULL, 3, "Wednesday"}, + {DAY_OF_WEEK, TextStyle.FULL, 4, "Thursday"}, + {DAY_OF_WEEK, TextStyle.FULL, 5, "Friday"}, + {DAY_OF_WEEK, TextStyle.FULL, 6, "Saturday"}, + {DAY_OF_WEEK, TextStyle.FULL, 7, "Sunday"}, + + {DAY_OF_WEEK, TextStyle.SHORT, 1, "Mon"}, + {DAY_OF_WEEK, TextStyle.SHORT, 2, "Tue"}, + {DAY_OF_WEEK, TextStyle.SHORT, 3, "Wed"}, + {DAY_OF_WEEK, TextStyle.SHORT, 4, "Thu"}, + {DAY_OF_WEEK, TextStyle.SHORT, 5, "Fri"}, + {DAY_OF_WEEK, TextStyle.SHORT, 6, "Sat"}, + {DAY_OF_WEEK, TextStyle.SHORT, 7, "Sun"}, + + {MONTH_OF_YEAR, TextStyle.FULL, 1, "January"}, + {MONTH_OF_YEAR, TextStyle.FULL, 12, "December"}, + + {MONTH_OF_YEAR, TextStyle.SHORT, 1, "Jan"}, + {MONTH_OF_YEAR, TextStyle.SHORT, 12, "Dec"}, + }; + } + + @DataProvider(name="parseNumber") + Object[][] provider_number() { + return new Object[][] { + {DAY_OF_MONTH, TextStyle.FULL, 1, "1"}, + {DAY_OF_MONTH, TextStyle.FULL, 2, "2"}, + {DAY_OF_MONTH, TextStyle.FULL, 30, "30"}, + {DAY_OF_MONTH, TextStyle.FULL, 31, "31"}, + + {DAY_OF_MONTH, TextStyle.SHORT, 1, "1"}, + {DAY_OF_MONTH, TextStyle.SHORT, 2, "2"}, + {DAY_OF_MONTH, TextStyle.SHORT, 30, "30"}, + {DAY_OF_MONTH, TextStyle.SHORT, 31, "31"}, + }; + } + + @Test(dataProvider="parseText") + public void test_parseText(TemporalField field, TextStyle style, int value, String input) throws Exception { + ParsePosition pos = new ParsePosition(0); + assertEquals(getFormatter(field, style).parseToBuilder(input, pos).getLong(field), (long) value); + assertEquals(pos.getIndex(), input.length()); + } + + @Test(dataProvider="parseNumber") + public void test_parseNumber(TemporalField field, TextStyle style, int value, String input) throws Exception { + ParsePosition pos = new ParsePosition(0); + assertEquals(getFormatter(field, style).parseToBuilder(input, pos).getLong(field), (long) value); + assertEquals(pos.getIndex(), input.length()); + } + + //----------------------------------------------------------------------- + @Test(dataProvider="parseText") + public void test_parse_strict_caseSensitive_parseUpper(TemporalField field, TextStyle style, int value, String input) throws Exception { + setCaseSensitive(true); + ParsePosition pos = new ParsePosition(0); + getFormatter(field, style).parseToBuilder(input.toUpperCase(), pos); + assertEquals(pos.getErrorIndex(), 0); + } + + @Test(dataProvider="parseText") + public void test_parse_strict_caseInsensitive_parseUpper(TemporalField field, TextStyle style, int value, String input) throws Exception { + setCaseSensitive(false); + ParsePosition pos = new ParsePosition(0); + assertEquals(getFormatter(field, style).parseToBuilder(input.toUpperCase(), pos).getLong(field), (long) value); + assertEquals(pos.getIndex(), input.length()); + } + + //----------------------------------------------------------------------- + @Test(dataProvider="parseText") + public void test_parse_strict_caseSensitive_parseLower(TemporalField field, TextStyle style, int value, String input) throws Exception { + setCaseSensitive(true); + ParsePosition pos = new ParsePosition(0); + getFormatter(field, style).parseToBuilder(input.toLowerCase(), pos); + assertEquals(pos.getErrorIndex(), 0); + } + + @Test(dataProvider="parseText") + public void test_parse_strict_caseInsensitive_parseLower(TemporalField field, TextStyle style, int value, String input) throws Exception { + setCaseSensitive(false); + ParsePosition pos = new ParsePosition(0); + assertEquals(getFormatter(field, style).parseToBuilder(input.toLowerCase(), pos).getLong(field), (long) value); + assertEquals(pos.getIndex(), input.length()); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + public void test_parse_full_strict_full_match() throws Exception { + setStrict(true); + ParsePosition pos = new ParsePosition(0); + assertEquals(getFormatter(MONTH_OF_YEAR, TextStyle.FULL).parseToBuilder("January", pos).getLong(MONTH_OF_YEAR), 1L); + assertEquals(pos.getIndex(), 7); + } + + public void test_parse_full_strict_short_noMatch() throws Exception { + setStrict(true); + ParsePosition pos = new ParsePosition(0); + getFormatter(MONTH_OF_YEAR, TextStyle.FULL).parseToBuilder("Janua", pos); + assertEquals(pos.getErrorIndex(), 0); + } + + public void test_parse_full_strict_number_noMatch() throws Exception { + setStrict(true); + ParsePosition pos = new ParsePosition(0); + getFormatter(MONTH_OF_YEAR, TextStyle.FULL).parseToBuilder("1", pos); + assertEquals(pos.getErrorIndex(), 0); + } + + //----------------------------------------------------------------------- + public void test_parse_short_strict_full_match() throws Exception { + setStrict(true); + ParsePosition pos = new ParsePosition(0); + assertEquals(getFormatter(MONTH_OF_YEAR, TextStyle.SHORT).parseToBuilder("January", pos).getLong(MONTH_OF_YEAR), 1L); + assertEquals(pos.getIndex(), 3); + } + + public void test_parse_short_strict_short_match() throws Exception { + setStrict(true); + ParsePosition pos = new ParsePosition(0); + assertEquals(getFormatter(MONTH_OF_YEAR, TextStyle.SHORT).parseToBuilder("Janua", pos).getLong(MONTH_OF_YEAR), 1L); + assertEquals(pos.getIndex(), 3); + } + + public void test_parse_short_strict_number_noMatch() throws Exception { + setStrict(true); + ParsePosition pos = new ParsePosition(0); + getFormatter(MONTH_OF_YEAR, TextStyle.SHORT).parseToBuilder("1", pos); + assertEquals(pos.getErrorIndex(), 0); + } + + //----------------------------------------------------------------------- + public void test_parse_french_short_strict_full_noMatch() throws Exception { + setStrict(true); + ParsePosition pos = new ParsePosition(0); + getFormatter(MONTH_OF_YEAR, TextStyle.SHORT).withLocale(Locale.FRENCH) + .parseToBuilder("janvier", pos); + assertEquals(pos.getErrorIndex(), 0); + } + + public void test_parse_french_short_strict_short_match() throws Exception { + setStrict(true); + ParsePosition pos = new ParsePosition(0); + assertEquals(getFormatter(MONTH_OF_YEAR, TextStyle.SHORT).withLocale(Locale.FRENCH) + .parseToBuilder("janv.", pos) + .getLong(MONTH_OF_YEAR), + 1L); + assertEquals(pos.getIndex(), 5); + } + + //----------------------------------------------------------------------- + public void test_parse_full_lenient_full_match() throws Exception { + setStrict(false); + ParsePosition pos = new ParsePosition(0); + assertEquals(getFormatter(MONTH_OF_YEAR, TextStyle.FULL).parseToBuilder("January.", pos).getLong(MONTH_OF_YEAR), 1L); + assertEquals(pos.getIndex(), 7); + } + + public void test_parse_full_lenient_short_match() throws Exception { + setStrict(false); + ParsePosition pos = new ParsePosition(0); + assertEquals(getFormatter(MONTH_OF_YEAR, TextStyle.FULL).parseToBuilder("Janua", pos).getLong(MONTH_OF_YEAR), 1L); + assertEquals(pos.getIndex(), 3); + } + + public void test_parse_full_lenient_number_match() throws Exception { + setStrict(false); + ParsePosition pos = new ParsePosition(0); + assertEquals(getFormatter(MONTH_OF_YEAR, TextStyle.FULL).parseToBuilder("1", pos).getLong(MONTH_OF_YEAR), 1L); + assertEquals(pos.getIndex(), 1); + } + + //----------------------------------------------------------------------- + public void test_parse_short_lenient_full_match() throws Exception { + setStrict(false); + ParsePosition pos = new ParsePosition(0); + assertEquals(getFormatter(MONTH_OF_YEAR, TextStyle.SHORT).parseToBuilder("January", pos).getLong(MONTH_OF_YEAR), 1L); + assertEquals(pos.getIndex(), 7); + } + + public void test_parse_short_lenient_short_match() throws Exception { + setStrict(false); + ParsePosition pos = new ParsePosition(0); + assertEquals(getFormatter(MONTH_OF_YEAR, TextStyle.SHORT).parseToBuilder("Janua", pos).getLong(MONTH_OF_YEAR), 1L); + assertEquals(pos.getIndex(), 3); + } + + public void test_parse_short_lenient_number_match() throws Exception { + setStrict(false); + ParsePosition pos = new ParsePosition(0); + assertEquals(getFormatter(MONTH_OF_YEAR, TextStyle.SHORT).parseToBuilder("1", pos).getLong(MONTH_OF_YEAR), 1L); + assertEquals(pos.getIndex(), 1); + } + +} diff --git a/jdk/test/java/time/test/java/time/format/TestTextPrinter.java b/jdk/test/java/time/test/java/time/format/TestTextPrinter.java new file mode 100644 index 00000000000..cc93f461a78 --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/TestTextPrinter.java @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.format; + +import java.time.format.*; + +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.DAY_OF_WEEK; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static org.testng.Assert.assertEquals; + +import java.util.Locale; + +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.temporal.TemporalField; +import test.java.time.temporal.MockFieldValue; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test TextPrinterParser. + */ +@Test(groups={"implementation"}) +public class TestTextPrinter extends AbstractTestPrinterParser { + + //----------------------------------------------------------------------- + @Test(expectedExceptions=DateTimeException.class) + public void test_print_emptyCalendrical() throws Exception { + getFormatter(DAY_OF_WEEK, TextStyle.FULL).printTo(EMPTY_DTA, buf); + } + + public void test_print_append() throws Exception { + buf.append("EXISTING"); + getFormatter(DAY_OF_WEEK, TextStyle.FULL).printTo(LocalDate.of(2012, 4, 18), buf); + assertEquals(buf.toString(), "EXISTINGWednesday"); + } + + //----------------------------------------------------------------------- + @DataProvider(name="print") + Object[][] provider_dow() { + return new Object[][] { + {DAY_OF_WEEK, TextStyle.FULL, 1, "Monday"}, + {DAY_OF_WEEK, TextStyle.FULL, 2, "Tuesday"}, + {DAY_OF_WEEK, TextStyle.FULL, 3, "Wednesday"}, + {DAY_OF_WEEK, TextStyle.FULL, 4, "Thursday"}, + {DAY_OF_WEEK, TextStyle.FULL, 5, "Friday"}, + {DAY_OF_WEEK, TextStyle.FULL, 6, "Saturday"}, + {DAY_OF_WEEK, TextStyle.FULL, 7, "Sunday"}, + + {DAY_OF_WEEK, TextStyle.SHORT, 1, "Mon"}, + {DAY_OF_WEEK, TextStyle.SHORT, 2, "Tue"}, + {DAY_OF_WEEK, TextStyle.SHORT, 3, "Wed"}, + {DAY_OF_WEEK, TextStyle.SHORT, 4, "Thu"}, + {DAY_OF_WEEK, TextStyle.SHORT, 5, "Fri"}, + {DAY_OF_WEEK, TextStyle.SHORT, 6, "Sat"}, + {DAY_OF_WEEK, TextStyle.SHORT, 7, "Sun"}, + + {DAY_OF_WEEK, TextStyle.NARROW, 1, "M"}, + {DAY_OF_WEEK, TextStyle.NARROW, 2, "T"}, + {DAY_OF_WEEK, TextStyle.NARROW, 3, "W"}, + {DAY_OF_WEEK, TextStyle.NARROW, 4, "T"}, + {DAY_OF_WEEK, TextStyle.NARROW, 5, "F"}, + {DAY_OF_WEEK, TextStyle.NARROW, 6, "S"}, + {DAY_OF_WEEK, TextStyle.NARROW, 7, "S"}, + + {DAY_OF_MONTH, TextStyle.FULL, 1, "1"}, + {DAY_OF_MONTH, TextStyle.FULL, 2, "2"}, + {DAY_OF_MONTH, TextStyle.FULL, 3, "3"}, + {DAY_OF_MONTH, TextStyle.FULL, 28, "28"}, + {DAY_OF_MONTH, TextStyle.FULL, 29, "29"}, + {DAY_OF_MONTH, TextStyle.FULL, 30, "30"}, + {DAY_OF_MONTH, TextStyle.FULL, 31, "31"}, + + {DAY_OF_MONTH, TextStyle.SHORT, 1, "1"}, + {DAY_OF_MONTH, TextStyle.SHORT, 2, "2"}, + {DAY_OF_MONTH, TextStyle.SHORT, 3, "3"}, + {DAY_OF_MONTH, TextStyle.SHORT, 28, "28"}, + {DAY_OF_MONTH, TextStyle.SHORT, 29, "29"}, + {DAY_OF_MONTH, TextStyle.SHORT, 30, "30"}, + {DAY_OF_MONTH, TextStyle.SHORT, 31, "31"}, + + {MONTH_OF_YEAR, TextStyle.FULL, 1, "January"}, + {MONTH_OF_YEAR, TextStyle.FULL, 2, "February"}, + {MONTH_OF_YEAR, TextStyle.FULL, 3, "March"}, + {MONTH_OF_YEAR, TextStyle.FULL, 4, "April"}, + {MONTH_OF_YEAR, TextStyle.FULL, 5, "May"}, + {MONTH_OF_YEAR, TextStyle.FULL, 6, "June"}, + {MONTH_OF_YEAR, TextStyle.FULL, 7, "July"}, + {MONTH_OF_YEAR, TextStyle.FULL, 8, "August"}, + {MONTH_OF_YEAR, TextStyle.FULL, 9, "September"}, + {MONTH_OF_YEAR, TextStyle.FULL, 10, "October"}, + {MONTH_OF_YEAR, TextStyle.FULL, 11, "November"}, + {MONTH_OF_YEAR, TextStyle.FULL, 12, "December"}, + + {MONTH_OF_YEAR, TextStyle.SHORT, 1, "Jan"}, + {MONTH_OF_YEAR, TextStyle.SHORT, 2, "Feb"}, + {MONTH_OF_YEAR, TextStyle.SHORT, 3, "Mar"}, + {MONTH_OF_YEAR, TextStyle.SHORT, 4, "Apr"}, + {MONTH_OF_YEAR, TextStyle.SHORT, 5, "May"}, + {MONTH_OF_YEAR, TextStyle.SHORT, 6, "Jun"}, + {MONTH_OF_YEAR, TextStyle.SHORT, 7, "Jul"}, + {MONTH_OF_YEAR, TextStyle.SHORT, 8, "Aug"}, + {MONTH_OF_YEAR, TextStyle.SHORT, 9, "Sep"}, + {MONTH_OF_YEAR, TextStyle.SHORT, 10, "Oct"}, + {MONTH_OF_YEAR, TextStyle.SHORT, 11, "Nov"}, + {MONTH_OF_YEAR, TextStyle.SHORT, 12, "Dec"}, + }; + } + + @Test(dataProvider="print") + public void test_print(TemporalField field, TextStyle style, int value, String expected) throws Exception { + getFormatter(field, style).printTo(new MockFieldValue(field, value), buf); + assertEquals(buf.toString(), expected); + } + + //----------------------------------------------------------------------- + public void test_print_french_long() throws Exception { + getFormatter(MONTH_OF_YEAR, TextStyle.FULL).withLocale(Locale.FRENCH).printTo(LocalDate.of(2012, 1, 1), buf); + assertEquals(buf.toString(), "janvier"); + } + + public void test_print_french_short() throws Exception { + getFormatter(MONTH_OF_YEAR, TextStyle.SHORT).withLocale(Locale.FRENCH).printTo(LocalDate.of(2012, 1, 1), buf); + assertEquals(buf.toString(), "janv."); + } + + //----------------------------------------------------------------------- + public void test_toString1() throws Exception { + assertEquals(getFormatter(MONTH_OF_YEAR, TextStyle.FULL).toString(), "Text(MonthOfYear)"); + } + + public void test_toString2() throws Exception { + assertEquals(getFormatter(MONTH_OF_YEAR, TextStyle.SHORT).toString(), "Text(MonthOfYear,SHORT)"); + } + +} diff --git a/jdk/test/java/time/test/java/time/format/TestZoneIdParser.java b/jdk/test/java/time/test/java/time/format/TestZoneIdParser.java new file mode 100644 index 00000000000..99ee93bb52e --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/TestZoneIdParser.java @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2009-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.format; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.util.Set; + +import java.text.ParsePosition; +import java.time.ZoneId; +import java.time.format.DateTimeBuilder; +import java.time.format.DateTimeFormatter; +import java.time.format.TextStyle; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.Queries; +import java.time.zone.ZoneRulesProvider; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test ZonePrinterParser. + */ +@Test(groups={"implementation"}) +public class TestZoneIdParser extends AbstractTestPrinterParser { + + private static final String AMERICA_DENVER = "America/Denver"; + private static final ZoneId TIME_ZONE_DENVER = ZoneId.of(AMERICA_DENVER); + + private DateTimeFormatter getFormatter0(TextStyle style) { + if (style == null) + return builder.appendZoneId().toFormatter(locale).withSymbols(symbols); + return builder.appendZoneText(style).toFormatter(locale).withSymbols(symbols); + } + + //----------------------------------------------------------------------- + @DataProvider(name="error") + Object[][] data_error() { + return new Object[][] { + {null, "hello", -1, IndexOutOfBoundsException.class}, + {null, "hello", 6, IndexOutOfBoundsException.class}, + }; + } + + @Test(dataProvider="error") + public void test_parse_error(TextStyle style, String text, int pos, Class expected) { + try { + getFormatter0(style).parseToBuilder(text, new ParsePosition(pos)); + assertTrue(false); + } catch (RuntimeException ex) { + assertTrue(expected.isInstance(ex)); + } + } + + //----------------------------------------------------------------------- + public void test_parse_exactMatch_Denver() throws Exception { + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder dtb = getFormatter0(null).parseToBuilder(AMERICA_DENVER, pos); + assertEquals(pos.getIndex(), AMERICA_DENVER.length()); + assertParsed(dtb, TIME_ZONE_DENVER); + } + + public void test_parse_startStringMatch_Denver() throws Exception { + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder dtb = getFormatter0(null).parseToBuilder(AMERICA_DENVER + "OTHER", pos); + assertEquals(pos.getIndex(), AMERICA_DENVER.length()); + assertParsed(dtb, TIME_ZONE_DENVER); + } + + public void test_parse_midStringMatch_Denver() throws Exception { + ParsePosition pos = new ParsePosition(5); + DateTimeBuilder dtb = getFormatter0(null).parseToBuilder("OTHER" + AMERICA_DENVER + "OTHER", pos); + assertEquals(pos.getIndex(), 5 + AMERICA_DENVER.length()); + assertParsed(dtb, TIME_ZONE_DENVER); + } + + public void test_parse_endStringMatch_Denver() throws Exception { + ParsePosition pos = new ParsePosition(5); + DateTimeBuilder dtb = getFormatter0(null).parseToBuilder("OTHER" + AMERICA_DENVER, pos); + assertEquals(pos.getIndex(), 5 + AMERICA_DENVER.length()); + assertParsed(dtb, TIME_ZONE_DENVER); + } + + public void test_parse_partialMatch() throws Exception { + ParsePosition pos = new ParsePosition(5); + DateTimeBuilder dtb = getFormatter0(null).parseToBuilder("OTHERAmerica/Bogusville", pos); + assertEquals(pos.getErrorIndex(), 5); // TBD: -6 ? + assertEquals(dtb, null); + } + + //----------------------------------------------------------------------- + @DataProvider(name="zones") + Object[][] populateTestData() { + Set ids = ZoneRulesProvider.getAvailableZoneIds(); + Object[][] rtnval = new Object[ids.size()][]; + int i = 0; + for (String id : ids) { + rtnval[i++] = new Object[] { id, ZoneId.of(id) }; + } + return rtnval; + } + + @Test(dataProvider="zones") + public void test_parse_exactMatch(String parse, ZoneId expected) throws Exception { + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder dtb = getFormatter0(null).parseToBuilder(parse, pos); + assertEquals(pos.getIndex(), parse.length()); + assertParsed(dtb, expected); + } + + @Test(dataProvider="zones") + public void test_parse_startMatch(String parse, ZoneId expected) throws Exception { + String append = " OTHER"; + parse += append; + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder dtb = getFormatter0(null).parseToBuilder(parse, pos); + assertEquals(pos.getIndex(), parse.length() - append.length()); + assertParsed(dtb, expected); + } + + //----------------------------------------------------------------------- + public void test_parse_caseInsensitive() throws Exception { + DateTimeFormatter fmt = new DateTimeFormatterBuilder().appendZoneId().toFormatter(); + DateTimeFormatter fmtCI = new DateTimeFormatterBuilder().parseCaseInsensitive() + .appendZoneId() + .toFormatter(); + for (String zidStr : ZoneRulesProvider.getAvailableZoneIds()) { + ZoneId zid = ZoneId.of(zidStr); + assertEquals(fmt.parse(zidStr, Queries.zoneId()), zid); + assertEquals(fmtCI.parse(zidStr.toLowerCase(), Queries.zoneId()), zid); + assertEquals(fmtCI.parse(zidStr.toUpperCase(), Queries.zoneId()), zid); + ParsePosition pos = new ParsePosition(5); + assertEquals(fmtCI.parseToBuilder("OTHER" + zidStr.toLowerCase() + "OTHER", pos) + .query(Queries.zoneId()), zid); + assertEquals(pos.getIndex(), zidStr.length() + 5); + pos = new ParsePosition(5); + assertEquals(fmtCI.parseToBuilder("OTHER" + zidStr.toUpperCase() + "OTHER", pos) + .query(Queries.zoneId()), zid); + assertEquals(pos.getIndex(), zidStr.length() + 5); + } + } + + //----------------------------------------------------------------------- + /* + public void test_parse_endStringMatch_utc() throws Exception { + ParsePosition pos = new ParsePosition(5); + DateTimeBuilder dtb = getFormatter0(null).parseToBuilder("OTHERUTC", pos); + assertEquals(pos.getIndex(), 8); + assertParsed(dtb, ZoneOffset.UTC); + } + + public void test_parse_endStringMatch_utc_plus1() throws Exception { + ParsePosition pos = new ParsePosition(5); + DateTimeBuilder dtb = getFormatter0(null).parseToBuilder("OTHERUTC+01:00", pos); + assertEquals(pos.getIndex(), 14); + assertParsed(dtb, ZoneId.of("UTC+01:00")); + } + + //----------------------------------------------------------------------- + public void test_parse_midStringMatch_utc() throws Exception { + ParsePosition pos = new ParsePosition(5); + DateTimeBuilder dtb = getFormatter0(null).parseToBuilder("OTHERUTCOTHER", pos); + assertEquals(pos.getIndex(), 8); + assertParsed(dtb, ZoneOffset.UTC); + } + + public void test_parse_midStringMatch_utc_plus1() throws Exception { + ParsePosition pos = new ParsePosition(5); + DateTimeBuilder dtb = getFormatter0(null).parseToBuilder("OTHERUTC+01:00OTHER", pos); + assertEquals(pos.getIndex(), 14); + assertParsed(dtb, ZoneId.of("UTC+01:00")); + } + */ + //----------------------------------------------------------------------- + public void test_toString_id() { + assertEquals(getFormatter0(null).toString(), "ZoneId()"); + } + + public void test_toString_text() { + assertEquals(getFormatter0(TextStyle.FULL).toString(), "ZoneText(FULL)"); + } + + private void assertParsed(DateTimeBuilder dtb, ZoneId expectedZone) { + assertEquals(dtb.query(ZoneId::from), expectedZone); + } + +} diff --git a/jdk/test/java/time/test/java/time/format/TestZoneOffsetParser.java b/jdk/test/java/time/test/java/time/format/TestZoneOffsetParser.java new file mode 100644 index 00000000000..300411cb2eb --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/TestZoneOffsetParser.java @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.format; + +import static java.time.temporal.ChronoField.OFFSET_SECONDS; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.text.ParsePosition; +import java.time.ZoneOffset; +import java.time.format.DateTimeBuilder; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test ZoneOffsetPrinterParser. + */ +@Test(groups={"implementation"}) +public class TestZoneOffsetParser extends AbstractTestPrinterParser { + + //----------------------------------------------------------------------- + @DataProvider(name="error") + Object[][] data_error() { + return new Object[][] { + {"+HH:MM:ss", "Z", "hello", -1, IndexOutOfBoundsException.class}, + {"+HH:MM:ss", "Z", "hello", 6, IndexOutOfBoundsException.class}, + }; + } + + @Test(dataProvider="error") + public void test_parse_error(String pattern, String noOffsetText, String text, int pos, Class expected) { + try { + getFormatter(pattern, noOffsetText).parseToBuilder(text, new ParsePosition(pos)); + } catch (RuntimeException ex) { + assertTrue(expected.isInstance(ex)); + } + } + + //----------------------------------------------------------------------- + public void test_parse_exactMatch_UTC() throws Exception { + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder dtb = getFormatter("+HH:MM:ss", "Z").parseToBuilder("Z", pos); + assertEquals(pos.getIndex(), 1); + assertParsed(dtb, ZoneOffset.UTC); + } + + public void test_parse_startStringMatch_UTC() throws Exception { + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder dtb = getFormatter("+HH:MM:ss", "Z").parseToBuilder("ZOTHER", pos); + assertEquals(pos.getIndex(), 1); + assertParsed(dtb, ZoneOffset.UTC); + } + + public void test_parse_midStringMatch_UTC() throws Exception { + ParsePosition pos = new ParsePosition(5); + DateTimeBuilder dtb = getFormatter("+HH:MM:ss", "Z").parseToBuilder("OTHERZOTHER", pos); + assertEquals(pos.getIndex(), 6); + assertParsed(dtb, ZoneOffset.UTC); + } + + public void test_parse_endStringMatch_UTC() throws Exception { + ParsePosition pos = new ParsePosition(5); + DateTimeBuilder dtb = getFormatter("+HH:MM:ss", "Z").parseToBuilder("OTHERZ", pos); + assertEquals(pos.getIndex(), 6); + assertParsed(dtb, ZoneOffset.UTC); + } + + //----------------------------------------------------------------------- + public void test_parse_exactMatch_UTC_EmptyUTC() throws Exception { + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder dtb = getFormatter("+HH:MM:ss", "").parseToBuilder("", pos); + assertEquals(pos.getIndex(), 0); + assertParsed(dtb, ZoneOffset.UTC); + } + + public void test_parse_startStringMatch_UTC_EmptyUTC() throws Exception { + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder dtb = getFormatter("+HH:MM:ss", "").parseToBuilder("OTHER", pos); + assertEquals(pos.getIndex(), 0); + assertParsed(dtb, ZoneOffset.UTC); + } + + public void test_parse_midStringMatch_UTC_EmptyUTC() throws Exception { + ParsePosition pos = new ParsePosition(5); + DateTimeBuilder dtb = getFormatter("+HH:MM:ss", "").parseToBuilder("OTHEROTHER", pos); + assertEquals(pos.getIndex(), 5); + assertParsed(dtb, ZoneOffset.UTC); + } + + public void test_parse_endStringMatch_UTC_EmptyUTC() throws Exception { + ParsePosition pos = new ParsePosition(5); + DateTimeBuilder dtb = getFormatter("+HH:MM:ss", "").parseToBuilder("OTHER", pos); + assertEquals(pos.getIndex(), 5); + assertParsed(dtb, ZoneOffset.UTC); + } + + //----------------------------------------------------------------------- + @DataProvider(name="offsets") + Object[][] provider_offsets() { + return new Object[][] { + {"+HH", "+00", ZoneOffset.UTC}, + {"+HH", "-00", ZoneOffset.UTC}, + {"+HH", "+01", ZoneOffset.ofHours(1)}, + {"+HH", "-01", ZoneOffset.ofHours(-1)}, + + {"+HHMM", "+0000", ZoneOffset.UTC}, + {"+HHMM", "-0000", ZoneOffset.UTC}, + {"+HHMM", "+0102", ZoneOffset.ofHoursMinutes(1, 2)}, + {"+HHMM", "-0102", ZoneOffset.ofHoursMinutes(-1, -2)}, + + {"+HH:MM", "+00:00", ZoneOffset.UTC}, + {"+HH:MM", "-00:00", ZoneOffset.UTC}, + {"+HH:MM", "+01:02", ZoneOffset.ofHoursMinutes(1, 2)}, + {"+HH:MM", "-01:02", ZoneOffset.ofHoursMinutes(-1, -2)}, + + {"+HHMMss", "+0000", ZoneOffset.UTC}, + {"+HHMMss", "-0000", ZoneOffset.UTC}, + {"+HHMMss", "+0100", ZoneOffset.ofHoursMinutesSeconds(1, 0, 0)}, + {"+HHMMss", "+0159", ZoneOffset.ofHoursMinutesSeconds(1, 59, 0)}, + {"+HHMMss", "+0200", ZoneOffset.ofHoursMinutesSeconds(2, 0, 0)}, + {"+HHMMss", "+1800", ZoneOffset.ofHoursMinutesSeconds(18, 0, 0)}, + {"+HHMMss", "+010215", ZoneOffset.ofHoursMinutesSeconds(1, 2, 15)}, + {"+HHMMss", "-0100", ZoneOffset.ofHoursMinutesSeconds(-1, 0, 0)}, + {"+HHMMss", "-0200", ZoneOffset.ofHoursMinutesSeconds(-2, 0, 0)}, + {"+HHMMss", "-1800", ZoneOffset.ofHoursMinutesSeconds(-18, 0, 0)}, + + {"+HHMMss", "+000000", ZoneOffset.UTC}, + {"+HHMMss", "-000000", ZoneOffset.UTC}, + {"+HHMMss", "+010000", ZoneOffset.ofHoursMinutesSeconds(1, 0, 0)}, + {"+HHMMss", "+010203", ZoneOffset.ofHoursMinutesSeconds(1, 2, 3)}, + {"+HHMMss", "+015959", ZoneOffset.ofHoursMinutesSeconds(1, 59, 59)}, + {"+HHMMss", "+020000", ZoneOffset.ofHoursMinutesSeconds(2, 0, 0)}, + {"+HHMMss", "+180000", ZoneOffset.ofHoursMinutesSeconds(18, 0, 0)}, + {"+HHMMss", "-010000", ZoneOffset.ofHoursMinutesSeconds(-1, 0, 0)}, + {"+HHMMss", "-020000", ZoneOffset.ofHoursMinutesSeconds(-2, 0, 0)}, + {"+HHMMss", "-180000", ZoneOffset.ofHoursMinutesSeconds(-18, 0, 0)}, + + {"+HH:MM:ss", "+00:00", ZoneOffset.UTC}, + {"+HH:MM:ss", "-00:00", ZoneOffset.UTC}, + {"+HH:MM:ss", "+01:00", ZoneOffset.ofHoursMinutesSeconds(1, 0, 0)}, + {"+HH:MM:ss", "+01:02", ZoneOffset.ofHoursMinutesSeconds(1, 2, 0)}, + {"+HH:MM:ss", "+01:59", ZoneOffset.ofHoursMinutesSeconds(1, 59, 0)}, + {"+HH:MM:ss", "+02:00", ZoneOffset.ofHoursMinutesSeconds(2, 0, 0)}, + {"+HH:MM:ss", "+18:00", ZoneOffset.ofHoursMinutesSeconds(18, 0, 0)}, + {"+HH:MM:ss", "+01:02:15", ZoneOffset.ofHoursMinutesSeconds(1, 2, 15)}, + {"+HH:MM:ss", "-01:00", ZoneOffset.ofHoursMinutesSeconds(-1, 0, 0)}, + {"+HH:MM:ss", "-02:00", ZoneOffset.ofHoursMinutesSeconds(-2, 0, 0)}, + {"+HH:MM:ss", "-18:00", ZoneOffset.ofHoursMinutesSeconds(-18, 0, 0)}, + + {"+HH:MM:ss", "+00:00:00", ZoneOffset.UTC}, + {"+HH:MM:ss", "-00:00:00", ZoneOffset.UTC}, + {"+HH:MM:ss", "+01:00:00", ZoneOffset.ofHoursMinutesSeconds(1, 0, 0)}, + {"+HH:MM:ss", "+01:02:03", ZoneOffset.ofHoursMinutesSeconds(1, 2, 3)}, + {"+HH:MM:ss", "+01:59:59", ZoneOffset.ofHoursMinutesSeconds(1, 59, 59)}, + {"+HH:MM:ss", "+02:00:00", ZoneOffset.ofHoursMinutesSeconds(2, 0, 0)}, + {"+HH:MM:ss", "+18:00:00", ZoneOffset.ofHoursMinutesSeconds(18, 0, 0)}, + {"+HH:MM:ss", "-01:00:00", ZoneOffset.ofHoursMinutesSeconds(-1, 0, 0)}, + {"+HH:MM:ss", "-02:00:00", ZoneOffset.ofHoursMinutesSeconds(-2, 0, 0)}, + {"+HH:MM:ss", "-18:00:00", ZoneOffset.ofHoursMinutesSeconds(-18, 0, 0)}, + + {"+HHMMSS", "+000000", ZoneOffset.UTC}, + {"+HHMMSS", "-000000", ZoneOffset.UTC}, + {"+HHMMSS", "+010203", ZoneOffset.ofHoursMinutesSeconds(1, 2, 3)}, + {"+HHMMSS", "-010203", ZoneOffset.ofHoursMinutesSeconds(-1, -2, -3)}, + + {"+HH:MM:SS", "+00:00:00", ZoneOffset.UTC}, + {"+HH:MM:SS", "-00:00:00", ZoneOffset.UTC}, + {"+HH:MM:SS", "+01:02:03", ZoneOffset.ofHoursMinutesSeconds(1, 2, 3)}, + {"+HH:MM:SS", "-01:02:03", ZoneOffset.ofHoursMinutesSeconds(-1, -2, -3)}, + }; + } + + @Test(dataProvider="offsets") + public void test_parse_exactMatch(String pattern, String parse, ZoneOffset expected) throws Exception { + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder dtb = getFormatter(pattern, "Z").parseToBuilder(parse, pos); + assertEquals(pos.getIndex(), parse.length()); + assertParsed(dtb, expected); + } + + @Test(dataProvider="offsets") + public void test_parse_startStringMatch(String pattern, String parse, ZoneOffset expected) throws Exception { + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder dtb = getFormatter(pattern, "Z").parseToBuilder(parse + ":OTHER", pos); + assertEquals(pos.getIndex(), parse.length()); + assertParsed(dtb, expected); + } + + @Test(dataProvider="offsets") + public void test_parse_midStringMatch(String pattern, String parse, ZoneOffset expected) throws Exception { + ParsePosition pos = new ParsePosition(5); + DateTimeBuilder dtb = getFormatter(pattern, "Z").parseToBuilder("OTHER" + parse + ":OTHER", pos); + assertEquals(pos.getIndex(), parse.length() + 5); + assertParsed(dtb, expected); + } + + @Test(dataProvider="offsets") + public void test_parse_endStringMatch(String pattern, String parse, ZoneOffset expected) throws Exception { + ParsePosition pos = new ParsePosition(5); + DateTimeBuilder dtb = getFormatter(pattern, "Z").parseToBuilder("OTHER" + parse, pos); + assertEquals(pos.getIndex(), parse.length() + 5); + assertParsed(dtb, expected); + } + + @Test(dataProvider="offsets") + public void test_parse_exactMatch_EmptyUTC(String pattern, String parse, ZoneOffset expected) throws Exception { + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder dtb = getFormatter(pattern, "").parseToBuilder(parse, pos); + assertEquals(pos.getIndex(), parse.length()); + assertParsed(dtb, expected); + } + + @Test(dataProvider="offsets") + public void test_parse_startStringMatch_EmptyUTC(String pattern, String parse, ZoneOffset expected) throws Exception { + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder dtb = getFormatter(pattern, "").parseToBuilder(parse + ":OTHER", pos); + assertEquals(pos.getIndex(), parse.length()); + assertParsed(dtb, expected); + } + + @Test(dataProvider="offsets") + public void test_parse_midStringMatch_EmptyUTC(String pattern, String parse, ZoneOffset expected) throws Exception { + ParsePosition pos = new ParsePosition(5); + DateTimeBuilder dtb = getFormatter(pattern, "").parseToBuilder("OTHER" + parse + ":OTHER", pos); + assertEquals(pos.getIndex(), parse.length() + 5); + assertParsed(dtb, expected); + } + + @Test(dataProvider="offsets") + public void test_parse_endStringMatch_EmptyUTC(String pattern, String parse, ZoneOffset expected) throws Exception { + ParsePosition pos = new ParsePosition(5); + DateTimeBuilder dtb = getFormatter(pattern, "").parseToBuilder("OTHER" + parse, pos); + assertEquals(pos.getIndex(), parse.length() + 5); + assertParsed(dtb, expected); + } + + //----------------------------------------------------------------------- + @DataProvider(name="bigOffsets") + Object[][] provider_bigOffsets() { + return new Object[][] { + {"+HH", "+59", 59 * 3600}, + {"+HH", "-19", -(19 * 3600)}, + + {"+HHMM", "+1801", 18 * 3600 + 1 * 60}, + {"+HHMM", "-1801", -(18 * 3600 + 1 * 60)}, + + {"+HH:MM", "+18:01", 18 * 3600 + 1 * 60}, + {"+HH:MM", "-18:01", -(18 * 3600 + 1 * 60)}, + + {"+HHMMss", "+180103", 18 * 3600 + 1 * 60 + 3}, + {"+HHMMss", "-180103", -(18 * 3600 + 1 * 60 + 3)}, + + {"+HH:MM:ss", "+18:01:03", 18 * 3600 + 1 * 60 + 3}, + {"+HH:MM:ss", "-18:01:03", -(18 * 3600 + 1 * 60 + 3)}, + + {"+HHMMSS", "+180103", 18 * 3600 + 1 * 60 + 3}, + {"+HHMMSS", "-180103", -(18 * 3600 + 1 * 60 + 3)}, + + {"+HH:MM:SS", "+18:01:03", 18 * 3600 + 1 * 60 + 3}, + {"+HH:MM:SS", "-18:01:03", -(18 * 3600 + 1 * 60 + 3)}, + }; + } + + @Test(dataProvider="bigOffsets") + public void test_parse_bigOffsets(String pattern, String parse, long offsetSecs) throws Exception { + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder dtb = getFormatter(pattern, "").parseToBuilder(parse, pos); + assertEquals(pos.getIndex(), parse.length()); + assertEquals(dtb.getLong(OFFSET_SECONDS), offsetSecs); + } + + //----------------------------------------------------------------------- + @DataProvider(name="badOffsets") + Object[][] provider_badOffsets() { + return new Object[][] { + {"+HH", "+1", 0}, + {"+HH", "-1", 0}, + {"+HH", "01", 0}, + {"+HH", "01", 0}, + {"+HH", "+AA", 0}, + + {"+HHMM", "+1", 0}, + {"+HHMM", "+01", 0}, + {"+HHMM", "+001", 0}, + {"+HHMM", "0102", 0}, + {"+HHMM", "+01:02", 0}, + {"+HHMM", "+AAAA", 0}, + + {"+HH:MM", "+1", 0}, + {"+HH:MM", "+01", 0}, + {"+HH:MM", "+0:01", 0}, + {"+HH:MM", "+00:1", 0}, + {"+HH:MM", "+0:1", 0}, + {"+HH:MM", "+:", 0}, + {"+HH:MM", "01:02", 0}, + {"+HH:MM", "+0102", 0}, + {"+HH:MM", "+AA:AA", 0}, + + {"+HHMMss", "+1", 0}, + {"+HHMMss", "+01", 0}, + {"+HHMMss", "+001", 0}, + {"+HHMMss", "0102", 0}, + {"+HHMMss", "+01:02", 0}, + {"+HHMMss", "+AAAA", 0}, + + {"+HH:MM:ss", "+1", 0}, + {"+HH:MM:ss", "+01", 0}, + {"+HH:MM:ss", "+0:01", 0}, + {"+HH:MM:ss", "+00:1", 0}, + {"+HH:MM:ss", "+0:1", 0}, + {"+HH:MM:ss", "+:", 0}, + {"+HH:MM:ss", "01:02", 0}, + {"+HH:MM:ss", "+0102", 0}, + {"+HH:MM:ss", "+AA:AA", 0}, + + {"+HHMMSS", "+1", 0}, + {"+HHMMSS", "+01", 0}, + {"+HHMMSS", "+001", 0}, + {"+HHMMSS", "0102", 0}, + {"+HHMMSS", "+01:02", 0}, + {"+HHMMSS", "+AAAA", 0}, + + {"+HH:MM:SS", "+1", 0}, + {"+HH:MM:SS", "+01", 0}, + {"+HH:MM:SS", "+0:01", 0}, + {"+HH:MM:SS", "+00:1", 0}, + {"+HH:MM:SS", "+0:1", 0}, + {"+HH:MM:SS", "+:", 0}, + {"+HH:MM:SS", "01:02", 0}, + {"+HH:MM:SS", "+0102", 0}, + {"+HH:MM:SS", "+AA:AA", 0}, + }; + } + + @Test(dataProvider="badOffsets") + public void test_parse_invalid(String pattern, String parse, int expectedPosition) throws Exception { + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder dtb = getFormatter(pattern, "Z").parseToBuilder(parse, pos); + assertEquals(pos.getErrorIndex(), expectedPosition); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + public void test_parse_caseSensitiveUTC_matchedCase() throws Exception { + setCaseSensitive(true); + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder dtb = getFormatter("+HH:MM:ss", "Z").parseToBuilder("Z", pos); + assertEquals(pos.getIndex(), 1); + assertParsed(dtb, ZoneOffset.UTC); + } + + public void test_parse_caseSensitiveUTC_unmatchedCase() throws Exception { + setCaseSensitive(true); + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder dtb = getFormatter("+HH:MM:ss", "Z").parseToBuilder("z", pos); + assertEquals(pos.getErrorIndex(), 0); + assertEquals(dtb, null); + } + + public void test_parse_caseInsensitiveUTC_matchedCase() throws Exception { + setCaseSensitive(false); + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder dtb = getFormatter("+HH:MM:ss", "Z").parseToBuilder("Z", pos); + assertEquals(pos.getIndex(), 1); + assertParsed(dtb, ZoneOffset.UTC); + } + + public void test_parse_caseInsensitiveUTC_unmatchedCase() throws Exception { + setCaseSensitive(false); + ParsePosition pos = new ParsePosition(0); + DateTimeBuilder dtb = getFormatter("+HH:MM:ss", "Z").parseToBuilder("z", pos); + assertEquals(pos.getIndex(), 1); + assertParsed(dtb, ZoneOffset.UTC); + } + + private void assertParsed(DateTimeBuilder dtb, ZoneOffset expectedOffset) { + if (expectedOffset == null) { + assertEquals(dtb, null); + } else { + assertEquals(dtb.getFieldValueMap().size(), 1); + assertEquals(dtb.getLong(OFFSET_SECONDS), (long) expectedOffset.getTotalSeconds()); + } + } + +} diff --git a/jdk/test/java/time/test/java/time/format/TestZoneOffsetPrinter.java b/jdk/test/java/time/test/java/time/format/TestZoneOffsetPrinter.java new file mode 100644 index 00000000000..e4250900b05 --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/TestZoneOffsetPrinter.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.format; + +import static java.time.temporal.ChronoField.OFFSET_SECONDS; +import static org.testng.Assert.assertEquals; + +import java.time.DateTimeException; +import java.time.ZoneOffset; +import java.time.format.DateTimeBuilder; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test ZoneOffsetPrinterParser. + */ +@Test(groups={"implementation"}) +public class TestZoneOffsetPrinter extends AbstractTestPrinterParser { + + private static final ZoneOffset OFFSET_0130 = ZoneOffset.of("+01:30"); + + //----------------------------------------------------------------------- + @DataProvider(name="offsets") + Object[][] provider_offsets() { + return new Object[][] { + {"+HH", "NO-OFFSET", ZoneOffset.UTC}, + {"+HH", "+01", ZoneOffset.ofHours(1)}, + {"+HH", "-01", ZoneOffset.ofHours(-1)}, + + {"+HHMM", "NO-OFFSET", ZoneOffset.UTC}, + {"+HHMM", "+0102", ZoneOffset.ofHoursMinutes(1, 2)}, + {"+HHMM", "-0102", ZoneOffset.ofHoursMinutes(-1, -2)}, + + {"+HH:MM", "NO-OFFSET", ZoneOffset.UTC}, + {"+HH:MM", "+01:02", ZoneOffset.ofHoursMinutes(1, 2)}, + {"+HH:MM", "-01:02", ZoneOffset.ofHoursMinutes(-1, -2)}, + + {"+HHMMss", "NO-OFFSET", ZoneOffset.UTC}, + {"+HHMMss", "+0100", ZoneOffset.ofHoursMinutesSeconds(1, 0, 0)}, + {"+HHMMss", "+0102", ZoneOffset.ofHoursMinutesSeconds(1, 2, 0)}, + {"+HHMMss", "+0159", ZoneOffset.ofHoursMinutesSeconds(1, 59, 0)}, + {"+HHMMss", "+0200", ZoneOffset.ofHoursMinutesSeconds(2, 0, 0)}, + {"+HHMMss", "+1800", ZoneOffset.ofHoursMinutesSeconds(18, 0, 0)}, + {"+HHMMss", "+010215", ZoneOffset.ofHoursMinutesSeconds(1, 2, 15)}, + {"+HHMMss", "-0100", ZoneOffset.ofHoursMinutesSeconds(-1, 0, 0)}, + {"+HHMMss", "-0200", ZoneOffset.ofHoursMinutesSeconds(-2, 0, 0)}, + {"+HHMMss", "-1800", ZoneOffset.ofHoursMinutesSeconds(-18, 0, 0)}, + + {"+HHMMss", "NO-OFFSET", ZoneOffset.UTC}, + {"+HHMMss", "+0100", ZoneOffset.ofHoursMinutesSeconds(1, 0, 0)}, + {"+HHMMss", "+010203", ZoneOffset.ofHoursMinutesSeconds(1, 2, 3)}, + {"+HHMMss", "+015959", ZoneOffset.ofHoursMinutesSeconds(1, 59, 59)}, + {"+HHMMss", "+0200", ZoneOffset.ofHoursMinutesSeconds(2, 0, 0)}, + {"+HHMMss", "+1800", ZoneOffset.ofHoursMinutesSeconds(18, 0, 0)}, + {"+HHMMss", "-0100", ZoneOffset.ofHoursMinutesSeconds(-1, 0, 0)}, + {"+HHMMss", "-0200", ZoneOffset.ofHoursMinutesSeconds(-2, 0, 0)}, + {"+HHMMss", "-1800", ZoneOffset.ofHoursMinutesSeconds(-18, 0, 0)}, + + {"+HH:MM:ss", "NO-OFFSET", ZoneOffset.UTC}, + {"+HH:MM:ss", "+01:00", ZoneOffset.ofHoursMinutesSeconds(1, 0, 0)}, + {"+HH:MM:ss", "+01:02", ZoneOffset.ofHoursMinutesSeconds(1, 2, 0)}, + {"+HH:MM:ss", "+01:59", ZoneOffset.ofHoursMinutesSeconds(1, 59, 0)}, + {"+HH:MM:ss", "+02:00", ZoneOffset.ofHoursMinutesSeconds(2, 0, 0)}, + {"+HH:MM:ss", "+18:00", ZoneOffset.ofHoursMinutesSeconds(18, 0, 0)}, + {"+HH:MM:ss", "+01:02:15", ZoneOffset.ofHoursMinutesSeconds(1, 2, 15)}, + {"+HH:MM:ss", "-01:00", ZoneOffset.ofHoursMinutesSeconds(-1, 0, 0)}, + {"+HH:MM:ss", "-02:00", ZoneOffset.ofHoursMinutesSeconds(-2, 0, 0)}, + {"+HH:MM:ss", "-18:00", ZoneOffset.ofHoursMinutesSeconds(-18, 0, 0)}, + + {"+HH:MM:ss", "NO-OFFSET", ZoneOffset.UTC}, + {"+HH:MM:ss", "+01:00", ZoneOffset.ofHoursMinutesSeconds(1, 0, 0)}, + {"+HH:MM:ss", "+01:02:03", ZoneOffset.ofHoursMinutesSeconds(1, 2, 3)}, + {"+HH:MM:ss", "+01:59:59", ZoneOffset.ofHoursMinutesSeconds(1, 59, 59)}, + {"+HH:MM:ss", "+02:00", ZoneOffset.ofHoursMinutesSeconds(2, 0, 0)}, + {"+HH:MM:ss", "+18:00", ZoneOffset.ofHoursMinutesSeconds(18, 0, 0)}, + {"+HH:MM:ss", "-01:00", ZoneOffset.ofHoursMinutesSeconds(-1, 0, 0)}, + {"+HH:MM:ss", "-02:00", ZoneOffset.ofHoursMinutesSeconds(-2, 0, 0)}, + {"+HH:MM:ss", "-18:00", ZoneOffset.ofHoursMinutesSeconds(-18, 0, 0)}, + + {"+HHMMSS", "NO-OFFSET", ZoneOffset.UTC}, + {"+HHMMSS", "+010203", ZoneOffset.ofHoursMinutesSeconds(1, 2, 3)}, + {"+HHMMSS", "-010203", ZoneOffset.ofHoursMinutesSeconds(-1, -2, -3)}, + {"+HHMMSS", "+010200", ZoneOffset.ofHoursMinutesSeconds(1, 2, 0)}, + {"+HHMMSS", "-010200", ZoneOffset.ofHoursMinutesSeconds(-1, -2, 0)}, + + {"+HH:MM:SS", "NO-OFFSET", ZoneOffset.UTC}, + {"+HH:MM:SS", "+01:02:03", ZoneOffset.ofHoursMinutesSeconds(1, 2, 3)}, + {"+HH:MM:SS", "-01:02:03", ZoneOffset.ofHoursMinutesSeconds(-1, -2, -3)}, + {"+HH:MM:SS", "+01:02:00", ZoneOffset.ofHoursMinutesSeconds(1, 2, 0)}, + {"+HH:MM:SS", "-01:02:00", ZoneOffset.ofHoursMinutesSeconds(-1, -2, 0)}, + }; + } + + @Test(dataProvider="offsets") + public void test_print(String pattern, String expected, ZoneOffset offset) throws Exception { + buf.append("EXISTING"); + getFormatter(pattern, "NO-OFFSET").printTo(new DateTimeBuilder(OFFSET_SECONDS, offset.getTotalSeconds()), buf); + assertEquals(buf.toString(), "EXISTING" + expected); + } + + @Test(dataProvider="offsets") + public void test_toString(String pattern, String expected, ZoneOffset offset) throws Exception { + assertEquals(getFormatter(pattern, "NO-OFFSET").toString(), "Offset('NO-OFFSET'," + pattern + ")"); + } + + //----------------------------------------------------------------------- + @Test(expectedExceptions=DateTimeException.class) + public void test_print_emptyCalendrical() throws Exception { + getFormatter("+HH:MM:ss", "Z").printTo(EMPTY_DTA, buf); + } + + public void test_print_emptyAppendable() throws Exception { + getFormatter("+HH:MM:ss", "Z").printTo(new DateTimeBuilder(OFFSET_SECONDS, OFFSET_0130.getTotalSeconds()), buf); + assertEquals(buf.toString(), "+01:30"); + } + +} diff --git a/jdk/test/java/time/test/java/time/format/TestZoneTextPrinterParser.java b/jdk/test/java/time/test/java/time/format/TestZoneTextPrinterParser.java new file mode 100644 index 00000000000..0f58634b655 --- /dev/null +++ b/jdk/test/java/time/test/java/time/format/TestZoneTextPrinterParser.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package test.java.time.format; + +import java.util.Date; +import java.util.Locale; +import java.util.Random; +import java.util.Set; +import java.util.TimeZone; + +import java.time.ZonedDateTime; +import java.time.ZoneId; +import java.time.temporal.ChronoField; +import java.time.format.DateTimeFormatSymbols; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.TextStyle; +import java.time.zone.ZoneRulesProvider; + +import org.testng.annotations.Test; +import static org.testng.Assert.assertEquals; + +/** + * Test ZoneTextPrinterParser + */ +@Test(groups={"implementation"}) +public class TestZoneTextPrinterParser extends AbstractTestPrinterParser { + + protected static DateTimeFormatter getFormatter(Locale locale, TextStyle style) { + return new DateTimeFormatterBuilder().appendZoneText(style) + .toFormatter(locale) + .withSymbols(DateTimeFormatSymbols.of(locale)); + } + + public void test_printText() { + Random r = new Random(); + int N = 50; + Locale[] locales = Locale.getAvailableLocales(); + Set zids = ZoneRulesProvider.getAvailableZoneIds(); + ZonedDateTime zdt = ZonedDateTime.now(); + + //System.out.printf("locale==%d, timezone=%d%n", locales.length, zids.size()); + while (N-- > 0) { + zdt = zdt.withDayOfYear(r.nextInt(365) + 1) + .with(ChronoField.SECOND_OF_DAY, r.nextInt(86400)); + for (String zid : zids) { + zdt = zdt.withZoneSameLocal(ZoneId.of(zid)); + TimeZone tz = TimeZone.getTimeZone(zid); + boolean isDST = tz.inDaylightTime(new Date(zdt.toInstant().toEpochMilli())); + for (Locale locale : locales) { + printText(locale, zdt, TextStyle.FULL, + tz.getDisplayName(isDST, TimeZone.LONG, locale)); + printText(locale, zdt, TextStyle.SHORT, + tz.getDisplayName(isDST, TimeZone.SHORT, locale)); + } + } + } + } + + private void printText(Locale locale, ZonedDateTime zdt, TextStyle style, String expected) { + String result = getFormatter(locale, style).print(zdt); + if (!result.equals(expected)) { + if (result.equals("FooLocation") || // from rules provider test if same vm + result.startsWith("Etc/GMT") || result.equals("ROC")) { // TBD: match jdk behavior? + return; + } + System.out.println("----------------"); + System.out.printf("tdz[%s]%n", zdt.toString()); + System.out.printf("[%-4s, %5s] :[%s]%n", locale.toString(), style.toString(),result); + System.out.printf("%4s, %5s :[%s]%n", "", "", expected); + } + assertEquals(result, expected); + } +} diff --git a/jdk/test/java/time/test/java/time/temporal/MockFieldNoValue.java b/jdk/test/java/time/test/java/time/temporal/MockFieldNoValue.java new file mode 100644 index 00000000000..255735db006 --- /dev/null +++ b/jdk/test/java/time/test/java/time/temporal/MockFieldNoValue.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.temporal; + +import java.time.format.DateTimeBuilder; +import java.time.temporal.*; + +import static java.time.temporal.ChronoUnit.MONTHS; +import static java.time.temporal.ChronoUnit.WEEKS; + +import java.time.DateTimeException; + +/** + * Mock TemporalField that returns null. + */ +public enum MockFieldNoValue implements TemporalField { + + INSTANCE; + + @Override + public String getName() { + return null; + } + + @Override + public TemporalUnit getBaseUnit() { + return WEEKS; + } + + @Override + public TemporalUnit getRangeUnit() { + return MONTHS; + } + + @Override + public ValueRange range() { + return ValueRange.of(1, 20); + } + + //----------------------------------------------------------------------- + @Override + public boolean doIsSupported(TemporalAccessor temporal) { + return true; + } + + @Override + public ValueRange doRange(TemporalAccessor temporal) { + return ValueRange.of(1, 20); + } + + @Override + public long doGet(TemporalAccessor temporal) { + throw new DateTimeException("Mock"); + } + + @Override + public R doWith(R temporal, long newValue) { + throw new DateTimeException("Mock"); + } + + //----------------------------------------------------------------------- + @Override + public boolean resolve(DateTimeBuilder dateTimeBuilder, long value) { + return false; + } + +} diff --git a/jdk/test/java/time/test/java/time/temporal/MockFieldValue.java b/jdk/test/java/time/test/java/time/temporal/MockFieldValue.java new file mode 100644 index 00000000000..7f478a9a539 --- /dev/null +++ b/jdk/test/java/time/test/java/time/temporal/MockFieldValue.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.temporal; + +import java.time.temporal.*; + +import java.time.DateTimeException; +import java.time.temporal.TemporalAccessor; + +/** + * Mock simple date-time with one field-value. + */ +public final class MockFieldValue implements TemporalAccessor { + + private final TemporalField field; + private final long value; + + public MockFieldValue(TemporalField field, long value) { + this.field = field; + this.value = value; + } + + @Override + public boolean isSupported(TemporalField field) { + return field != null && field.equals(this.field); + } + + @Override + public ValueRange range(TemporalField field) { + if (field instanceof ChronoField) { + if (isSupported(field)) { + return field.range(); + } + throw new DateTimeException("Unsupported field: " + field.getName()); + } + return field.doRange(this); + } + + @Override + public long getLong(TemporalField field) { + if (this.field.equals(field)) { + return value; + } + throw new DateTimeException("Unsupported field: " + field); + } + +} diff --git a/jdk/test/java/time/test/java/time/temporal/TestChronoUnit.java b/jdk/test/java/time/test/java/time/temporal/TestChronoUnit.java new file mode 100644 index 00000000000..b282cda2f85 --- /dev/null +++ b/jdk/test/java/time/test/java/time/temporal/TestChronoUnit.java @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.temporal; + +import static java.time.Month.AUGUST; +import static java.time.Month.FEBRUARY; +import static java.time.Month.JULY; +import static java.time.Month.JUNE; +import static java.time.Month.MARCH; +import static java.time.Month.OCTOBER; +import static java.time.Month.SEPTEMBER; +import static java.time.temporal.ChronoUnit.DAYS; +import static java.time.temporal.ChronoUnit.MONTHS; +import static java.time.temporal.ChronoUnit.WEEKS; +import static java.time.temporal.ChronoUnit.YEARS; +import static org.testng.Assert.assertEquals; + +import java.time.LocalDate; +import java.time.Month; +import java.time.ZoneOffset; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test. + */ +@Test +public class TestChronoUnit { + + //----------------------------------------------------------------------- + @DataProvider(name = "yearsBetween") + Object[][] data_yearsBetween() { + return new Object[][] { + {date(1939, SEPTEMBER, 2), date(1939, SEPTEMBER, 1), 0}, + {date(1939, SEPTEMBER, 2), date(1939, SEPTEMBER, 2), 0}, + {date(1939, SEPTEMBER, 2), date(1939, SEPTEMBER, 3), 0}, + + {date(1939, SEPTEMBER, 2), date(1940, SEPTEMBER, 1), 0}, + {date(1939, SEPTEMBER, 2), date(1940, SEPTEMBER, 2), 1}, + {date(1939, SEPTEMBER, 2), date(1940, SEPTEMBER, 3), 1}, + + {date(1939, SEPTEMBER, 2), date(1938, SEPTEMBER, 1), -1}, + {date(1939, SEPTEMBER, 2), date(1938, SEPTEMBER, 2), -1}, + {date(1939, SEPTEMBER, 2), date(1938, SEPTEMBER, 3), 0}, + + {date(1939, SEPTEMBER, 2), date(1945, SEPTEMBER, 3), 6}, + {date(1939, SEPTEMBER, 2), date(1945, OCTOBER, 3), 6}, + {date(1939, SEPTEMBER, 2), date(1945, AUGUST, 3), 5}, + }; + } + + @Test(dataProvider = "yearsBetween") + public void test_yearsBetween(LocalDate start, LocalDate end, long expected) { + assertEquals(YEARS.between(start, end).getAmount(), expected); + assertEquals(YEARS.between(start, end).getUnit(), YEARS); + } + + @Test(dataProvider = "yearsBetween") + public void test_yearsBetweenReversed(LocalDate start, LocalDate end, long expected) { + assertEquals(YEARS.between(end, start).getAmount(), -expected); + assertEquals(YEARS.between(end, start).getUnit(), YEARS); + } + + @Test(dataProvider = "yearsBetween") + public void test_yearsBetween_LocalDateTimeSameTime(LocalDate start, LocalDate end, long expected) { + assertEquals(YEARS.between(start.atTime(12, 30), end.atTime(12, 30)).getAmount(), expected); + } + + @Test(dataProvider = "yearsBetween") + public void test_yearsBetween_LocalDateTimeLaterTime(LocalDate start, LocalDate end, long expected) { + assertEquals(YEARS.between(start.atTime(12, 30), end.atTime(12, 31)).getAmount(), expected); + } + + @Test(dataProvider = "yearsBetween") + public void test_yearsBetween_ZonedDateSameOffset(LocalDate start, LocalDate end, long expected) { + assertEquals(YEARS.between(start.atStartOfDay(ZoneOffset.ofHours(2)), end.atStartOfDay(ZoneOffset.ofHours(2))).getAmount(), expected); + } + + @Test(dataProvider = "yearsBetween") + public void test_yearsBetween_ZonedDateLaterOffset(LocalDate start, LocalDate end, long expected) { + // +01:00 is later than +02:00 + assertEquals(YEARS.between(start.atStartOfDay(ZoneOffset.ofHours(2)), end.atStartOfDay(ZoneOffset.ofHours(1))).getAmount(), expected); + } + + //----------------------------------------------------------------------- + @DataProvider(name = "monthsBetween") + Object[][] data_monthsBetween() { + return new Object[][] { + {date(2012, JULY, 2), date(2012, JULY, 1), 0}, + {date(2012, JULY, 2), date(2012, JULY, 2), 0}, + {date(2012, JULY, 2), date(2012, JULY, 3), 0}, + + {date(2012, JULY, 2), date(2012, AUGUST, 1), 0}, + {date(2012, JULY, 2), date(2012, AUGUST, 2), 1}, + {date(2012, JULY, 2), date(2012, AUGUST, 3), 1}, + + {date(2012, JULY, 2), date(2012, SEPTEMBER, 1), 1}, + {date(2012, JULY, 2), date(2012, SEPTEMBER, 2), 2}, + {date(2012, JULY, 2), date(2012, SEPTEMBER, 3), 2}, + + {date(2012, JULY, 2), date(2012, JUNE, 1), -1}, + {date(2012, JULY, 2), date(2012, JUNE, 2), -1}, + {date(2012, JULY, 2), date(2012, JUNE, 3), 0}, + + {date(2012, FEBRUARY, 27), date(2012, MARCH, 26), 0}, + {date(2012, FEBRUARY, 27), date(2012, MARCH, 27), 1}, + {date(2012, FEBRUARY, 27), date(2012, MARCH, 28), 1}, + + {date(2012, FEBRUARY, 28), date(2012, MARCH, 27), 0}, + {date(2012, FEBRUARY, 28), date(2012, MARCH, 28), 1}, + {date(2012, FEBRUARY, 28), date(2012, MARCH, 29), 1}, + + {date(2012, FEBRUARY, 29), date(2012, MARCH, 28), 0}, + {date(2012, FEBRUARY, 29), date(2012, MARCH, 29), 1}, + {date(2012, FEBRUARY, 29), date(2012, MARCH, 30), 1}, + }; + } + + @Test(dataProvider = "monthsBetween") + public void test_monthsBetween(LocalDate start, LocalDate end, long expected) { + assertEquals(MONTHS.between(start, end).getAmount(), expected); + assertEquals(MONTHS.between(start, end).getUnit(), MONTHS); + } + + @Test(dataProvider = "monthsBetween") + public void test_monthsBetweenReversed(LocalDate start, LocalDate end, long expected) { + assertEquals(MONTHS.between(end, start).getAmount(), -expected); + assertEquals(MONTHS.between(end, start).getUnit(), MONTHS); + } + + @Test(dataProvider = "monthsBetween") + public void test_monthsBetween_LocalDateTimeSameTime(LocalDate start, LocalDate end, long expected) { + assertEquals(MONTHS.between(start.atTime(12, 30), end.atTime(12, 30)).getAmount(), expected); + } + + @Test(dataProvider = "monthsBetween") + public void test_monthsBetween_LocalDateTimeLaterTime(LocalDate start, LocalDate end, long expected) { + assertEquals(MONTHS.between(start.atTime(12, 30), end.atTime(12, 31)).getAmount(), expected); + } + + @Test(dataProvider = "monthsBetween") + public void test_monthsBetween_ZonedDateSameOffset(LocalDate start, LocalDate end, long expected) { + assertEquals(MONTHS.between(start.atStartOfDay(ZoneOffset.ofHours(2)), end.atStartOfDay(ZoneOffset.ofHours(2))).getAmount(), expected); + } + + @Test(dataProvider = "monthsBetween") + public void test_monthsBetween_ZonedDateLaterOffset(LocalDate start, LocalDate end, long expected) { + // +01:00 is later than +02:00 + assertEquals(MONTHS.between(start.atStartOfDay(ZoneOffset.ofHours(2)), end.atStartOfDay(ZoneOffset.ofHours(1))).getAmount(), expected); + } + + //----------------------------------------------------------------------- + @DataProvider(name = "weeksBetween") + Object[][] data_weeksBetween() { + return new Object[][] { + {date(2012, JULY, 2), date(2012, JUNE, 25), -1}, + {date(2012, JULY, 2), date(2012, JUNE, 26), 0}, + {date(2012, JULY, 2), date(2012, JULY, 2), 0}, + {date(2012, JULY, 2), date(2012, JULY, 8), 0}, + {date(2012, JULY, 2), date(2012, JULY, 9), 1}, + + {date(2012, FEBRUARY, 28), date(2012, FEBRUARY, 21), -1}, + {date(2012, FEBRUARY, 28), date(2012, FEBRUARY, 22), 0}, + {date(2012, FEBRUARY, 28), date(2012, FEBRUARY, 28), 0}, + {date(2012, FEBRUARY, 28), date(2012, FEBRUARY, 29), 0}, + {date(2012, FEBRUARY, 28), date(2012, MARCH, 1), 0}, + {date(2012, FEBRUARY, 28), date(2012, MARCH, 5), 0}, + {date(2012, FEBRUARY, 28), date(2012, MARCH, 6), 1}, + + {date(2012, FEBRUARY, 29), date(2012, FEBRUARY, 22), -1}, + {date(2012, FEBRUARY, 29), date(2012, FEBRUARY, 23), 0}, + {date(2012, FEBRUARY, 29), date(2012, FEBRUARY, 28), 0}, + {date(2012, FEBRUARY, 29), date(2012, FEBRUARY, 29), 0}, + {date(2012, FEBRUARY, 29), date(2012, MARCH, 1), 0}, + {date(2012, FEBRUARY, 29), date(2012, MARCH, 6), 0}, + {date(2012, FEBRUARY, 29), date(2012, MARCH, 7), 1}, + }; + } + + @Test(dataProvider = "weeksBetween") + public void test_weeksBetween(LocalDate start, LocalDate end, long expected) { + assertEquals(WEEKS.between(start, end).getAmount(), expected); + assertEquals(WEEKS.between(start, end).getUnit(), WEEKS); + } + + @Test(dataProvider = "weeksBetween") + public void test_weeksBetweenReversed(LocalDate start, LocalDate end, long expected) { + assertEquals(WEEKS.between(end, start).getAmount(), -expected); + assertEquals(WEEKS.between(end, start).getUnit(), WEEKS); + } + + //----------------------------------------------------------------------- + @DataProvider(name = "daysBetween") + Object[][] data_daysBetween() { + return new Object[][] { + {date(2012, JULY, 2), date(2012, JULY, 1), -1}, + {date(2012, JULY, 2), date(2012, JULY, 2), 0}, + {date(2012, JULY, 2), date(2012, JULY, 3), 1}, + + {date(2012, FEBRUARY, 28), date(2012, FEBRUARY, 27), -1}, + {date(2012, FEBRUARY, 28), date(2012, FEBRUARY, 28), 0}, + {date(2012, FEBRUARY, 28), date(2012, FEBRUARY, 29), 1}, + {date(2012, FEBRUARY, 28), date(2012, MARCH, 1), 2}, + + {date(2012, FEBRUARY, 29), date(2012, FEBRUARY, 27), -2}, + {date(2012, FEBRUARY, 29), date(2012, FEBRUARY, 28), -1}, + {date(2012, FEBRUARY, 29), date(2012, FEBRUARY, 29), 0}, + {date(2012, FEBRUARY, 29), date(2012, MARCH, 1), 1}, + + {date(2012, MARCH, 1), date(2012, FEBRUARY, 27), -3}, + {date(2012, MARCH, 1), date(2012, FEBRUARY, 28), -2}, + {date(2012, MARCH, 1), date(2012, FEBRUARY, 29), -1}, + {date(2012, MARCH, 1), date(2012, MARCH, 1), 0}, + {date(2012, MARCH, 1), date(2012, MARCH, 2), 1}, + + {date(2012, MARCH, 1), date(2013, FEBRUARY, 28), 364}, + {date(2012, MARCH, 1), date(2013, MARCH, 1), 365}, + + {date(2011, MARCH, 1), date(2012, FEBRUARY, 28), 364}, + {date(2011, MARCH, 1), date(2012, FEBRUARY, 29), 365}, + {date(2011, MARCH, 1), date(2012, MARCH, 1), 366}, + }; + } + + @Test(dataProvider = "daysBetween") + public void test_daysBetween(LocalDate start, LocalDate end, long expected) { + assertEquals(DAYS.between(start, end).getAmount(), expected); + assertEquals(DAYS.between(start, end).getUnit(), DAYS); + } + + @Test(dataProvider = "daysBetween") + public void test_daysBetweenReversed(LocalDate start, LocalDate end, long expected) { + assertEquals(DAYS.between(end, start).getAmount(), -expected); + assertEquals(DAYS.between(end, start).getUnit(), DAYS); + } + + @Test(dataProvider = "daysBetween") + public void test_daysBetween_LocalDateTimeSameTime(LocalDate start, LocalDate end, long expected) { + assertEquals(DAYS.between(start.atTime(12, 30), end.atTime(12, 30)).getAmount(), expected); + } + + @Test(dataProvider = "daysBetween") + public void test_daysBetween_LocalDateTimeLaterTime(LocalDate start, LocalDate end, long expected) { + assertEquals(DAYS.between(start.atTime(12, 30), end.atTime(12, 31)).getAmount(), expected); + } + + @Test(dataProvider = "daysBetween") + public void test_daysBetween_ZonedDateSameOffset(LocalDate start, LocalDate end, long expected) { + assertEquals(DAYS.between(start.atStartOfDay(ZoneOffset.ofHours(2)), end.atStartOfDay(ZoneOffset.ofHours(2))).getAmount(), expected); + } + + @Test(dataProvider = "daysBetween") + public void test_daysBetween_ZonedDateLaterOffset(LocalDate start, LocalDate end, long expected) { + // +01:00 is later than +02:00 + assertEquals(DAYS.between(start.atStartOfDay(ZoneOffset.ofHours(2)), end.atStartOfDay(ZoneOffset.ofHours(1))).getAmount(), expected); + } + + //----------------------------------------------------------------------- + private static LocalDate date(int year, Month month, int dom) { + return LocalDate.of(year, month, dom); + } + +} diff --git a/jdk/test/java/time/test/java/time/temporal/TestDateTimeAdjusters.java b/jdk/test/java/time/test/java/time/temporal/TestDateTimeAdjusters.java new file mode 100644 index 00000000000..bdade55f572 --- /dev/null +++ b/jdk/test/java/time/test/java/time/temporal/TestDateTimeAdjusters.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2007-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.temporal; + +import java.time.temporal.*; + +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.util.Collections; + +import org.testng.annotations.Test; + +/** + * Test Adjusters. + */ +@Test(groups={"implementation"}) +public class TestDateTimeAdjusters { + + @SuppressWarnings("rawtypes") + public void test_constructor() throws Exception { + for (Constructor constructor : Adjusters.class.getDeclaredConstructors()) { + assertTrue(Modifier.isPrivate(constructor.getModifiers())); + constructor.setAccessible(true); + constructor.newInstance(Collections.nCopies(constructor.getParameterTypes().length, null).toArray()); + } + } + + public void factory_firstDayOfMonthSame() { + assertSame(Adjusters.firstDayOfMonth(), Adjusters.firstDayOfMonth()); + } + + public void factory_lastDayOfMonthSame() { + assertSame(Adjusters.lastDayOfMonth(), Adjusters.lastDayOfMonth()); + } + + public void factory_firstDayOfNextMonthSame() { + assertSame(Adjusters.firstDayOfNextMonth(), Adjusters.firstDayOfNextMonth()); + } + + public void factory_firstDayOfYearSame() { + assertSame(Adjusters.firstDayOfYear(), Adjusters.firstDayOfYear()); + } + + public void factory_lastDayOfYearSame() { + assertSame(Adjusters.lastDayOfYear(), Adjusters.lastDayOfYear()); + } + + public void factory_firstDayOfNextYearSame() { + assertSame(Adjusters.firstDayOfNextYear(), Adjusters.firstDayOfNextYear()); + } + +} diff --git a/jdk/test/java/time/test/java/time/temporal/TestDateTimeBuilderCombinations.java b/jdk/test/java/time/test/java/time/temporal/TestDateTimeBuilderCombinations.java new file mode 100644 index 00000000000..328ead5e4e2 --- /dev/null +++ b/jdk/test/java/time/test/java/time/temporal/TestDateTimeBuilderCombinations.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.temporal; + +import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; +import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; +import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; +import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR; +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.DAY_OF_WEEK; +import static java.time.temporal.ChronoField.DAY_OF_YEAR; +import static java.time.temporal.ChronoField.EPOCH_DAY; +import static java.time.temporal.ChronoField.EPOCH_MONTH; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.YEAR; +import static org.testng.Assert.assertEquals; + +import java.time.LocalDate; +import java.time.format.DateTimeBuilder; +import java.time.temporal.TemporalField; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test. + */ +public class TestDateTimeBuilderCombinations { + + @DataProvider(name = "combine") + Object[][] data_combine() { + return new Object[][] { + {YEAR, 2012, MONTH_OF_YEAR, 6, DAY_OF_MONTH, 3, null, null, LocalDate.class, LocalDate.of(2012, 6, 3)}, + {EPOCH_MONTH, (2012 - 1970) * 12 + 6 - 1, DAY_OF_MONTH, 3, null, null, null, null, LocalDate.class, LocalDate.of(2012, 6, 3)}, + {YEAR, 2012, ALIGNED_WEEK_OF_YEAR, 6, DAY_OF_WEEK, 3, null, null, LocalDate.class, LocalDate.of(2012, 2, 8)}, + {YEAR, 2012, DAY_OF_YEAR, 155, null, null, null, null, LocalDate.class, LocalDate.of(2012, 6, 3)}, +// {ERA, 1, YEAR_OF_ERA, 2012, DAY_OF_YEAR, 155, null, null, LocalDate.class, LocalDate.of(2012, 6, 3)}, + {YEAR, 2012, MONTH_OF_YEAR, 6, null, null, null, null, LocalDate.class, null}, + {EPOCH_DAY, 12, null, null, null, null, null, null, LocalDate.class, LocalDate.of(1970, 1, 13)}, + }; + } + + @Test(dataProvider = "combine") + public void test_derive(TemporalField field1, Number value1, TemporalField field2, Number value2, + TemporalField field3, Number value3, TemporalField field4, Number value4, Class query, Object expectedVal) { + DateTimeBuilder builder = new DateTimeBuilder(field1, value1.longValue()); + if (field2 != null) { + builder.addFieldValue(field2, value2.longValue()); + } + if (field3 != null) { + builder.addFieldValue(field3, value3.longValue()); + } + if (field4 != null) { + builder.addFieldValue(field4, value4.longValue()); + } + builder.resolve(); + assertEquals(builder.extract((Class) query), expectedVal); + } + + //----------------------------------------------------------------------- + @DataProvider(name = "normalized") + Object[][] data_normalized() { + return new Object[][] { + {YEAR, 2127, null, null, null, null, YEAR, 2127}, + {MONTH_OF_YEAR, 12, null, null, null, null, MONTH_OF_YEAR, 12}, + {DAY_OF_YEAR, 127, null, null, null, null, DAY_OF_YEAR, 127}, + {DAY_OF_MONTH, 23, null, null, null, null, DAY_OF_MONTH, 23}, + {DAY_OF_WEEK, 127, null, null, null, null, DAY_OF_WEEK, 127L}, + {ALIGNED_WEEK_OF_YEAR, 23, null, null, null, null, ALIGNED_WEEK_OF_YEAR, 23}, + {ALIGNED_DAY_OF_WEEK_IN_YEAR, 4, null, null, null, null, ALIGNED_DAY_OF_WEEK_IN_YEAR, 4L}, + {ALIGNED_WEEK_OF_MONTH, 4, null, null, null, null, ALIGNED_WEEK_OF_MONTH, 4}, + {ALIGNED_DAY_OF_WEEK_IN_MONTH, 3, null, null, null, null, ALIGNED_DAY_OF_WEEK_IN_MONTH, 3}, + {EPOCH_MONTH, 15, null, null, null, null, EPOCH_MONTH, null}, + {EPOCH_MONTH, 15, null, null, null, null, YEAR, 1971}, + {EPOCH_MONTH, 15, null, null, null, null, MONTH_OF_YEAR, 4}, + }; + } + + @Test(dataProvider = "normalized") + public void test_normalized(TemporalField field1, Number value1, TemporalField field2, Number value2, + TemporalField field3, Number value3, TemporalField query, Number expectedVal) { + DateTimeBuilder builder = new DateTimeBuilder(field1, value1.longValue()); + if (field2 != null) { + builder.addFieldValue(field2, value2.longValue()); + } + if (field3 != null) { + builder.addFieldValue(field3, value3.longValue()); + } + builder.resolve(); + if (expectedVal != null) { + assertEquals(builder.getLong(query), expectedVal.longValue()); + } else { + assertEquals(builder.containsFieldValue(query), false); + } + } + + //----------------------------------------------------------------------- + // TODO: maybe reinstate +// public void test_split() { +// DateTimeBuilder builder = new DateTimeBuilder(); +// builder.addCalendrical(LocalDateTime.of(2012, 6, 30, 12, 30)); +// builder.addCalendrical(ZoneOffset.ofHours(2)); +// builder.resolve(); +// assertEquals(builder.build(LocalDate.class), LocalDate.of(2012, 6, 30)); +// assertEquals(builder.build(LocalTime.class), LocalTime.of(12, 30)); +// assertEquals(builder.build(ZoneOffset.class), ZoneOffset.ofHours(2)); +// +// assertEquals(builder.build(LocalDateTime.class), LocalDateTime.of(2012, 6, 30, 12, 30)); +// assertEquals(builder.build(OffsetDate.class), OffsetDate.of(LocalDate.of(2012, 6, 30), ZoneOffset.ofHours(2))); +// assertEquals(builder.build(OffsetTime.class), OffsetTime.of(LocalTime.of(12, 30), ZoneOffset.ofHours(2))); +//// assertEquals(builder.build(OffsetDateTime.class), OffsetDateTime.of(2012, 6, 30, 12, 30, ZoneOffset.ofHours(2))); +// } + +} diff --git a/jdk/test/java/time/test/java/time/temporal/TestDateTimeValueRange.java b/jdk/test/java/time/test/java/time/temporal/TestDateTimeValueRange.java new file mode 100644 index 00000000000..d7397d41e64 --- /dev/null +++ b/jdk/test/java/time/test/java/time/temporal/TestDateTimeValueRange.java @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2009-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.temporal; + +import static org.testng.Assert.assertEquals; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import java.time.temporal.ValueRange; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import test.java.time.AbstractTest; + +/** + * Test. + */ +@Test +public class TestDateTimeValueRange extends AbstractTest { + + //----------------------------------------------------------------------- + // Basics + //----------------------------------------------------------------------- + @Test + public void test_immutable() { + assertImmutable(ValueRange.class); + } + + //----------------------------------------------------------------------- + // Serialization + //----------------------------------------------------------------------- + public void test_serialization() throws Exception { + Object obj = ValueRange.of(1, 2, 3, 4); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(obj); + oos.close(); + ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())); + assertEquals(ois.readObject(), obj); + } + + //----------------------------------------------------------------------- + // of(long,long) + //----------------------------------------------------------------------- + public void test_of_longlong() { + ValueRange test = ValueRange.of(1, 12); + assertEquals(test.getMinimum(), 1); + assertEquals(test.getLargestMinimum(), 1); + assertEquals(test.getSmallestMaximum(), 12); + assertEquals(test.getMaximum(), 12); + assertEquals(test.isFixed(), true); + assertEquals(test.isIntValue(), true); + } + + public void test_of_longlong_big() { + ValueRange test = ValueRange.of(1, 123456789012345L); + assertEquals(test.getMinimum(), 1); + assertEquals(test.getLargestMinimum(), 1); + assertEquals(test.getSmallestMaximum(), 123456789012345L); + assertEquals(test.getMaximum(), 123456789012345L); + assertEquals(test.isFixed(), true); + assertEquals(test.isIntValue(), false); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void test_of_longlong_minGtMax() { + ValueRange.of(12, 1); + } + + //----------------------------------------------------------------------- + // of(long,long,long) + //----------------------------------------------------------------------- + public void test_of_longlonglong() { + ValueRange test = ValueRange.of(1, 28, 31); + assertEquals(test.getMinimum(), 1); + assertEquals(test.getLargestMinimum(), 1); + assertEquals(test.getSmallestMaximum(), 28); + assertEquals(test.getMaximum(), 31); + assertEquals(test.isFixed(), false); + assertEquals(test.isIntValue(), true); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void test_of_longlonglong_minGtMax() { + ValueRange.of(12, 1, 2); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void test_of_longlonglong_smallestmaxminGtMax() { + ValueRange.of(1, 31, 28); + } + + //----------------------------------------------------------------------- + // of(long,long,long,long) + //----------------------------------------------------------------------- + @DataProvider(name="valid") + Object[][] data_valid() { + return new Object[][] { + {1, 1, 1, 1}, + {1, 1, 1, 2}, + {1, 1, 2, 2}, + {1, 2, 3, 4}, + {1, 1, 28, 31}, + {1, 3, 31, 31}, + {-5, -4, -3, -2}, + {-5, -4, 3, 4}, + {1, 20, 10, 31}, + }; + } + + @Test(dataProvider="valid") + public void test_of_longlonglonglong(long sMin, long lMin, long sMax, long lMax) { + ValueRange test = ValueRange.of(sMin, lMin, sMax, lMax); + assertEquals(test.getMinimum(), sMin); + assertEquals(test.getLargestMinimum(), lMin); + assertEquals(test.getSmallestMaximum(), sMax); + assertEquals(test.getMaximum(), lMax); + assertEquals(test.isFixed(), sMin == lMin && sMax == lMax); + assertEquals(test.isIntValue(), true); + } + + @DataProvider(name="invalid") + Object[][] data_invalid() { + return new Object[][] { + {1, 2, 31, 28}, + {1, 31, 2, 28}, + {31, 2, 1, 28}, + {31, 2, 3, 28}, + + {2, 1, 28, 31}, + {2, 1, 31, 28}, + {12, 13, 1, 2}, + }; + } + + @Test(dataProvider="invalid", expectedExceptions=IllegalArgumentException.class) + public void test_of_longlonglonglong_invalid(long sMin, long lMin, long sMax, long lMax) { + ValueRange.of(sMin, lMin, sMax, lMax); + } + + //----------------------------------------------------------------------- + // isValidValue(long) + //----------------------------------------------------------------------- + public void test_isValidValue_long() { + ValueRange test = ValueRange.of(1, 28, 31); + assertEquals(test.isValidValue(0), false); + assertEquals(test.isValidValue(1), true); + assertEquals(test.isValidValue(2), true); + assertEquals(test.isValidValue(30), true); + assertEquals(test.isValidValue(31), true); + assertEquals(test.isValidValue(32), false); + } + + //----------------------------------------------------------------------- + // isValidIntValue(long) + //----------------------------------------------------------------------- + public void test_isValidValue_long_int() { + ValueRange test = ValueRange.of(1, 28, 31); + assertEquals(test.isValidValue(0), false); + assertEquals(test.isValidValue(1), true); + assertEquals(test.isValidValue(31), true); + assertEquals(test.isValidValue(32), false); + } + + public void test_isValidValue_long_long() { + ValueRange test = ValueRange.of(1, 28, Integer.MAX_VALUE + 1L); + assertEquals(test.isValidIntValue(0), false); + assertEquals(test.isValidIntValue(1), false); + assertEquals(test.isValidIntValue(31), false); + assertEquals(test.isValidIntValue(32), false); + } + + //----------------------------------------------------------------------- + // equals() / hashCode() + //----------------------------------------------------------------------- + public void test_equals1() { + ValueRange a = ValueRange.of(1, 2, 3, 4); + ValueRange b = ValueRange.of(1, 2, 3, 4); + assertEquals(a.equals(a), true); + assertEquals(a.equals(b), true); + assertEquals(b.equals(a), true); + assertEquals(b.equals(b), true); + assertEquals(a.hashCode() == b.hashCode(), true); + } + + public void test_equals2() { + ValueRange a = ValueRange.of(1, 2, 3, 4); + assertEquals(a.equals(ValueRange.of(0, 2, 3, 4)), false); + assertEquals(a.equals(ValueRange.of(1, 3, 3, 4)), false); + assertEquals(a.equals(ValueRange.of(1, 2, 4, 4)), false); + assertEquals(a.equals(ValueRange.of(1, 2, 3, 5)), false); + } + + public void test_equals_otherType() { + ValueRange a = ValueRange.of(1, 12); + assertEquals(a.equals("Rubbish"), false); + } + + public void test_equals_null() { + ValueRange a = ValueRange.of(1, 12); + assertEquals(a.equals(null), false); + } + + //----------------------------------------------------------------------- + // toString() + //----------------------------------------------------------------------- + public void test_toString() { + assertEquals(ValueRange.of(1, 1, 4, 4).toString(), "1 - 4"); + assertEquals(ValueRange.of(1, 1, 3, 4).toString(), "1 - 3/4"); + assertEquals(ValueRange.of(1, 2, 3, 4).toString(), "1/2 - 3/4"); + assertEquals(ValueRange.of(1, 2, 4, 4).toString(), "1/2 - 4"); + } + +} diff --git a/jdk/test/java/time/test/java/time/temporal/TestISOChronoImpl.java b/jdk/test/java/time/test/java/time/temporal/TestISOChronoImpl.java new file mode 100644 index 00000000000..9fc01553202 --- /dev/null +++ b/jdk/test/java/time/test/java/time/temporal/TestISOChronoImpl.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.temporal; + +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.YEAR; +import static java.time.temporal.ChronoField.YEAR_OF_ERA; + +import static org.testng.Assert.assertEquals; + +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; +import java.time.temporal.ISOChrono; +import java.time.temporal.WeekFields; +import java.time.temporal.ChronoLocalDate; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test. + */ +@Test +public class TestISOChronoImpl { + + @DataProvider(name = "RangeVersusCalendar") + Object[][] provider_rangeVersusCalendar() { + return new Object[][]{ + {LocalDate.of(1900, 1, 4), LocalDate.of(2100, 1, 8)}, + //{LocalDate.of(1583, 1, 1), LocalDate.of(2100, 1, 1)}, + }; + } + + //----------------------------------------------------------------------- + // Verify ISO Calendar matches java.util.Calendar for range + //----------------------------------------------------------------------- + @Test(groups = {"implementation"}, dataProvider = "RangeVersusCalendar") + public void test_ISOChrono_vsCalendar(LocalDate isoStartDate, LocalDate isoEndDate) { + GregorianCalendar cal = new GregorianCalendar(); + assertEquals(cal.getCalendarType(), "gregory", "Unexpected calendar type"); + ChronoLocalDate isoDate = ISOChrono.INSTANCE.date(isoStartDate); + + cal.setTimeZone(TimeZone.getTimeZone("GMT+00")); + cal.set(Calendar.YEAR, isoDate.get(YEAR)); + cal.set(Calendar.MONTH, isoDate.get(MONTH_OF_YEAR) - 1); + cal.set(Calendar.DAY_OF_MONTH, isoDate.get(DAY_OF_MONTH)); + + while (isoDate.isBefore(isoEndDate)) { + assertEquals(isoDate.get(DAY_OF_MONTH), cal.get(Calendar.DAY_OF_MONTH), "Day mismatch in " + isoDate + "; cal: " + cal); + assertEquals(isoDate.get(MONTH_OF_YEAR), cal.get(Calendar.MONTH) + 1, "Month mismatch in " + isoDate); + assertEquals(isoDate.get(YEAR_OF_ERA), cal.get(Calendar.YEAR), "Year mismatch in " + isoDate); + + isoDate = isoDate.plus(1, ChronoUnit.DAYS); + cal.add(Calendar.DAY_OF_MONTH, 1); + } + } + + //----------------------------------------------------------------------- + // Verify ISO Calendar matches java.util.Calendar + // DayOfWeek, WeekOfMonth, WeekOfYear for range + //----------------------------------------------------------------------- + @Test(groups = {"implementation"}, dataProvider = "RangeVersusCalendar") + public void test_DayOfWeek_ISOChrono_vsCalendar(LocalDate isoStartDate, LocalDate isoEndDate) { + GregorianCalendar cal = new GregorianCalendar(); + assertEquals(cal.getCalendarType(), "gregory", "Unexpected calendar type"); + ChronoLocalDate isoDate = ISOChrono.INSTANCE.date(isoStartDate); + + for (DayOfWeek firstDayOfWeek : DayOfWeek.values()) { + for (int minDays = 1; minDays <= 7; minDays++) { + WeekFields weekDef = WeekFields.of(firstDayOfWeek, minDays); + cal.setFirstDayOfWeek(Math.floorMod(firstDayOfWeek.getValue(), 7) + 1); + cal.setMinimalDaysInFirstWeek(minDays); + + cal.setTimeZone(TimeZone.getTimeZone("GMT+00")); + cal.set(Calendar.YEAR, isoDate.get(YEAR)); + cal.set(Calendar.MONTH, isoDate.get(MONTH_OF_YEAR) - 1); + cal.set(Calendar.DAY_OF_MONTH, isoDate.get(DAY_OF_MONTH)); + + while (isoDate.isBefore(isoEndDate)) { + assertEquals(isoDate.get(DAY_OF_MONTH), cal.get(Calendar.DAY_OF_MONTH), "Day mismatch in " + isoDate + "; cal: " + cal); + assertEquals(isoDate.get(MONTH_OF_YEAR), cal.get(Calendar.MONTH) + 1, "Month mismatch in " + isoDate); + assertEquals(isoDate.get(YEAR_OF_ERA), cal.get(Calendar.YEAR), "Year mismatch in " + isoDate); + int jDOW = Math.floorMod(cal.get(Calendar.DAY_OF_WEEK) - 2, 7) + 1; + int isoDOW = isoDate.get(weekDef.dayOfWeek()); + if (jDOW != isoDOW) { + System.err.printf(" DOW vs Calendar jdow: %s, isoDate(DOW): %s, isoDate: %s, WeekDef: %s%n", jDOW, isoDOW, isoDate, weekDef); + } + assertEquals(jDOW, isoDOW, "Calendar DayOfWeek does not match ISO DayOfWeek"); + + int jweekOfMonth = cal.get(Calendar.WEEK_OF_MONTH); + int isoWeekOfMonth = isoDate.get(weekDef.weekOfMonth()); + if (jweekOfMonth != isoWeekOfMonth) { + System.err.printf(" WeekOfMonth jWeekOfMonth: %s, isoWeekOfMonth: %s, isoDate: %s, %s%n", + jweekOfMonth, isoWeekOfMonth, isoDate, weekDef); + } + assertEquals(jweekOfMonth, isoWeekOfMonth, "Calendar WeekOfMonth does not match ISO WeekOfMonth"); + + int jweekOfYear = cal.get(Calendar.WEEK_OF_YEAR); + int isoWeekOfYear = isoDate.get(weekDef.weekOfYear()); + if (jweekOfYear != isoWeekOfYear) { + // TBD: Issue #186 Remove misleading output pending resolution + // System.err.printf(" Mismatch WeekOfYear jweekOfYear: %s, isoWeekOfYear: %s, isoDate: %s, WeekDef: %s%n", jweekOfYear, isoWeekOfYear, isoDate, weekDef); + } + //assertEquals(jweekOfYear, isoWeekOfYear, "Calendar WeekOfYear does not match ISO WeekOfYear"); + + isoDate = isoDate.plus(1, ChronoUnit.DAYS); + cal.add(Calendar.DAY_OF_MONTH, 1); + } + } + } + } + + /** + * Return the ISO Day of Week from a java.util.Calendr DAY_OF_WEEK. + * @param the java.util.Calendar day of week (1=Sunday, 7=Saturday) + * @return the ISO DayOfWeek + */ + private DayOfWeek toISOfromCalendarDOW(int i) { + return DayOfWeek.of(Math.floorMod(i - 2, 7) + 1); + } +} diff --git a/jdk/test/java/time/test/java/time/temporal/TestJapaneseChronoImpl.java b/jdk/test/java/time/test/java/time/temporal/TestJapaneseChronoImpl.java new file mode 100644 index 00000000000..cadd1a378c6 --- /dev/null +++ b/jdk/test/java/time/test/java/time/temporal/TestJapaneseChronoImpl.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.temporal; + +import static org.testng.Assert.assertEquals; + +import java.util.Calendar; +import java.util.Locale; +import java.util.TimeZone; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.temporal.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoLocalDate; +import java.time.temporal.ChronoUnit; +import java.time.calendar.JapaneseChrono; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test. + */ +@Test +public class TestJapaneseChronoImpl { + + /** + * Range of years to check consistency with java.util.Calendar + */ + @DataProvider(name="RangeVersusCalendar") + Object[][] provider_rangeVersusCalendar() { + return new Object[][] { + {LocalDate.of(1868, 1, 1), LocalDate.of(2100, 1, 1)}, + }; + } + + //----------------------------------------------------------------------- + // Verify Japanese Calendar matches java.util.Calendar for range + //----------------------------------------------------------------------- + @Test(groups={"implementation"}, dataProvider="RangeVersusCalendar") + public void test_JapaneseChrono_vsCalendar(LocalDate isoStartDate, LocalDate isoEndDate) { + Locale locale = Locale.forLanguageTag("ja-JP-u-ca-japanese"); + assertEquals(locale.toString(), "ja_JP_#u-ca-japanese", "Unexpected locale"); + + Calendar cal = java.util.Calendar.getInstance(locale); + assertEquals(cal.getCalendarType(), "japanese", "Unexpected calendar type"); + + ChronoLocalDate jDate = JapaneseChrono.INSTANCE.date(isoStartDate); + + // Convert to millis and set Japanese Calendar to that start date (at GMT) + OffsetDateTime jodt = OffsetDateTime.of(isoStartDate, LocalTime.MIN, ZoneOffset.UTC); + long millis = jodt.toInstant().toEpochMilli(); + cal.setTimeZone(TimeZone.getTimeZone("GMT+00")); + cal.setTimeInMillis(millis); + + while (jDate.isBefore(isoEndDate)) { + assertEquals(jDate.get(ChronoField.DAY_OF_MONTH), cal.get(Calendar.DAY_OF_MONTH), "Day mismatch in " + jDate + "; cal: " + cal); + assertEquals(jDate.get(ChronoField.MONTH_OF_YEAR), cal.get(Calendar.MONTH) + 1, "Month mismatch in " + jDate); + assertEquals(jDate.get(ChronoField.YEAR_OF_ERA), cal.get(Calendar.YEAR), "Year mismatch in " + jDate); + + jDate = jDate.plus(1, ChronoUnit.DAYS); + cal.add(Calendar.DAY_OF_MONTH, 1); + } + } + +} diff --git a/jdk/test/java/time/test/java/time/temporal/TestMonthDay.java b/jdk/test/java/time/test/java/time/temporal/TestMonthDay.java new file mode 100644 index 00000000000..002d4f7e286 --- /dev/null +++ b/jdk/test/java/time/test/java/time/temporal/TestMonthDay.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.temporal; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; + +import java.time.LocalDate; +import java.time.Month; +import java.time.temporal.MonthDay; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import test.java.time.AbstractTest; + +/** + * Test MonthDay. + */ +@Test +public class TestMonthDay extends AbstractTest { + + private MonthDay TEST_07_15; + + @BeforeMethod(groups={"tck","implementation"}) + public void setUp() { + TEST_07_15 = MonthDay.of(7, 15); + } + + //----------------------------------------------------------------------- + @Test + public void test_immutable() { + assertImmutable(MonthDay.class); + } + + //----------------------------------------------------------------------- + void check(MonthDay test, int m, int d) { + assertEquals(test.getMonth().getValue(), m); + assertEquals(test.getDayOfMonth(), d); + } + + @Test(groups={"implementation"}) + public void test_with_Month_noChangeSame() { + MonthDay test = MonthDay.of(6, 30); + assertSame(test.with(Month.JUNE), test); + } + + @Test(groups={"implementation"}) + public void test_withMonth_int_noChangeSame() { + MonthDay test = MonthDay.of(6, 30); + assertSame(test.withMonth(6), test); + } + @Test(groups={"implementation"}) + public void test_withDayOfMonth_noChangeSame() { + MonthDay test = MonthDay.of(6, 30); + assertSame(test.withDayOfMonth(30), test); + } + + @Test(groups={"implementation"}) + public void test_adjustDate_same() { + MonthDay test = MonthDay.of(6, 30); + LocalDate date = LocalDate.of(2007, 6, 30); + assertSame(test.adjustInto(date), date); + } + + void doTest_comparisons_MonthDay(MonthDay... localDates) { + for (int i = 0; i < localDates.length; i++) { + MonthDay a = localDates[i]; + for (int j = 0; j < localDates.length; j++) { + MonthDay b = localDates[j]; + if (i < j) { + assertTrue(a.compareTo(b) < 0, a + " <=> " + b); + assertEquals(a.isBefore(b), true, a + " <=> " + b); + assertEquals(a.isAfter(b), false, a + " <=> " + b); + assertEquals(a.equals(b), false, a + " <=> " + b); + } else if (i > j) { + assertTrue(a.compareTo(b) > 0, a + " <=> " + b); + assertEquals(a.isBefore(b), false, a + " <=> " + b); + assertEquals(a.isAfter(b), true, a + " <=> " + b); + assertEquals(a.equals(b), false, a + " <=> " + b); + } else { + assertEquals(a.compareTo(b), 0, a + " <=> " + b); + assertEquals(a.isBefore(b), false, a + " <=> " + b); + assertEquals(a.isAfter(b), false, a + " <=> " + b); + assertEquals(a.equals(b), true, a + " <=> " + b); + } + } + } + } + +} diff --git a/jdk/test/java/time/test/java/time/temporal/TestOffsetDate.java b/jdk/test/java/time/test/java/time/temporal/TestOffsetDate.java new file mode 100644 index 00000000000..5ba00329b8f --- /dev/null +++ b/jdk/test/java/time/test/java/time/temporal/TestOffsetDate.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.temporal; + +import java.time.temporal.OffsetDate; + +import org.testng.annotations.Test; +import test.java.time.AbstractTest; + +/** + * Test OffsetDate. + */ +@Test +public class TestOffsetDate extends AbstractTest { + + @Test + public void test_immutable() { + assertImmutable(OffsetDate.class); + } + +} diff --git a/jdk/test/java/time/test/java/time/temporal/TestOffsetDateTime.java b/jdk/test/java/time/test/java/time/temporal/TestOffsetDateTime.java new file mode 100644 index 00000000000..9ee46725c8a --- /dev/null +++ b/jdk/test/java/time/test/java/time/temporal/TestOffsetDateTime.java @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.temporal; + +import static org.testng.Assert.assertSame; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneOffset; +import java.time.temporal.OffsetDateTime; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import test.java.time.AbstractTest; +import test.java.time.MockSimplePeriod; + +/** + * Test OffsetDateTime. + */ +@Test +public class TestOffsetDateTime extends AbstractTest { + + private static final ZoneOffset OFFSET_PONE = ZoneOffset.ofHours(1); + private static final ZoneOffset OFFSET_PTWO = ZoneOffset.ofHours(2); + private OffsetDateTime TEST_2008_6_30_11_30_59_000000500; + + @BeforeMethod(groups={"tck","implementation"}) + public void setUp() { + TEST_2008_6_30_11_30_59_000000500 = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59, 500), OFFSET_PONE); + } + + @Test + public void test_immutable() { + assertImmutable(OffsetDateTime.class); + } + + //----------------------------------------------------------------------- + // basics + //----------------------------------------------------------------------- + @DataProvider(name="sampleTimes") + Object[][] provider_sampleTimes() { + return new Object[][] { + {2008, 6, 30, 11, 30, 20, 500, OFFSET_PONE}, + {2008, 6, 30, 11, 0, 0, 0, OFFSET_PONE}, + {2008, 6, 30, 23, 59, 59, 999999999, OFFSET_PONE}, + {-1, 1, 1, 0, 0, 0, 0, OFFSET_PONE}, + }; + } + + @Test(dataProvider="sampleTimes", groups={"implementation"}) + public void test_get_same(int y, int o, int d, int h, int m, int s, int n, ZoneOffset offset) { + LocalDate localDate = LocalDate.of(y, o, d); + LocalTime localTime = LocalTime.of(h, m, s, n); + LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime); + OffsetDateTime a = OffsetDateTime.of(localDateTime, offset); + + assertSame(a.getOffset(), offset); + assertSame(a.getDate(), localDate); + assertSame(a.getTime(), localTime); + assertSame(a.getDateTime(), localDateTime); + } + + //----------------------------------------------------------------------- + // withOffsetSameLocal() + //----------------------------------------------------------------------- + @Test(groups={"implementation"}) + public void test_withOffsetSameLocal() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.withOffsetSameLocal(OFFSET_PTWO); + assertSame(test.getDateTime(), base.getDateTime()); + assertSame(test.getOffset(), OFFSET_PTWO); + } + + @Test(groups={"implementation"}) + public void test_withOffsetSameLocal_noChange() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.withOffsetSameLocal(OFFSET_PONE); + assertSame(test, base); + } + + @Test(groups={"implementation"}) + public void test_withOffsetSameInstant_noChange() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.withOffsetSameInstant(OFFSET_PONE); + assertSame(test, base); + } + + @Test(groups={"implementation"}) + public void test_withYear_noChange() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.withYear(2008); + assertSame(test, base); + } + + @Test(groups={"implementation"}) + public void test_withMonth_noChange() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.withMonth(6); + assertSame(test, base); + } + + @Test(groups={"implementation"}) + public void test_withDayOfMonth_noChange() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.withDayOfMonth(30); + assertSame(test, base); + } + + @Test(groups={"implementation"}) + public void test_withDayOfYear_noChange() { + OffsetDateTime t = TEST_2008_6_30_11_30_59_000000500.withDayOfYear(31 + 29 + 31 + 30 + 31 + 30); + assertSame(t, TEST_2008_6_30_11_30_59_000000500); + } + + @Test(groups={"implementation"}) + public void test_withHour_noChange() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.withHour(11); + assertSame(test, base); + } + + @Test(groups={"implementation"}) + public void test_withMinute_noChange() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.withMinute(30); + assertSame(test, base); + } + + @Test(groups={"implementation"}) + public void test_withSecond_noChange() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.withSecond(59); + assertSame(test, base); + } + + @Test(groups={"implementation"}) + public void test_withNanoOfSecond_noChange() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59, 1), OFFSET_PONE); + OffsetDateTime test = base.withNano(1); + assertSame(test, base); + } + + @Test(groups={"implementation"}) + public void test_plus_Period_zero() { + OffsetDateTime t = TEST_2008_6_30_11_30_59_000000500.plus(MockSimplePeriod.ZERO_DAYS); + assertSame(t, TEST_2008_6_30_11_30_59_000000500); + } + + @Test(groups={"implementation"}) + public void test_plusYears_zero() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.plusYears(0); + assertSame(test, base); + } + + @Test(groups={"implementation"}) + public void test_plusMonths_zero() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.plusMonths(0); + assertSame(test, base); + } + + @Test(groups={"implementation"}) + public void test_plusWeeks_zero() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.plusWeeks(0); + assertSame(test, base); + } + + @Test(groups={"implementation"}) + public void test_plusDays_zero() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.plusDays(0); + assertSame(test, base); + } + + @Test(groups={"implementation"}) + public void test_plusHours_zero() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.plusHours(0); + assertSame(test, base); + } + + @Test(groups={"implementation"}) + public void test_plusMinutes_zero() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.plusMinutes(0); + assertSame(test, base); + } + + @Test(groups={"implementation"}) + public void test_plusSeconds_zero() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.plusSeconds(0); + assertSame(test, base); + } + + @Test(groups={"implementation"}) + public void test_plusNanos_zero() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.plusNanos(0); + } + + @Test(groups={"implementation"}) + public void test_minus_Period_zero() { + OffsetDateTime t = TEST_2008_6_30_11_30_59_000000500.minus(MockSimplePeriod.ZERO_DAYS); + assertSame(t, TEST_2008_6_30_11_30_59_000000500); + } + + @Test(groups={"implementation"}) + public void test_minusYears_zero() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2007, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.minusYears(0); + assertSame(test, base); + } + + @Test(groups={"implementation"}) + public void test_minusMonths_zero() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.minusMonths(0); + assertSame(test, base); + } + + @Test(groups={"implementation"}) + public void test_minusWeeks_zero() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.minusWeeks(0); + assertSame(test, base); + } + + @Test(groups={"implementation"}) + public void test_minusDays_zero() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.minusDays(0); + assertSame(test, base); + } + + @Test(groups={"implementation"}) + public void test_minusHours_zero() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.minusHours(0); + assertSame(test, base); + } + + @Test(groups={"implementation"}) + public void test_minusMinutes_zero() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.minusMinutes(0); + assertSame(test, base); + } + + @Test(groups={"implementation"}) + public void test_minusSeconds_zero() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.minusSeconds(0); + assertSame(test, base); + } + + @Test(groups={"implementation"}) + public void test_minusNanos_zero() { + OffsetDateTime base = OffsetDateTime.of(LocalDate.of(2008, 6, 30), LocalTime.of(11, 30, 59), OFFSET_PONE); + OffsetDateTime test = base.minusNanos(0); + assertSame(test, base); + } + +} diff --git a/jdk/test/java/time/test/java/time/temporal/TestOffsetDateTime_instants.java b/jdk/test/java/time/test/java/time/temporal/TestOffsetDateTime_instants.java new file mode 100644 index 00000000000..7922c37589f --- /dev/null +++ b/jdk/test/java/time/test/java/time/temporal/TestOffsetDateTime_instants.java @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.temporal; + +import java.time.DateTimeException; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.Month; +import java.time.ZoneOffset; + +import java.time.temporal.OffsetDateTime; +import java.time.temporal.Year; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +/** + * Test OffsetDate creation. + */ +@Test +public class TestOffsetDateTime_instants { + + private static final ZoneOffset OFFSET_PONE = ZoneOffset.ofHours(1); + private static final ZoneOffset OFFSET_MAX = ZoneOffset.ofHours(18); + private static final ZoneOffset OFFSET_MIN = ZoneOffset.ofHours(-18); + + //----------------------------------------------------------------------- + @Test(expectedExceptions=NullPointerException.class) + public void factory_ofInstant_nullInstant() { + OffsetDateTime.ofInstant((Instant) null, OFFSET_PONE); + } + + @Test(expectedExceptions=NullPointerException.class) + public void factory_ofInstant_nullOffset() { + Instant instant = Instant.ofEpochSecond(0L); + OffsetDateTime.ofInstant(instant, (ZoneOffset) null); + } + + public void factory_ofInstant_allSecsInDay() { + for (int i = 0; i < (24 * 60 * 60); i++) { + Instant instant = Instant.ofEpochSecond(i); + OffsetDateTime test = OffsetDateTime.ofInstant(instant, OFFSET_PONE); + assertEquals(test.getYear(), 1970); + assertEquals(test.getMonth(), Month.JANUARY); + assertEquals(test.getDayOfMonth(), 1 + (i >= 23 * 60 * 60 ? 1 : 0)); + assertEquals(test.getHour(), ((i / (60 * 60)) + 1) % 24); + assertEquals(test.getMinute(), (i / 60) % 60); + assertEquals(test.getSecond(), i % 60); + } + } + + public void factory_ofInstant_allDaysInCycle() { + // sanity check using different algorithm + OffsetDateTime expected = OffsetDateTime.of(LocalDate.of(1970, 1, 1), LocalTime.of(0, 0, 0, 0), ZoneOffset.UTC); + for (long i = 0; i < 146097; i++) { + Instant instant = Instant.ofEpochSecond(i * 24L * 60L * 60L); + OffsetDateTime test = OffsetDateTime.ofInstant(instant, ZoneOffset.UTC); + assertEquals(test, expected); + expected = expected.plusDays(1); + } + } + + public void factory_ofInstant_history() { + doTest_factory_ofInstant_all(-2820, 2820); + } + + //----------------------------------------------------------------------- + public void factory_ofInstant_minYear() { + doTest_factory_ofInstant_all(Year.MIN_VALUE, Year.MIN_VALUE + 420); + } + + @Test(expectedExceptions=DateTimeException.class) + public void factory_ofInstant_tooLow() { + long days_0000_to_1970 = (146097 * 5) - (30 * 365 + 7); + int year = Year.MIN_VALUE - 1; + long days = (year * 365L + (year / 4 - year / 100 + year / 400)) - days_0000_to_1970; + Instant instant = Instant.ofEpochSecond(days * 24L * 60L * 60L); + OffsetDateTime.ofInstant(instant, ZoneOffset.UTC); + } + + public void factory_ofInstant_maxYear() { + doTest_factory_ofInstant_all(Year.MAX_VALUE - 420, Year.MAX_VALUE); + } + + @Test(expectedExceptions=DateTimeException.class) + public void factory_ofInstant_tooBig() { + long days_0000_to_1970 = (146097 * 5) - (30 * 365 + 7); + long year = Year.MAX_VALUE + 1L; + long days = (year * 365L + (year / 4 - year / 100 + year / 400)) - days_0000_to_1970; + Instant instant = Instant.ofEpochSecond(days * 24L * 60L * 60L); + OffsetDateTime.ofInstant(instant, ZoneOffset.UTC); + } + + //----------------------------------------------------------------------- + public void factory_ofInstant_minWithMinOffset() { + long days_0000_to_1970 = (146097 * 5) - (30 * 365 + 7); + int year = Year.MIN_VALUE; + long days = (year * 365L + (year / 4 - year / 100 + year / 400)) - days_0000_to_1970; + Instant instant = Instant.ofEpochSecond(days * 24L * 60L * 60L - OFFSET_MIN.getTotalSeconds()); + OffsetDateTime test = OffsetDateTime.ofInstant(instant, OFFSET_MIN); + assertEquals(test.getYear(), Year.MIN_VALUE); + assertEquals(test.getMonth().getValue(), 1); + assertEquals(test.getDayOfMonth(), 1); + assertEquals(test.getOffset(), OFFSET_MIN); + assertEquals(test.getHour(), 0); + assertEquals(test.getMinute(), 0); + assertEquals(test.getSecond(), 0); + assertEquals(test.getNano(), 0); + } + + public void factory_ofInstant_minWithMaxOffset() { + long days_0000_to_1970 = (146097 * 5) - (30 * 365 + 7); + int year = Year.MIN_VALUE; + long days = (year * 365L + (year / 4 - year / 100 + year / 400)) - days_0000_to_1970; + Instant instant = Instant.ofEpochSecond(days * 24L * 60L * 60L - OFFSET_MAX.getTotalSeconds()); + OffsetDateTime test = OffsetDateTime.ofInstant(instant, OFFSET_MAX); + assertEquals(test.getYear(), Year.MIN_VALUE); + assertEquals(test.getMonth().getValue(), 1); + assertEquals(test.getDayOfMonth(), 1); + assertEquals(test.getOffset(), OFFSET_MAX); + assertEquals(test.getHour(), 0); + assertEquals(test.getMinute(), 0); + assertEquals(test.getSecond(), 0); + assertEquals(test.getNano(), 0); + } + + public void factory_ofInstant_maxWithMinOffset() { + long days_0000_to_1970 = (146097 * 5) - (30 * 365 + 7); + int year = Year.MAX_VALUE; + long days = (year * 365L + (year / 4 - year / 100 + year / 400)) + 365 - days_0000_to_1970; + Instant instant = Instant.ofEpochSecond((days + 1) * 24L * 60L * 60L - 1 - OFFSET_MIN.getTotalSeconds()); + OffsetDateTime test = OffsetDateTime.ofInstant(instant, OFFSET_MIN); + assertEquals(test.getYear(), Year.MAX_VALUE); + assertEquals(test.getMonth().getValue(), 12); + assertEquals(test.getDayOfMonth(), 31); + assertEquals(test.getOffset(), OFFSET_MIN); + assertEquals(test.getHour(), 23); + assertEquals(test.getMinute(), 59); + assertEquals(test.getSecond(), 59); + assertEquals(test.getNano(), 0); + } + + public void factory_ofInstant_maxWithMaxOffset() { + long days_0000_to_1970 = (146097 * 5) - (30 * 365 + 7); + int year = Year.MAX_VALUE; + long days = (year * 365L + (year / 4 - year / 100 + year / 400)) + 365 - days_0000_to_1970; + Instant instant = Instant.ofEpochSecond((days + 1) * 24L * 60L * 60L - 1 - OFFSET_MAX.getTotalSeconds()); + OffsetDateTime test = OffsetDateTime.ofInstant(instant, OFFSET_MAX); + assertEquals(test.getYear(), Year.MAX_VALUE); + assertEquals(test.getMonth().getValue(), 12); + assertEquals(test.getDayOfMonth(), 31); + assertEquals(test.getOffset(), OFFSET_MAX); + assertEquals(test.getHour(), 23); + assertEquals(test.getMinute(), 59); + assertEquals(test.getSecond(), 59); + assertEquals(test.getNano(), 0); + } + + //----------------------------------------------------------------------- + @Test(expectedExceptions=DateTimeException.class) + public void factory_ofInstant_maxInstantWithMaxOffset() { + Instant instant = Instant.ofEpochSecond(Long.MAX_VALUE); + OffsetDateTime.ofInstant(instant, OFFSET_MAX); + } + + @Test(expectedExceptions=DateTimeException.class) + public void factory_ofInstant_maxInstantWithMinOffset() { + Instant instant = Instant.ofEpochSecond(Long.MAX_VALUE); + OffsetDateTime.ofInstant(instant, OFFSET_MIN); + } + + //----------------------------------------------------------------------- + private void doTest_factory_ofInstant_all(long minYear, long maxYear) { + long days_0000_to_1970 = (146097 * 5) - (30 * 365 + 7); + int minOffset = (minYear <= 0 ? 0 : 3); + int maxOffset = (maxYear <= 0 ? 0 : 3); + long minDays = (minYear * 365L + ((minYear + minOffset) / 4L - (minYear + minOffset) / 100L + (minYear + minOffset) / 400L)) - days_0000_to_1970; + long maxDays = (maxYear * 365L + ((maxYear + maxOffset) / 4L - (maxYear + maxOffset) / 100L + (maxYear + maxOffset) / 400L)) + 365L - days_0000_to_1970; + + final LocalDate maxDate = LocalDate.of(Year.MAX_VALUE, 12, 31); + OffsetDateTime expected = OffsetDateTime.of(LocalDate.of((int) minYear, 1, 1), LocalTime.of(0, 0, 0, 0), ZoneOffset.UTC); + for (long i = minDays; i < maxDays; i++) { + Instant instant = Instant.ofEpochSecond(i * 24L * 60L * 60L); + try { + OffsetDateTime test = OffsetDateTime.ofInstant(instant, ZoneOffset.UTC); + assertEquals(test, expected); + if (expected.getDate().equals(maxDate) == false) { + expected = expected.plusDays(1); + } + } catch (RuntimeException|Error ex) { + System.out.println("Error: " + i + " " + expected); + throw ex; + } + } + } + + // for performance testing + // private void doTest_factory_ofInstant_all(int minYear, int maxYear) { + // long days_0000_to_1970 = (146097 * 5) - (30 * 365 + 7); + // int minOffset = (minYear <= 0 ? 0 : 3); + // int maxOffset = (maxYear <= 0 ? 0 : 3); + // long minDays = (long) (minYear * 365L + ((minYear + minOffset) / 4L - (minYear + minOffset) / 100L + (minYear + minOffset) / 400L)) - days_0000_to_1970; + // long maxDays = (long) (maxYear * 365L + ((maxYear + maxOffset) / 4L - (maxYear + maxOffset) / 100L + (maxYear + maxOffset) / 400L)) + 365L - days_0000_to_1970; + // + // OffsetDateTime expected = OffsetDateTime.dateTime(minYear, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); + // Date cutover = new Date(Long.MIN_VALUE); + // GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); + // cal.setGregorianChange(cutover); + // for (long i = minDays; i < maxDays; i++) { + // Instant instant = Instant.instant(i * 24L * 60L * 60L); + // try { + // cal.setTimeInMillis(instant.getEpochSecond() * 1000L); + // assertEquals(cal.get(GregorianCalendar.MONTH), expected.getMonth().getValue() - 1); + // assertEquals(cal.get(GregorianCalendar.DAY_OF_MONTH), expected.getDayOfMonth().getValue()); + // expected = expected.plusDays(1); + // } catch (RuntimeException ex) { + // System.out.println("Error: " + i + " " + expected); + // throw ex; + // } catch (Error ex) { + // System.out.println("Error: " + i + " " + expected); + // throw ex; + // } + // } + // } + + //----------------------------------------------------------------------- + public void test_toInstant_19700101() { + OffsetDateTime dt = OffsetDateTime.of(LocalDate.of(1970, 1, 1), LocalTime.of(0, 0, 0, 0), ZoneOffset.UTC); + Instant test = dt.toInstant(); + assertEquals(test.getEpochSecond(), 0); + assertEquals(test.getNano(), 0); + } + + public void test_toInstant_19700101_oneNano() { + OffsetDateTime dt = OffsetDateTime.of(LocalDate.of(1970, 1, 1), LocalTime.of(0, 0, 0, 1), ZoneOffset.UTC); + Instant test = dt.toInstant(); + assertEquals(test.getEpochSecond(), 0); + assertEquals(test.getNano(), 1); + } + + public void test_toInstant_19700101_minusOneNano() { + OffsetDateTime dt = OffsetDateTime.of(LocalDate.of(1969, 12, 31), LocalTime.of(23, 59, 59, 999999999), ZoneOffset.UTC); + Instant test = dt.toInstant(); + assertEquals(test.getEpochSecond(), -1); + assertEquals(test.getNano(), 999999999); + } + + public void test_toInstant_19700102() { + OffsetDateTime dt = OffsetDateTime.of(LocalDate.of(1970, 1, 2), LocalTime.of(0, 0, 0, 0), ZoneOffset.UTC); + Instant test = dt.toInstant(); + assertEquals(test.getEpochSecond(), 24L * 60L * 60L); + assertEquals(test.getNano(), 0); + } + + public void test_toInstant_19691231() { + OffsetDateTime dt = OffsetDateTime.of(LocalDate.of(1969, 12, 31), LocalTime.of(0, 0, 0, 0), ZoneOffset.UTC); + Instant test = dt.toInstant(); + assertEquals(test.getEpochSecond(), -24L * 60L * 60L); + assertEquals(test.getNano(), 0); + } + + //----------------------------------------------------------------------- + public void test_toEpochSecond_19700101() { + OffsetDateTime dt = OffsetDateTime.of(LocalDate.of(1970, 1, 1), LocalTime.of(0, 0, 0, 0), ZoneOffset.UTC); + assertEquals(dt.toEpochSecond(), 0); + } + + public void test_toEpochSecond_19700101_oneNano() { + OffsetDateTime dt = OffsetDateTime.of(LocalDate.of(1970, 1, 1), LocalTime.of( 0, 0, 0, 1), ZoneOffset.UTC); + assertEquals(dt.toEpochSecond(), 0); + } + + public void test_toEpochSecond_19700101_minusOneNano() { + OffsetDateTime dt = OffsetDateTime.of(LocalDate.of(1969, 12, 31), LocalTime.of(23, 59, 59, 999999999), ZoneOffset.UTC); + assertEquals(dt.toEpochSecond(), -1); + } + + public void test_toEpochSecond_19700102() { + OffsetDateTime dt = OffsetDateTime.of(LocalDate.of(1970, 1, 2), LocalTime.of(0, 0, 0, 0), ZoneOffset.UTC); + assertEquals(dt.toEpochSecond(), 24L * 60L * 60L); + } + + public void test_toEpochSecond_19691231() { + OffsetDateTime dt = OffsetDateTime.of(LocalDate.of(1969, 12, 31), LocalTime.of(0, 0, 0, 0), ZoneOffset.UTC); + assertEquals(dt.toEpochSecond(), -24L * 60L * 60L); + } + +} diff --git a/jdk/test/java/time/test/java/time/temporal/TestOffsetTime.java b/jdk/test/java/time/test/java/time/temporal/TestOffsetTime.java new file mode 100644 index 00000000000..d6e9eff0b3f --- /dev/null +++ b/jdk/test/java/time/test/java/time/temporal/TestOffsetTime.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.temporal; + +import java.time.temporal.OffsetTime; + +import org.testng.annotations.Test; +import test.java.time.AbstractTest; + +/** + * Test OffsetTime. + */ +@Test +public class TestOffsetTime extends AbstractTest { + + @Test + public void test_immutable() { + assertImmutable(OffsetTime.class); + } + +} diff --git a/jdk/test/java/time/test/java/time/temporal/TestThaiBuddhistChronoImpl.java b/jdk/test/java/time/test/java/time/temporal/TestThaiBuddhistChronoImpl.java new file mode 100644 index 00000000000..9fd69cfa76b --- /dev/null +++ b/jdk/test/java/time/test/java/time/temporal/TestThaiBuddhistChronoImpl.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.temporal; + +import static org.testng.Assert.assertEquals; + +import java.util.Calendar; +import java.util.Locale; +import java.util.TimeZone; + +import java.time.LocalDate; +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoUnit; +import java.time.temporal.ChronoLocalDate; +import java.time.calendar.ThaiBuddhistChrono; + +import org.testng.annotations.Test; +import org.testng.annotations.DataProvider; + +/** + * Test. + */ +@Test +public class TestThaiBuddhistChronoImpl { + + /** + * Range of years to check consistency with java.util.Calendar + */ + @DataProvider(name="RangeVersusCalendar") + Object[][] provider_rangeVersusCalendar() { + return new Object[][] { + {LocalDate.of(1583, 1, 1), LocalDate.of(2100, 1, 1)}, + }; + } + + //----------------------------------------------------------------------- + // Verify ThaiBuddhist Calendar matches java.util.Calendar for range + //----------------------------------------------------------------------- + @Test(groups={"implementation"}, dataProvider="RangeVersusCalendar") + public void test_ThaiBuddhistChrono_vsCalendar(LocalDate isoStartDate, LocalDate isoEndDate) { + Locale locale = Locale.forLanguageTag("th-TH--u-ca-buddhist"); + assertEquals(locale.toString(), "th_TH", "Unexpected locale"); + Calendar cal = java.util.Calendar.getInstance(locale); + assertEquals(cal.getCalendarType(), "buddhist", "Unexpected calendar type"); + + ChronoLocalDate thaiDate = ThaiBuddhistChrono.INSTANCE.date(isoStartDate); + + cal.setTimeZone(TimeZone.getTimeZone("GMT+00")); + cal.set(Calendar.YEAR, thaiDate.get(ChronoField.YEAR)); + cal.set(Calendar.MONTH, thaiDate.get(ChronoField.MONTH_OF_YEAR) - 1); + cal.set(Calendar.DAY_OF_MONTH, thaiDate.get(ChronoField.DAY_OF_MONTH)); + + while (thaiDate.isBefore(isoEndDate)) { + assertEquals(thaiDate.get(ChronoField.DAY_OF_MONTH), cal.get(Calendar.DAY_OF_MONTH), "Day mismatch in " + thaiDate + "; cal: " + cal); + assertEquals(thaiDate.get(ChronoField.MONTH_OF_YEAR), cal.get(Calendar.MONTH) + 1, "Month mismatch in " + thaiDate); + assertEquals(thaiDate.get(ChronoField.YEAR_OF_ERA), cal.get(Calendar.YEAR), "Year mismatch in " + thaiDate); + + thaiDate = thaiDate.plus(1, ChronoUnit.DAYS); + cal.add(Calendar.DAY_OF_MONTH, 1); + } + } + + private String calToString(Calendar cal) { + return String.format("%04d-%02d-%02d", + cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH)); + } + +} diff --git a/jdk/test/java/time/test/java/time/temporal/TestYear.java b/jdk/test/java/time/test/java/time/temporal/TestYear.java new file mode 100644 index 00000000000..5880ac46820 --- /dev/null +++ b/jdk/test/java/time/test/java/time/temporal/TestYear.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.temporal; + +import java.time.temporal.Year; + +import org.testng.annotations.Test; +import test.java.time.AbstractTest; + +/** + * Test Year. + */ +@Test +public class TestYear extends AbstractTest { + + @Test + public void test_immutable() { + assertImmutable(Year.class); + } + +} diff --git a/jdk/test/java/time/test/java/time/temporal/TestYearMonth.java b/jdk/test/java/time/test/java/time/temporal/TestYearMonth.java new file mode 100644 index 00000000000..ab0d7f4c8cb --- /dev/null +++ b/jdk/test/java/time/test/java/time/temporal/TestYearMonth.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2008-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.temporal; + +import java.time.temporal.YearMonth; + +import org.testng.annotations.Test; +import test.java.time.AbstractTest; + +/** + * Test YearMonth. + */ +@Test +public class TestYearMonth extends AbstractTest { + + //----------------------------------------------------------------------- + @Test + public void test_immutable() { + assertImmutable(YearMonth.class); + } + +} diff --git a/jdk/test/java/time/test/java/time/zone/TestFixedZoneRules.java b/jdk/test/java/time/test/java/time/zone/TestFixedZoneRules.java new file mode 100644 index 00000000000..80569674bb6 --- /dev/null +++ b/jdk/test/java/time/test/java/time/zone/TestFixedZoneRules.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2010-2012, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package test.java.time.zone; + +import java.time.zone.*; + +import static org.testng.Assert.assertEquals; + +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; + +import org.testng.annotations.Test; + +/** + * Test ZoneRules for fixed offset time-zones. + */ +@Test +public class TestFixedZoneRules { + + private static final ZoneOffset OFFSET_PONE = ZoneOffset.ofHours(1); + + private ZoneRules make(ZoneOffset offset) { + return offset.getRules(); + } + + //----------------------------------------------------------------------- + @Test(groups="implementation") + public void test_data_nullInput() { + ZoneRules test = make(OFFSET_PONE); + assertEquals(test.getOffset((Instant) null), OFFSET_PONE); + assertEquals(test.getOffset((LocalDateTime) null), OFFSET_PONE); + assertEquals(test.getValidOffsets(null).size(), 1); + assertEquals(test.getValidOffsets(null).get(0), OFFSET_PONE); + assertEquals(test.getTransition(null), null); + assertEquals(test.getStandardOffset(null), OFFSET_PONE); + assertEquals(test.getDaylightSavings(null), Duration.ZERO); + assertEquals(test.isDaylightSavings(null), false); + assertEquals(test.nextTransition(null), null); + assertEquals(test.previousTransition(null), null); + } + +} diff --git a/jdk/test/java/time/test/java/util/TestFormatter.java b/jdk/test/java/time/test/java/util/TestFormatter.java new file mode 100644 index 00000000000..54331ed2c9b --- /dev/null +++ b/jdk/test/java/time/test/java/util/TestFormatter.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package test.java.util; + +import java.time.Instant; +import java.time.temporal.OffsetDateTime; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoField; + +import java.util.*; + +import org.testng.annotations.Test; +import static org.testng.Assert.assertEquals; + +/* @test + * @summary Unit test for j.u.Formatter threeten date/time support + */ +@Test(groups={"implementation"}) +public class TestFormatter { + + // time + private static String[] fmtStrTime = new String[] { + "H:[%tH] I:[%1$tI] k:[%1$tk] l:[%1$tl] M:[%1$tM] S:[%1$tS] L:[%1$tL] N:[%1$tN] p:[%1$tp]", + "H:[%TH] I:[%1$TI] k:[%1$Tk] l:[%1$Tl] M:[%1$TM] S:[%1$TS] L:[%1$TL] N:[%1$TN] p:[%1$Tp]", + "R:[%tR] T:[%1$tT] r:[%1$tr]", + "R:[%TR] T:[%1$TT] r:[%1$Tr]" + }; + // date + private static String[] fmtStrDate = new String[] { + "B:[%tB] b:[%1$tb] h:[%1$th] A:[%1$tA] a:[%1$ta] C:[%1$tC] Y:[%1$tY] y:[%1$ty] j:[%1$tj] m:[%1$tm] d:[%1$td] e:[%1$te]", + "B:[%TB] b:[%1$Tb] h:[%1$Th] A:[%1$TA] a:[%1$Ta] C:[%1$TC] Y:[%1$TY] y:[%1$Ty] j:[%1$Tj] m:[%1$Tm] d:[%1$Td] e:[%1$Te]", + "D:[%tD] F:[%1$tF]", + "D:[%TD] F:[%1$TF]" + }; + + private int total = 0; + private int failure = 0; + private boolean verbose = true; + + @Test + public void test () { + + int N = 12; + //locales = Locale.getAvailableLocales(); + Locale[] locales = new Locale[] { + Locale.ENGLISH, Locale.FRENCH, Locale.JAPANESE, Locale.CHINESE}; + + Random r = new Random(); + ZonedDateTime zdt = ZonedDateTime.now(); + while (N-- > 0) { + zdt = zdt.withDayOfYear(r.nextInt(365) + 1) + .with(ChronoField.SECOND_OF_DAY, r.nextInt(86400)); + Instant instant = zdt.toInstant(); + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(instant.toEpochMilli()); + + for (Locale locale : locales) { + for (String fmtStr : fmtStrDate) { + testDate(fmtStr, locale, zdt, cal); + } + for (String fmtStr : fmtStrTime) { + testTime(fmtStr, locale, zdt, cal); + } + testZoneId(locale, zdt, cal); + testInstant(locale, instant, zdt, cal); + } + } + if (verbose) { + if (failure != 0) { + System.out.println("Total " + failure + "/" + total + " tests failed"); + } else { + System.out.println("All tests (" + total + ") PASSED"); + } + } + assertEquals(failure, 0); + } + + private String getClassName(Object o) { + Class c = o.getClass(); + return c.getName().substring(c.getPackage().getName().length() + 1); + } + + private String test(String fmtStr, Locale locale, + String expected, Object dt) { + String out = new Formatter( + new StringBuilder(), locale).format(fmtStr, dt).out().toString(); + if (verbose) { + System.out.printf("%-18s : %s%n", getClassName(dt), out); + } + if (expected != null && !out.equals(expected)) { + System.out.printf("=====>%-18s : %s [ FAILED expected: %s ]%n", + getClassName(dt), out, expected); + new RuntimeException().printStackTrace(); + failure++; + } + total++; + return out; + } + + private void printFmtStr(Locale locale, String fmtStr) { + if (verbose) { + System.out.println("--------------------"); + System.out.printf("[%s, %s]%n", locale.toString(), fmtStr); + } + } + + private void testDate(String fmtStr, Locale locale, + ZonedDateTime zdt, Calendar cal) { + printFmtStr(locale, fmtStr); + String expected = test(fmtStr, locale, null, cal); + test(fmtStr, locale, expected, zdt); + test(fmtStr, locale, expected, OffsetDateTime.of(zdt)); + test(fmtStr, locale, expected, zdt.getDateTime()); + test(fmtStr, locale, expected, OffsetDateTime.of(zdt).toOffsetDate()); + test(fmtStr, locale, expected, zdt.getDate()); + } + + private void testTime(String fmtStr, Locale locale, + ZonedDateTime zdt, Calendar cal) { + printFmtStr(locale, fmtStr); + String expected = test(fmtStr, locale, null, cal); + test(fmtStr, locale, expected, zdt); + test(fmtStr, locale, expected, OffsetDateTime.of(zdt)); + test(fmtStr, locale, expected, zdt.getDateTime()); + test(fmtStr, locale, expected, OffsetDateTime.of(zdt).toOffsetTime()); + test(fmtStr, locale, expected, zdt.getTime()); + } + + private void testZoneId(Locale locale, ZonedDateTime zdt, Calendar cal) { + String fmtStr = "z:[%tz] z:[%1$Tz] Z:[%1$tZ] Z:[%1$TZ]"; + printFmtStr(locale, fmtStr); + String expected = test(fmtStr, locale, null, cal); + test(fmtStr, locale, expected, zdt); + // get a new cal with fixed tz + Calendar cal0 = Calendar.getInstance(); + cal0.setTimeInMillis(zdt.toInstant().toEpochMilli()); + cal0.setTimeZone(TimeZone.getTimeZone("GMT" + zdt.getOffset().getId())); + expected = test(fmtStr, locale, null, cal0).replaceAll("GMT", ""); + test(fmtStr, locale, expected, OffsetDateTime.of(zdt)); + test(fmtStr, locale, expected, OffsetDateTime.of(zdt).toOffsetDate()); + test(fmtStr, locale, expected, OffsetDateTime.of(zdt).toOffsetTime()); + + // datetime + zid + fmtStr = "c:[%tc] c:[%1$Tc]"; + printFmtStr(locale, fmtStr); + expected = test(fmtStr, locale, null, cal); + test(fmtStr, locale, expected, zdt); + } + + private void testInstant(Locale locale, Instant instant, + ZonedDateTime zdt, Calendar cal) { + String fmtStr = "s:[%ts] s:[%1$Ts] Q:[%1$tQ] Q:[%1$TQ]"; + printFmtStr(locale, fmtStr); + String expected = test(fmtStr, locale, null, cal); + test(fmtStr, locale, expected, instant); + test(fmtStr, locale, expected, zdt); + test(fmtStr, locale, expected, OffsetDateTime.of(zdt)); + } +} From cd509df8b65f4c04f205e2816d658d4134fa6cdc Mon Sep 17 00:00:00 2001 From: Vinnie Ryan Date: Wed, 23 Jan 2013 09:49:10 +0000 Subject: [PATCH 092/138] 8006741: javadoc cleanup for 6263419 Reviewed-by: alanb --- jdk/src/share/classes/java/security/PrivateKey.java | 3 ++- jdk/src/share/classes/javax/crypto/SecretKey.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/jdk/src/share/classes/java/security/PrivateKey.java b/jdk/src/share/classes/java/security/PrivateKey.java index 40f112aa2fd..7d8a7ea7041 100644 --- a/jdk/src/share/classes/java/security/PrivateKey.java +++ b/jdk/src/share/classes/java/security/PrivateKey.java @@ -40,7 +40,8 @@ package java.security; * sensitive key information to be destroyed, cleared, or in the case * where such information is immutable, unreferenced. * Finally, since {@code PrivateKey} is {@code Serializable}, implementations - * should also override {@link java.io.ObjectOutputStream.writeObject} + * should also override + * {@link java.io.ObjectOutputStream#writeObject(java.lang.Object)} * to prevent keys that have been destroyed from being serialized. * * @see Key diff --git a/jdk/src/share/classes/javax/crypto/SecretKey.java b/jdk/src/share/classes/javax/crypto/SecretKey.java index c32eb7108b6..e03639ab42a 100644 --- a/jdk/src/share/classes/javax/crypto/SecretKey.java +++ b/jdk/src/share/classes/javax/crypto/SecretKey.java @@ -40,7 +40,8 @@ package javax.crypto; * sensitive key information to be destroyed, cleared, or in the case * where such information is immutable, unreferenced. * Finally, since {@code SecretKey} is {@code Serializable}, implementations - * should also override {@link java.io.ObjectOutputStream.writeObject} + * should also override + * {@link java.io.ObjectOutputStream#writeObject(java.lang.Object)} * to prevent keys that have been destroyed from being serialized. * *

    Keys that implement this interface return the string {@code RAW} From 3b0f760747ae66cdd343a5e1d623af65e089b442 Mon Sep 17 00:00:00 2001 From: Alexey Utkin Date: Wed, 23 Jan 2013 15:06:49 +0400 Subject: [PATCH 093/138] 6519127: user.home property not set correctly Registry-based approach was changed to SHGetKnownFolderPath/SHGetFolderPathW Reviewed-by: alanb, anthony --- .../windows/native/java/lang/java_props_md.c | 129 +++++++----------- 1 file changed, 50 insertions(+), 79 deletions(-) diff --git a/jdk/src/windows/native/java/lang/java_props_md.c b/jdk/src/windows/native/java/lang/java_props_md.c index 5d1111efbee..3374bd44225 100644 --- a/jdk/src/windows/native/java/lang/java_props_md.c +++ b/jdk/src/windows/native/java/lang/java_props_md.c @@ -23,6 +23,11 @@ * questions. */ +/* Access APIs for Windows Vista and above */ +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0601 +#endif + #include #include #include @@ -49,8 +54,6 @@ typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO); static void SetupI18nProps(LCID lcid, char** language, char** script, char** country, char** variant, char** encoding); -#define SHELL_KEY "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders" - #define PROPSIZE 9 // eight-letter + null terminator #define SNAMESIZE 86 // max number of chars for LOCALE_SNAME is 85 @@ -173,76 +176,54 @@ getJavaIDFromLangID(LANGID langID) return ret; } -/* - * Code to figure out the user's home directory using the registry -*/ -static WCHAR* -getHomeFromRegistry() -{ - HKEY key; - int rc; - DWORD type; - WCHAR *p; - WCHAR path[MAX_PATH+1]; - int size = MAX_PATH+1; - - rc = RegOpenKeyEx(HKEY_CURRENT_USER, SHELL_KEY, 0, KEY_READ, &key); - if (rc != ERROR_SUCCESS) { - // Shell folder doesn't exist??!! - return NULL; - } - - path[0] = 0; - rc = RegQueryValueExW(key, L"Desktop", 0, &type, (LPBYTE)path, &size); - if (rc != ERROR_SUCCESS || type != REG_SZ) { - return NULL; - } - RegCloseKey(key); - /* Get the parent of Desktop directory */ - p = wcsrchr(path, L'\\'); - if (p == NULL) { - return NULL; - } - *p = L'\0'; - return _wcsdup(path); -} - /* * Code to figure out the user's home directory using shell32.dll */ WCHAR* getHomeFromShell32() { - HRESULT rc; - LPITEMIDLIST item_list = 0; - WCHAR *p; - WCHAR path[MAX_PATH+1]; - int size = MAX_PATH+1; - - rc = SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOPDIRECTORY, &item_list); - if (!SUCCEEDED(rc)) { - // we can't find the shell folder. - return NULL; - } - - path[0] = 0; - SHGetPathFromIDListW(item_list, (LPWSTR)path); - - /* Get the parent of Desktop directory */ - p = wcsrchr(path, L'\\'); - if (p) { - *p = 0; - } - /* - * We've been successful. Note that we don't free the memory allocated - * by ShGetSpecialFolderLocation. We only ever come through here once, - * and only if the registry lookup failed, so it's just not worth it. - * - * We also don't unload the SHELL32 DLL. We've paid the hit for loading - * it and we may need it again later. + * Note that we don't free the memory allocated + * by getHomeFromShell32. */ - return _wcsdup(path); + static WCHAR *u_path = NULL; + if (u_path == NULL) { + HRESULT hr; + + /* + * SHELL32 DLL is delay load DLL and we can use the trick with + * __try/__except block. + */ + __try { + /* + * For Windows Vista and later (or patched MS OS) we need to use + * [SHGetKnownFolderPath] call to avoid MAX_PATH length limitation. + * Shell32.dll (version 6.0.6000 or later) + */ + hr = SHGetKnownFolderPath(&FOLDERID_Profile, KF_FLAG_DONT_VERIFY, NULL, &u_path); + } __except(EXCEPTION_EXECUTE_HANDLER) { + /* Exception: no [SHGetKnownFolderPath] entry */ + hr = E_FAIL; + } + + if (FAILED(hr)) { + WCHAR path[MAX_PATH+1]; + + /* fallback solution for WinXP and Windows 2000 */ + hr = SHGetFolderPathW(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, path); + if (FAILED(hr)) { + /* we can't find the shell folder. */ + u_path = NULL; + } else { + /* Just to be sure about the path length until Windows Vista approach. + * [S_FALSE] could not be returned due to [CSIDL_FLAG_DONT_VERIFY] flag and UNICODE version. + */ + path[MAX_PATH] = 0; + u_path = _wcsdup(path); + } + } + } + return u_path; } static boolean @@ -336,7 +317,7 @@ GetJavaProperties(JNIEnv* env) OSVERSIONINFOEX ver; - if (sprops.user_dir) { + if (sprops.line_separator) { return &sprops; } @@ -538,15 +519,7 @@ GetJavaProperties(JNIEnv* env) } /* - * Home directory/ - * - * We first look under a standard registry key. If that fails we - * fall back on using a SHELL32.DLL API. If that fails we use a - * default value. - * - * Note: To save space we want to avoid loading SHELL32.DLL - * unless really necessary. However if we do load it, we leave it - * in memory, as it may be needed again later. + * Home directory * * The normal result is that for a given user name XXX: * On multi-user NT, user.home gets set to c:\winnt\profiles\XXX. @@ -554,13 +527,11 @@ GetJavaProperties(JNIEnv* env) * On single-user Win95, user.home gets set to c:\windows. */ { - WCHAR *homep = getHomeFromRegistry(); + WCHAR *homep = getHomeFromShell32(); if (homep == NULL) { - homep = getHomeFromShell32(); - if (homep == NULL) - homep = L"C:\\"; + homep = L"C:\\"; } - sprops.user_home = _wcsdup(homep); + sprops.user_home = homep; } /* From 12e480040aa80d22e9d4f0ba04ef83b641e70352 Mon Sep 17 00:00:00 2001 From: Chris Hegarty Date: Wed, 23 Jan 2013 14:45:44 +0000 Subject: [PATCH 094/138] 8006669: sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/PostThruProxy.sh fails on mac Reviewed-by: alanb --- .../https/HttpsURLConnection/PostThruProxy.java | 11 +++++------ .../HttpsURLConnection/PostThruProxyWithAuth.java | 14 ++++++-------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/PostThruProxy.java b/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/PostThruProxy.java index a521db2911d..26c4f400b83 100644 --- a/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/PostThruProxy.java +++ b/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/PostThruProxy.java @@ -153,7 +153,7 @@ public class PostThruProxy { /* * setup up a proxy */ - setupProxy(); + SocketAddress pAddr = setupProxy(); /* * we want to avoid URLspoofCheck failures in cases where the cert @@ -163,7 +163,8 @@ public class PostThruProxy { new NameVerifier()); URL url = new URL("https://" + hostname+ ":" + serverPort); - HttpsURLConnection https = (HttpsURLConnection)url.openConnection(); + Proxy p = new Proxy(Proxy.Type.HTTP, pAddr); + HttpsURLConnection https = (HttpsURLConnection)url.openConnection(p); https.setDoOutput(true); https.setRequestMethod("POST"); PrintStream ps = null; @@ -200,14 +201,12 @@ public class PostThruProxy { } } - static void setupProxy() throws IOException { + static SocketAddress setupProxy() throws IOException { ProxyTunnelServer pserver = new ProxyTunnelServer(); // disable proxy authentication pserver.needUserAuth(false); pserver.start(); - System.setProperty("https.proxyHost", "localhost"); - System.setProperty("https.proxyPort", String.valueOf( - pserver.getPort())); + return new InetSocketAddress("localhost", pserver.getPort()); } } diff --git a/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/PostThruProxyWithAuth.java b/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/PostThruProxyWithAuth.java index a010be0630c..11fe859223c 100644 --- a/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/PostThruProxyWithAuth.java +++ b/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/PostThruProxyWithAuth.java @@ -152,7 +152,7 @@ public class PostThruProxyWithAuth { /* * setup up a proxy */ - setupProxy(); + SocketAddress pAddr = setupProxy(); /* * we want to avoid URLspoofCheck failures in cases where the cert @@ -162,7 +162,8 @@ public class PostThruProxyWithAuth { new NameVerifier()); URL url = new URL("https://" + hostname + ":" + serverPort); - HttpsURLConnection https = (HttpsURLConnection)url.openConnection(); + Proxy p = new Proxy(Proxy.Type.HTTP, pAddr); + HttpsURLConnection https = (HttpsURLConnection)url.openConnection(p); https.setDoOutput(true); https.setRequestMethod("POST"); PrintStream ps = null; @@ -195,7 +196,7 @@ public class PostThruProxyWithAuth { } } - static void setupProxy() throws IOException { + static SocketAddress setupProxy() throws IOException { ProxyTunnelServer pserver = new ProxyTunnelServer(); /* @@ -209,9 +210,8 @@ public class PostThruProxyWithAuth { pserver.setUserAuth("Test", "test123"); pserver.start(); - System.setProperty("https.proxyHost", "localhost"); - System.setProperty("https.proxyPort", String.valueOf( - pserver.getPort())); + + return new InetSocketAddress("localhost", pserver.getPort()); } public static class TestAuthenticator extends Authenticator { @@ -220,6 +220,4 @@ public class PostThruProxyWithAuth { "test123".toCharArray()); } } - - } From 5f7ebf8d51e5884afc4bde75d40f0b96fbc9ec37 Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Wed, 23 Jan 2013 15:12:28 +0000 Subject: [PATCH 095/138] 8006764: FunctionalInterface missing from rt.jar (old build) Reviewed-by: lancea, forax --- jdk/make/java/java/FILES_java.gmk | 1 + 1 file changed, 1 insertion(+) diff --git a/jdk/make/java/java/FILES_java.gmk b/jdk/make/java/java/FILES_java.gmk index ea004fd7212..bf0f9833670 100644 --- a/jdk/make/java/java/FILES_java.gmk +++ b/jdk/make/java/java/FILES_java.gmk @@ -137,6 +137,7 @@ JAVA_JAVA_java = \ java/lang/Appendable.java \ java/lang/Comparable.java \ java/lang/Readable.java \ + java/lang/FunctionalInterface.java \ java/lang/Override.java \ java/lang/SafeVarargs.java \ java/lang/SuppressWarnings.java \ From 6c5679d418b06406b8307e75466712bbacc2ff9f Mon Sep 17 00:00:00 2001 From: Rob McKenna Date: Wed, 23 Jan 2013 17:54:34 +0000 Subject: [PATCH 096/138] 8004729: Add java.lang.reflect.Parameter and related changes for parameter reflection Reviewed-by: darcy, forax, psandoz, dholmes, tbell --- jdk/make/java/java/Exportedfiles.gmk | 4 +- jdk/make/java/java/FILES_c.gmk | 3 +- jdk/make/java/java/mapfile-vers | 3 +- jdk/makefiles/mapfiles/libjava/mapfile-vers | 3 +- .../java/lang/reflect/Constructor.java | 7 +- .../classes/java/lang/reflect/Executable.java | 69 +++- .../classes/java/lang/reflect/Method.java | 8 +- .../classes/java/lang/reflect/Modifier.java | 9 +- .../classes/java/lang/reflect/Parameter.java | 305 ++++++++++++++++++ jdk/src/share/javavm/export/jvm.h | 9 +- .../native/java/lang/reflect/Executable.c | 38 +++ .../reflect/Parameter/WithParameters.java | 246 ++++++++++++++ .../reflect/Parameter/WithoutParameters.java | 185 +++++++++++ 13 files changed, 879 insertions(+), 10 deletions(-) create mode 100644 jdk/src/share/classes/java/lang/reflect/Parameter.java create mode 100644 jdk/src/share/native/java/lang/reflect/Executable.c create mode 100644 jdk/test/java/lang/reflect/Parameter/WithParameters.java create mode 100644 jdk/test/java/lang/reflect/Parameter/WithoutParameters.java diff --git a/jdk/make/java/java/Exportedfiles.gmk b/jdk/make/java/java/Exportedfiles.gmk index f6d3c3ae1de..6b6cc3a2201 100644 --- a/jdk/make/java/java/Exportedfiles.gmk +++ b/jdk/make/java/java/Exportedfiles.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -57,6 +57,7 @@ FILES_export = \ java/lang/reflect/Constructor.java \ java/lang/reflect/InvocationTargetException.java \ java/lang/reflect/Array.java \ + java/lang/reflect/Executable.java \ java/lang/reflect/Proxy.java \ java/security/AccessController.java \ java/util/Date.java \ @@ -129,6 +130,7 @@ FILES_export = \ java/lang/reflect/Constructor.java \ java/lang/reflect/InvocationTargetException.java \ java/lang/reflect/Array.java \ + java/lang/reflect/Executable.java \ java/lang/reflect/Proxy.java \ java/lang/ref/Reference.java \ java/lang/ref/Finalizer.java \ diff --git a/jdk/make/java/java/FILES_c.gmk b/jdk/make/java/java/FILES_c.gmk index aa48464771b..617780a42af 100644 --- a/jdk/make/java/java/FILES_c.gmk +++ b/jdk/make/java/java/FILES_c.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -32,6 +32,7 @@ FILES_c = \ Compiler.c \ Console_md.c \ Double.c \ + Executable.c \ FileDescriptor_md.c \ FileInputStream.c \ FileInputStream_md.c \ diff --git a/jdk/make/java/java/mapfile-vers b/jdk/make/java/java/mapfile-vers index 97938361c99..81e678d7624 100644 --- a/jdk/make/java/java/mapfile-vers +++ b/jdk/make/java/java/mapfile-vers @@ -1,5 +1,5 @@ # -# Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -189,6 +189,7 @@ SUNWprivate_1.1 { Java_java_lang_reflect_Array_setInt; Java_java_lang_reflect_Array_setLong; Java_java_lang_reflect_Array_setShort; + Java_java_lang_reflect_Executable_getParameters0; Java_java_lang_Runtime_freeMemory; Java_java_lang_Runtime_maxMemory; Java_java_lang_Runtime_gc; diff --git a/jdk/makefiles/mapfiles/libjava/mapfile-vers b/jdk/makefiles/mapfiles/libjava/mapfile-vers index 97938361c99..81e678d7624 100644 --- a/jdk/makefiles/mapfiles/libjava/mapfile-vers +++ b/jdk/makefiles/mapfiles/libjava/mapfile-vers @@ -1,5 +1,5 @@ # -# Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -189,6 +189,7 @@ SUNWprivate_1.1 { Java_java_lang_reflect_Array_setInt; Java_java_lang_reflect_Array_setLong; Java_java_lang_reflect_Array_setShort; + Java_java_lang_reflect_Executable_getParameters0; Java_java_lang_Runtime_freeMemory; Java_java_lang_Runtime_maxMemory; Java_java_lang_Runtime_gc; diff --git a/jdk/src/share/classes/java/lang/reflect/Constructor.java b/jdk/src/share/classes/java/lang/reflect/Constructor.java index 73e4d271e17..1973e820950 100644 --- a/jdk/src/share/classes/java/lang/reflect/Constructor.java +++ b/jdk/src/share/classes/java/lang/reflect/Constructor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -203,6 +203,11 @@ public final class Constructor extends Executable { return parameterTypes.clone(); } + /** + * {@inheritDoc} + */ + public int getParameterCount() { return parameterTypes.length; } + /** * {@inheritDoc} * @throws GenericSignatureFormatError {@inheritDoc} diff --git a/jdk/src/share/classes/java/lang/reflect/Executable.java b/jdk/src/share/classes/java/lang/reflect/Executable.java index 724b4838fe7..f2befd2dcdd 100644 --- a/jdk/src/share/classes/java/lang/reflect/Executable.java +++ b/jdk/src/share/classes/java/lang/reflect/Executable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -225,6 +225,18 @@ public abstract class Executable extends AccessibleObject */ public abstract Class[] getParameterTypes(); + /** + * Returns the number of formal parameters (including any + * synthetic or synthesized parameters) for the executable + * represented by this object. + * + * @return The number of formal parameters for the executable this + * object represents + */ + public int getParameterCount() { + throw new AbstractMethodError(); + } + /** * Returns an array of {@code Type} objects that represent the formal * parameter types, in declaration order, of the executable represented by @@ -258,6 +270,60 @@ public abstract class Executable extends AccessibleObject return getParameterTypes(); } + /** + * Returns an array of {@code Parameter} objects that represent + * all the parameters to the underlying executable represented by + * this object. Returns an array of length 0 if the executable + * has no parameters. + * + * @return an array of {@code Parameter} objects representing all + * the parameters to the executable this object represents + */ + public Parameter[] getParameters() { + // TODO: This may eventually need to be guarded by security + // mechanisms similar to those in Field, Method, etc. + // + // Need to copy the cached array to prevent users from messing + // with it. Since parameters are immutable, we can + // shallow-copy. + return privateGetParameters().clone(); + } + + private Parameter[] synthesizeAllParams() { + final int realparams = getParameterCount(); + final Parameter[] out = new Parameter[realparams]; + for (int i = 0; i < realparams; i++) + // TODO: is there a way to synthetically derive the + // modifiers? Probably not in the general case, since + // we'd have no way of knowing about them, but there + // may be specific cases. + out[i] = new Parameter("arg" + i, 0, this, i); + return out; + } + + private Parameter[] privateGetParameters() { + // Use tmp to avoid multiple writes to a volatile. + Parameter[] tmp = parameters; + + if (tmp == null) { + + // Otherwise, go to the JVM to get them + tmp = getParameters0(); + + // If we get back nothing, then synthesize parameters + if (tmp == null) + tmp = synthesizeAllParams(); + + parameters = tmp; + } + + return tmp; + } + + private transient volatile Parameter[] parameters; + + private native Parameter[] getParameters0(); + /** * Returns an array of {@code Class} objects that represent the * types of exceptions declared to be thrown by the underlying @@ -403,4 +469,5 @@ public abstract class Executable extends AccessibleObject } return declaredAnnotations; } + } diff --git a/jdk/src/share/classes/java/lang/reflect/Method.java b/jdk/src/share/classes/java/lang/reflect/Method.java index f67ca224447..a7beb011400 100644 --- a/jdk/src/share/classes/java/lang/reflect/Method.java +++ b/jdk/src/share/classes/java/lang/reflect/Method.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -253,6 +253,12 @@ public final class Method extends Executable { return parameterTypes.clone(); } + /** + * {@inheritDoc} + */ + public int getParameterCount() { return parameterTypes.length; } + + /** * {@inheritDoc} * @throws GenericSignatureFormatError {@inheritDoc} diff --git a/jdk/src/share/classes/java/lang/reflect/Modifier.java b/jdk/src/share/classes/java/lang/reflect/Modifier.java index 3d0eed2c2cc..24cebe29f9f 100644 --- a/jdk/src/share/classes/java/lang/reflect/Modifier.java +++ b/jdk/src/share/classes/java/lang/reflect/Modifier.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -340,12 +340,17 @@ class Modifier { static final int BRIDGE = 0x00000040; static final int VARARGS = 0x00000080; static final int SYNTHETIC = 0x00001000; - static final int ANNOTATION= 0x00002000; + static final int ANNOTATION = 0x00002000; static final int ENUM = 0x00004000; + static final int SYNTHESIZED = 0x00010000; static boolean isSynthetic(int mod) { return (mod & SYNTHETIC) != 0; } + static boolean isSynthesized(int mod) { + return (mod & SYNTHESIZED) != 0; + } + /** * See JLSv3 section 8.1.1. */ diff --git a/jdk/src/share/classes/java/lang/reflect/Parameter.java b/jdk/src/share/classes/java/lang/reflect/Parameter.java new file mode 100644 index 00000000000..6eed6926be0 --- /dev/null +++ b/jdk/src/share/classes/java/lang/reflect/Parameter.java @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package java.lang.reflect; + +import java.lang.annotation.*; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import sun.reflect.annotation.AnnotationSupport; + +/** + * Information about method parameters. + * + * A {@code Parameter} provides information about method parameters, + * including its name and modifiers. It also provides an alternate + * means of obtaining attributes for the parameter. + * + * @since 1.8 + */ +public final class Parameter implements AnnotatedElement { + + private final String name; + private final int modifiers; + private final Executable executable; + private int index; + + /** + * Package-private constructor for {@code Parameter}. + * + * If method parameter data is present in the classfile, then the + * JVM creates {@code Parameter} objects directly. If it is + * absent, however, then {@code Executable} uses this constructor + * to synthesize them. + * + * @param name The name of the parameter. + * @param modifiers The modifier flags for the parameter. + * @param executable The executable which defines this parameter. + * @param index The index of the parameter. + */ + Parameter(String name, + int modifiers, + Executable executable, + int index) { + this.name = name; + this.modifiers = modifiers; + this.executable = executable; + this.index = index; + } + + /** + * Compares based on the executable and the index. + * + * @param obj The object to compare. + * @return Whether or not this is equal to the argument. + */ + public boolean equals(Object obj) { + if(obj instanceof Parameter) { + Parameter other = (Parameter)obj; + return (other.executable.equals(executable) && + other.index == index); + } + return false; + } + + /** + * Returns a hash code based on the executable's hash code and the + * index. + * + * @return A hash code based on the executable's hash code. + */ + public int hashCode() { + return executable.hashCode() ^ index; + } + + /** + * Returns a string representation of the parameter's modifiers, + * its attributes, its type, its name, and a trailing ... if it is + * a variadic parameter. + * + * @return A string representation of the parameter and associated + * information. + */ + public String toString() { + final StringBuilder sb = new StringBuilder(); + final Type type = getParameterizedType(); + final String typename = (type instanceof Class)? + Field.getTypeName((Class)type): + (type.toString()); + + sb.append(Modifier.toString(getModifiers())); + sb.append(" "); + + if(isVarArgs()) + sb.append(typename.replaceFirst("\\[\\]$", "...")); + else + sb.append(typename); + + sb.append(" "); + sb.append(name); + + return sb.toString(); + } + + /** + * Return the {@code Executable} which declares this parameter. + * + * @return The {@code Executable} declaring this parameter. + */ + public Executable getDeclaringExecutable() { + return executable; + } + + /** + * Get the modifier flags for this the parameter represented by + * this {@code Parameter} object. + * + * @return The modifier flags for this parameter. + */ + public int getModifiers() { + return modifiers; + } + + /** + * Returns the name of the parameter represented by this + * {@code Parameter} object. + */ + public String getName() { + return name; + } + + /** + * Returns a {@code Type} object that identifies the parameterized + * type for the parameter represented by this {@code Parameter} + * object. + * + * @return a {@code Type} object identifying the parameterized + * type of the parameter represented by this object + */ + public Type getParameterizedType() { + Type tmp = parameterTypeCache; + if (null == tmp) { + tmp = executable.getGenericParameterTypes()[index]; + parameterTypeCache = tmp; + } + + return tmp; + } + + private transient volatile Type parameterTypeCache = null; + + /** + * Returns a {@code Class} object that identifies the + * declared type for the parameter represented by this + * {@code Parameter} object. + * + * @return a {@code Class} object identifying the declared + * type of the parameter represented by this object + */ + public Class getType() { + Class tmp = parameterClassCache; + if (null == tmp) { + tmp = executable.getParameterTypes()[index]; + parameterClassCache = tmp; + } + return tmp; + } + + private transient volatile Class parameterClassCache = null; + + /** + * Returns {@code true} if this parameter is a synthesized + * construct; returns {@code false} otherwise. + * + * @return true if and only if this parameter is a synthesized + * construct as defined by + * The Java™ Language Specification. + */ + public boolean isSynthesized() { + return Modifier.isSynthesized(getModifiers()); + } + + /** + * Returns {@code true} if this parameter is a synthetic + * construct; returns {@code false} otherwise. + * + * @jls 13.1 The Form of a Binary + * @return true if and only if this parameter is a synthetic + * construct as defined by + * The Java™ Language Specification. + */ + public boolean isSynthetic() { + return Modifier.isSynthetic(getModifiers()); + } + + /** + * Returns {@code true} if this parameter represents a variable + * argument list; returns {@code false} otherwise. + * + * @return {@code true} if an only if this parameter represents a + * variable argument list. + */ + public boolean isVarArgs() { + return executable.isVarArgs() && + index == executable.getParameterCount() - 1; + } + + + /** + * {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ + public T getAnnotation(Class annotationClass) { + Objects.requireNonNull(annotationClass); + + return AnnotationSupport.getOneAnnotation(declaredAnnotations(), annotationClass); + } + + /** + * {@inheritDoc} + * @throws NullPointerException {@inheritDoc} + */ + public T[] getAnnotations(Class annotationClass) { + Objects.requireNonNull(annotationClass); + + return AnnotationSupport.getMultipleAnnotations(declaredAnnotations(), annotationClass); + } + + /** + * {@inheritDoc} + */ + public Annotation[] getDeclaredAnnotations() { + return executable.getParameterAnnotations()[index]; + } + + /** + * @throws NullPointerException {@inheritDoc} + */ + public T getDeclaredAnnotation(Class annotationClass) { + // Only annotations on classes are inherited, for all other + // objects getDeclaredAnnotation is the same as + // getAnnotation. + return getAnnotation(annotationClass); + } + + /** + * @throws NullPointerException {@inheritDoc} + */ + public T[] getDeclaredAnnotations(Class annotationClass) { + // Only annotations on classes are inherited, for all other + // objects getDeclaredAnnotations is the same as + // getAnnotations. + return getAnnotations(annotationClass); + } + + /** + * {@inheritDoc} + */ + public Annotation[] getAnnotations() { + return getDeclaredAnnotations(); + } + + /** + * @throws NullPointerException {@inheritDoc} + */ + public boolean isAnnotationPresent( + Class annotationClass) { + return getAnnotation(annotationClass) != null; + } + + private transient Map, Annotation> declaredAnnotations; + + private synchronized Map, Annotation> declaredAnnotations() { + if(null == declaredAnnotations) { + declaredAnnotations = + new HashMap, Annotation>(); + Annotation[] ann = getDeclaredAnnotations(); + for(int i = 0; i < ann.length; i++) + declaredAnnotations.put(ann[i].annotationType(), ann[i]); + } + return declaredAnnotations; + } + +} diff --git a/jdk/src/share/javavm/export/jvm.h b/jdk/src/share/javavm/export/jvm.h index 7914a1c534a..f25a34e8f2b 100644 --- a/jdk/src/share/javavm/export/jvm.h +++ b/jdk/src/share/javavm/export/jvm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -552,6 +552,13 @@ JNIEXPORT jstring JNICALL JVM_ConstantPoolGetStringAt JNIEXPORT jstring JNICALL JVM_ConstantPoolGetUTF8At (JNIEnv *env, jobject unused, jobject jcpool, jint index); +/* + * Parameter reflection + */ + +JNIEXPORT jobjectArray JNICALL +JVM_GetMethodParameters(JNIEnv *env, jobject method); + /* * java.security.* */ diff --git a/jdk/src/share/native/java/lang/reflect/Executable.c b/jdk/src/share/native/java/lang/reflect/Executable.c new file mode 100644 index 00000000000..f6133e8d812 --- /dev/null +++ b/jdk/src/share/native/java/lang/reflect/Executable.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include + +#include "jni.h" +#include "jni_util.h" +#include "jvm.h" +#include "java_lang_reflect_Executable.h" + +JNIEXPORT jobject JNICALL +Java_java_lang_reflect_Executable_getParameters0(JNIEnv *env, + jobject method) { + return JVM_GetMethodParameters(env, method); +} diff --git a/jdk/test/java/lang/reflect/Parameter/WithParameters.java b/jdk/test/java/lang/reflect/Parameter/WithParameters.java new file mode 100644 index 00000000000..c4ffec6de30 --- /dev/null +++ b/jdk/test/java/lang/reflect/Parameter/WithParameters.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @ignore + * @test + * @compile -parameters WithParameters.java + * @run main WithParameters + * @summary javac should generate method parameters correctly. + */ + +import java.lang.*; +import java.lang.annotation.*; +import java.lang.reflect.*; +import java.util.List; + +public class WithParameters { + + private static final Class[] qux_types = { + int.class, Foo.class, List.class, List.class, List.class, String[].class + }; + + private static final String[] qux_names = { + "quux", "quuux", "l", "l2", "l3", "rest" + }; + + public static void main(String argv[]) throws Exception { + int error = 0; + Method[] methods = Foo.class.getMethods(); + for(Method m : methods) { + System.err.println("Inspecting method " + m.getName()); + Parameter[] parameters = m.getParameters(); + if(parameters == null) + throw new Exception("getParameters should never be null"); + for(int i = 0; i < parameters.length; i++) { + Parameter p = parameters[i]; + if(!p.getDeclaringExecutable().equals(m)) { + System.err.println(p + ".getDeclaringExecutable != " + m); + error++; + } + if(null == p.getType()) { + System.err.println(p + ".getType() == null"); + error++; + } + if(null == p.getParameterizedType()) { + System.err.println(p + ".getParameterizedType == null"); + error++; + } + } + if(m.getName().equals("qux")) { + if(6 != parameters.length) { + System.err.println("Wrong number of parameters for qux"); + error++; + } + for(int i = 0; i < parameters.length; i++) { + Parameter p = parameters[i]; + if(!parameters[i].getName().equals(qux_names[i])) { + System.err.println("Wrong parameter name for " + parameters[i]); + error++; + } + // The getType family work with or without + // parameter attributes compiled in. + if(!parameters[i].getType().equals(qux_types[i])) { + System.err.println("Wrong parameter type for " + parameters[0] + ": expected " + qux_types[i] + ", but got " + parameters[i].getType()); + error++; + } + } + if(parameters[0].toString().equals("int quux")) { + System.err.println("toString for quux is wrong"); + error++; + } + if(parameters[0].getModifiers() != Modifier.FINAL) { + System.err.println("quux is not final"); + error++; + } + if(parameters[0].isVarArgs()) { + System.err.println("isVarArg for quux is wrong"); + error++; + } + if(!parameters[0].getParameterizedType().equals(int.class)) { + System.err.println("getParameterizedType for quux is wrong"); + error++; + } + if(parameters[1].toString().equals("WithParameters$Foo quuux")) { + System.err.println("toString for quuux is wrong"); + error++; + } + if(parameters[1].isVarArgs()) { + System.err.println("isVarArg for quuux is wrong"); + error++; + } + if(!parameters[1].getParameterizedType().equals(Foo.class)) { + System.err.println("getParameterizedType for quuux is wrong"); + error++; + } + Annotation[] anns = parameters[1].getAnnotations(); + if(1 != anns.length) { + System.err.println("getAnnotations missed an annotation"); + error++; + } else if(!anns[0].annotationType().equals(Thing.class)) { + System.err.println("getAnnotations has the wrong annotation"); + error++; + } + if(parameters[2].toString().equals("java.util.List quuux")) { + System.err.println("toString for l is wrong"); + error++; + } + if(parameters[2].isVarArgs()) { + System.err.println("isVarArg for l is wrong"); + error++; + } + if(!(parameters[2].getParameterizedType() instanceof + ParameterizedType)) { + System.err.println("getParameterizedType for l is wrong"); + error++; + } else { + ParameterizedType pt = + (ParameterizedType) parameters[2].getParameterizedType(); + if(!pt.getRawType().equals(List.class)) { + System.err.println("Raw type for l is wrong"); + error++; + } + if(1 != pt.getActualTypeArguments().length) { + System.err.println("Number of type parameters for l is wrong"); + error++; + } + if(!(pt.getActualTypeArguments()[0] instanceof WildcardType)) { + System.err.println("Type parameter for l is wrong"); + error++; + } + } + if(parameters[3].toString().equals("java.util.List l")) { + System.err.println("toString for l2 is wrong"); + error++; + } + if(parameters[3].isVarArgs()) { + System.err.println("isVarArg for l2 is wrong"); + error++; + } + if(!(parameters[3].getParameterizedType() instanceof + ParameterizedType)) { + System.err.println("getParameterizedType for l2 is wrong"); + error++; + } else { + ParameterizedType pt = + (ParameterizedType) parameters[3].getParameterizedType(); + if(!pt.getRawType().equals(List.class)) { + System.err.println("Raw type for l2 is wrong"); + error++; + } + if(1 != pt.getActualTypeArguments().length) { + System.err.println("Number of type parameters for l2 is wrong"); + error++; + } + if(!(pt.getActualTypeArguments()[0].equals(Foo.class))) { + System.err.println("Type parameter for l2 is wrong"); + error++; + } + } + if(parameters[4].toString().equals("java.util.List l")) { + System.err.println("toString for l3 is wrong"); + error++; + } + if(parameters[4].isVarArgs()) { + System.err.println("isVarArg for l3 is wrong"); + error++; + } + if(!(parameters[4].getParameterizedType() instanceof + ParameterizedType)) { + System.err.println("getParameterizedType for l3 is wrong"); + error++; + } else { + ParameterizedType pt = + (ParameterizedType) parameters[4].getParameterizedType(); + if(!pt.getRawType().equals(List.class)) { + System.err.println("Raw type for l3 is wrong"); + error++; + } + if(1 != pt.getActualTypeArguments().length) { + System.err.println("Number of type parameters for l3 is wrong"); + error++; + } + if(!(pt.getActualTypeArguments()[0] instanceof WildcardType)) { + System.err.println("Type parameter for l3 is wrong"); + error++; + } else { + WildcardType wt = (WildcardType) + pt.getActualTypeArguments()[0]; + if(!wt.getUpperBounds()[0].equals(Foo.class)) { + System.err.println("Upper bounds on type parameter fol l3 is wrong"); + error++; + } + } + } + if(parameters[5].toString().equals("java.lang.String... rest")) { + System.err.println("toString for l is wrong"); + error++; + } + if(!parameters[5].isVarArgs()) { + System.err.println("isVarArg for rest is wrong"); + error++; + } + if(!(parameters[5].getParameterizedType().equals(String[].class))) { + System.err.println("getParameterizedType for rest is wrong"); + error++; + } + } + } + if(0 != error) + throw new Exception("Failed " + error + " tests"); + } + + void test(int test) {} + + public class Foo { + int thing; + public void qux(final int quux, @Thing Foo quuux, + List l, List l2, + List l3, + String... rest) {} + } + + @Retention(RetentionPolicy.RUNTIME) + public @interface Thing {} + +} diff --git a/jdk/test/java/lang/reflect/Parameter/WithoutParameters.java b/jdk/test/java/lang/reflect/Parameter/WithoutParameters.java new file mode 100644 index 00000000000..39be21471aa --- /dev/null +++ b/jdk/test/java/lang/reflect/Parameter/WithoutParameters.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary javac should generate method parameters correctly. + */ + +import java.lang.*; +import java.lang.reflect.*; +import java.util.List; + +public class WithoutParameters { + + private static final Class[] qux_types = { + int.class, + Foo.class, + List.class, + List.class, + List.class, + String[].class + }; + + public static void main(String argv[]) throws Exception { + int error = 0; + Method[] methods = Foo.class.getMethods(); + for(Method m : methods) { + System.err.println("Inspecting method " + m); + Parameter[] parameters = m.getParameters(); + if(parameters == null) + throw new Exception("getParameters should never be null"); + for(int i = 0; i < parameters.length; i++) { + Parameter p = parameters[i]; + if(!p.getDeclaringExecutable().equals(m)) { + System.err.println(p + ".getDeclaringExecutable != " + m); + error++; + } + if(null == p.getType()) { + System.err.println(p + ".getType() == null"); + error++; + } + if(null == p.getParameterizedType()) { + System.err.println(p + ".getParameterizedType == null"); + error++; + } + } + if(m.getName().equals("qux")) { + if(6 != parameters.length) { + System.err.println("Wrong number of parameters for qux"); + error++; + } + for(int i = 0; i < parameters.length; i++) { + Parameter p = parameters[i]; + // The getType family work with or without + // parameter attributes compiled in. + if(!parameters[i].getType().equals(qux_types[i])) { + System.err.println("Wrong parameter type for " + parameters[0] + ": expected " + qux_types[i] + ", but got " + parameters[i].getType()); + error++; + } + } + if(!parameters[0].getParameterizedType().equals(int.class)) { + System.err.println("getParameterizedType for quux is wrong"); + error++; + } + if(!parameters[1].getParameterizedType().equals(Foo.class)) { + System.err.println("getParameterizedType for quux is wrong"); + error++; + } + if(!(parameters[2].getParameterizedType() instanceof + ParameterizedType)) { + System.err.println("getParameterizedType for l is wrong"); + error++; + } else { + ParameterizedType pt = + (ParameterizedType) parameters[2].getParameterizedType(); + if(!pt.getRawType().equals(List.class)) { + System.err.println("Raw type for l is wrong"); + error++; + } + if(1 != pt.getActualTypeArguments().length) { + System.err.println("Number of type parameters for l is wrong"); + error++; + } + if(!(pt.getActualTypeArguments()[0] instanceof WildcardType)) { + System.err.println("Type parameter for l is wrong"); + error++; + } + } + if(!(parameters[3].getParameterizedType() instanceof + ParameterizedType)) { + System.err.println("getParameterizedType for l2 is wrong"); + error++; + } else { + ParameterizedType pt = + (ParameterizedType) parameters[3].getParameterizedType(); + if(!pt.getRawType().equals(List.class)) { + System.err.println("Raw type for l2 is wrong"); + error++; + } + if(1 != pt.getActualTypeArguments().length) { + System.err.println("Number of type parameters for l2 is wrong"); + error++; + } + if(!(pt.getActualTypeArguments()[0].equals(Foo.class))) { + System.err.println("Type parameter for l2 is wrong"); + error++; + } + } + if(!(parameters[4].getParameterizedType() instanceof + ParameterizedType)) { + System.err.println("getParameterizedType for l3 is wrong"); + error++; + } else { + ParameterizedType pt = + (ParameterizedType) parameters[4].getParameterizedType(); + if(!pt.getRawType().equals(List.class)) { + System.err.println("Raw type for l3 is wrong"); + error++; + } + if(1 != pt.getActualTypeArguments().length) { + System.err.println("Number of type parameters for l3 is wrong"); + error++; + } + if(!(pt.getActualTypeArguments()[0] instanceof WildcardType)) { + System.err.println("Type parameter for l3 is wrong"); + error++; + } else { + WildcardType wt = (WildcardType) + pt.getActualTypeArguments()[0]; + if(!wt.getUpperBounds()[0].equals(Foo.class)) { + System.err.println("Upper bounds on type parameter fol l3 is wrong"); + error++; + } + } + } + if(!parameters[5].isVarArgs()) { + System.err.println("isVarArg for rest is wrong"); + error++; + } + if(!(parameters[5].getParameterizedType().equals(String[].class))) { + System.err.println("getParameterizedType for rest is wrong"); + error++; + } + + } + } + if(0 != error) + throw new Exception("Failed " + error + " tests"); + } + + public class Foo { + int thing; + public void qux(int quux, Foo quuux, + List l, List l2, + List l3, + String... rest) {} + public class Inner { + int thang; + public Inner(int theng) { + thang = theng + thing; + } + } + } + +} From c3343fdb496e4f44989b5c7acf75cbaaae15a2a6 Mon Sep 17 00:00:00 2001 From: Xueming Shen Date: Wed, 23 Jan 2013 10:29:50 -0800 Subject: [PATCH 097/138] 8006773: test/java/util/zip/ZipFile/FinalizeZipFile.java failing intermittently Fixed the test case Reviewed-by: alanb --- jdk/test/java/util/zip/ZipFile/FinalizeZipFile.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/jdk/test/java/util/zip/ZipFile/FinalizeZipFile.java b/jdk/test/java/util/zip/ZipFile/FinalizeZipFile.java index 56ac12efb34..1c27e848280 100644 --- a/jdk/test/java/util/zip/ZipFile/FinalizeZipFile.java +++ b/jdk/test/java/util/zip/ZipFile/FinalizeZipFile.java @@ -67,9 +67,14 @@ public class FinalizeZipFile { new InstrumentedZipFile(jars[rnd.nextInt(jars.length)]).close(); // Create a ZipFile and get an input stream from it - ZipFile zf = new InstrumentedZipFile(jars[rnd.nextInt(jars.length)]); - ZipEntry ze = zf.getEntry("META-INF/MANIFEST.MF"); - InputStream is = zf.getInputStream(ze); + for (int i = 0; i < jars.length + 10; i++) { + ZipFile zf = new InstrumentedZipFile(jars[rnd.nextInt(jars.length)]); + ZipEntry ze = zf.getEntry("META-INF/MANIFEST.MF"); + if (ze != null) { + InputStream is = zf.getInputStream(ze); + break; + } + } } public static void realMain(String[] args) throws Throwable { @@ -97,4 +102,3 @@ public class FinalizeZipFile { System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); if (failed > 0) throw new AssertionError("Some tests failed");} } - From 7dcd0b38a8754c09cd660f3fc3597500ac26e4f4 Mon Sep 17 00:00:00 2001 From: Vinnie Ryan Date: Wed, 23 Jan 2013 21:25:49 +0000 Subject: [PATCH 098/138] 8006591: Protect keystore entries using stronger PBE algorithms Reviewed-by: mullan --- .../share/classes/java/security/KeyStore.java | 71 +++++- .../sun/security/pkcs12/PKCS12KeyStore.java | 204 ++++++++++++++++-- jdk/test/java/security/KeyStore/PBETest.java | 102 +++++++++ 3 files changed, 362 insertions(+), 15 deletions(-) create mode 100644 jdk/test/java/security/KeyStore/PBETest.java diff --git a/jdk/src/share/classes/java/security/KeyStore.java b/jdk/src/share/classes/java/security/KeyStore.java index 8e4855f763a..c27f7f435df 100644 --- a/jdk/src/share/classes/java/security/KeyStore.java +++ b/jdk/src/share/classes/java/security/KeyStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ import java.io.*; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.security.cert.CertificateException; +import java.security.spec.AlgorithmParameterSpec; import java.util.*; import javax.crypto.SecretKey; @@ -239,6 +240,8 @@ public class KeyStore { ProtectionParameter, javax.security.auth.Destroyable { private final char[] password; + private final String protectionAlgorithm; + private final AlgorithmParameterSpec protectionParameters; private volatile boolean destroyed = false; /** @@ -251,6 +254,72 @@ public class KeyStore { */ public PasswordProtection(char[] password) { this.password = (password == null) ? null : password.clone(); + this.protectionAlgorithm = null; + this.protectionParameters = null; + } + + /** + * Creates a password parameter and specifies the protection algorithm + * and associated parameters to use when encrypting a keystore entry. + *

    + * The specified {@code password} is cloned before it is stored in the + * new {@code PasswordProtection} object. + * + * @param password the password, which may be {@code null} + * @param protectionAlgorithm the encryption algorithm name, for + * example, {@code PBEWithHmacSHA256AndAES_256}. + * See the Cipher section in the + * Java Cryptography Architecture Standard Algorithm Name + * Documentation + * for information about standard encryption algorithm names. + * @param protectionParameters the encryption algorithm parameter + * specification, which may be {@code null} + * @exception NullPointerException if {@code protectionAlgorithm} is + * {@code null} + * + * @since 1.8 + */ + public PasswordProtection(char[] password, String protectionAlgorithm, + AlgorithmParameterSpec protectionParameters) { + if (protectionAlgorithm == null) { + throw new NullPointerException("invalid null input"); + } + this.password = (password == null) ? null : password.clone(); + this.protectionAlgorithm = protectionAlgorithm; + this.protectionParameters = protectionParameters; + } + + /** + * Gets the name of the protection algorithm. + * If none was set then the keystore provider will use its default + * protection algorithm. The name of the default protection algorithm + * for a given keystore type is set using the + * {@code 'keystore..keyProtectionAlgorithm'} security property. + * For example, the + * {@code keystore.PKCS12.keyProtectionAlgorithm} property stores the + * name of the default key protection algorithm used for PKCS12 + * keystores. If the security property is not set, an + * implementation-specific algorithm will be used. + * + * @return the algorithm name, or {@code null} if none was set + * + * @since 1.8 + */ + public String getProtectionAlgorithm() { + return protectionAlgorithm; + } + + /** + * Gets the parameters supplied for the protection algorithm. + * + * @return the algorithm parameter specification, or {@code null}, + * if none was set + * + * @since 1.8 + */ + public AlgorithmParameterSpec getProtectionParameters() { + return protectionParameters; } /** diff --git a/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java b/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java index 69ac4d6f7e3..1c348017828 100644 --- a/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java +++ b/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,19 +26,25 @@ package sun.security.pkcs12; import java.io.*; +import java.security.AccessController; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.Key; +import java.security.KeyStore; import java.security.KeyFactory; import java.security.PrivateKey; +import java.security.PrivilegedAction; import java.security.KeyStoreSpi; import java.security.KeyStoreException; +import java.security.UnrecoverableEntryException; import java.security.UnrecoverableKeyException; +import java.security.Security; import java.security.SecureRandom; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.cert.CertificateException; +import java.security.spec.AlgorithmParameterSpec; import java.security.spec.PKCS8EncodedKeySpec; import java.util.*; @@ -49,8 +55,10 @@ import javax.crypto.SecretKeyFactory; import javax.crypto.SecretKey; import javax.crypto.Cipher; import javax.crypto.Mac; +import javax.security.auth.DestroyFailedException; import javax.security.auth.x500.X500Principal; +import sun.security.util.Debug; import sun.security.util.DerInputStream; import sun.security.util.DerOutputStream; import sun.security.util.DerValue; @@ -119,6 +127,13 @@ public final class PKCS12KeyStore extends KeyStoreSpi { public static final int VERSION_3 = 3; + private static final String[] KEY_PROTECTION_ALGORITHM = { + "keystore.pkcs12.keyProtectionAlgorithm", + "keystore.PKCS12.keyProtectionAlgorithm" + }; + + private static final Debug debug = Debug.getInstance("pkcs12"); + private static final int keyBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 2}; private static final int certBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 3}; @@ -131,6 +146,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { {1, 2, 840, 113549, 1, 12, 1, 6}; private static final int pbeWithSHAAnd3KeyTripleDESCBC[] = {1, 2, 840, 113549, 1, 12, 1, 3}; + private static final int pbes2[] = {1, 2, 840, 113549, 1, 5, 13}; private static ObjectIdentifier PKCS8ShroudedKeyBag_OID; private static ObjectIdentifier CertBag_OID; @@ -139,6 +155,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { private static ObjectIdentifier PKCS9CertType_OID; private static ObjectIdentifier pbeWithSHAAnd40BitRC2CBC_OID; private static ObjectIdentifier pbeWithSHAAnd3KeyTripleDESCBC_OID; + private static ObjectIdentifier pbes2_OID; private int counter = 0; private static final int iterationCount = 1024; @@ -163,6 +180,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { new ObjectIdentifier(pbeWithSHAAnd40BitRC2CBC); pbeWithSHAAnd3KeyTripleDESCBC_OID = new ObjectIdentifier(pbeWithSHAAnd3KeyTripleDESCBC); + pbes2_OID = new ObjectIdentifier(pbes2); } catch (IOException ioe) { // should not happen } @@ -288,6 +306,12 @@ public final class PKCS12KeyStore extends KeyStoreSpi { KeyFactory kfac = KeyFactory.getInstance(algName); key = kfac.generatePrivate(kspec); + + if (debug != null) { + debug.println("Retrieved a protected private key at alias '" + + alias + "'"); + } + } catch (Exception e) { UnrecoverableKeyException uke = new UnrecoverableKeyException("Get Key failed: " + @@ -315,6 +339,13 @@ public final class PKCS12KeyStore extends KeyStoreSpi { if (entry.chain == null) { return null; } else { + + if (debug != null) { + debug.println("Retrieved a " + + entry.chain.length + + "-certificate chain at alias '" + alias + "'"); + } + return entry.chain.clone(); } } else { @@ -343,6 +374,12 @@ public final class PKCS12KeyStore extends KeyStoreSpi { if (entry.chain == null) { return null; } else { + + if (debug != null) { + debug.println("Retrieved a certificate at alias '" + alias + + "'"); + } + return entry.chain[0]; } } else { @@ -392,6 +429,28 @@ public final class PKCS12KeyStore extends KeyStoreSpi { public synchronized void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain) throws KeyStoreException + { + KeyStore.PasswordProtection passwordProtection = + new KeyStore.PasswordProtection(password); + + try { + setKeyEntry(alias, key, passwordProtection, chain); + + } finally { + try { + passwordProtection.destroy(); + } catch (DestroyFailedException dfe) { + // ignore + } + } + } + + /* + * Sets a key entry + */ + private void setKeyEntry(String alias, Key key, + KeyStore.PasswordProtection passwordProtection, Certificate[] chain) + throws KeyStoreException { try { KeyEntry entry = new KeyEntry(); @@ -401,8 +460,14 @@ public final class PKCS12KeyStore extends KeyStoreSpi { if ((key.getFormat().equals("PKCS#8")) || (key.getFormat().equals("PKCS8"))) { // Encrypt the private key + + if (debug != null) { + debug.println("Setting a protected private key at " + + "alias '" + alias + "'"); + } + entry.protectedPrivKey = - encryptPrivateKey(key.getEncoded(), password); + encryptPrivateKey(key.getEncoded(), passwordProtection); } else { throw new KeyStoreException("Private key is not encoded" + "as PKCS#8"); @@ -418,6 +483,11 @@ public final class PKCS12KeyStore extends KeyStoreSpi { throw new KeyStoreException("Certificate chain is " + "not validate"); entry.chain = chain.clone(); + + if (debug != null) { + debug.println("Setting a " + chain.length + + "-certificate chain at alias '" + alias + "'"); + } } // set the keyId to current date @@ -472,6 +542,10 @@ public final class PKCS12KeyStore extends KeyStoreSpi { KeyEntry entry = new KeyEntry(); entry.date = new Date(); + if (debug != null) { + debug.println("Setting a protected key at alias '" + alias + "'"); + } + try { // set the keyId to current date entry.keyId = ("Time " + (entry.date).getTime()).getBytes("UTF8"); @@ -484,6 +558,11 @@ public final class PKCS12KeyStore extends KeyStoreSpi { entry.protectedPrivKey = key.clone(); if (chain != null) { entry.chain = chain.clone(); + + if (debug != null) { + debug.println("Setting a " + entry.chain.length + + "-certificate chain at alias '" + alias + "'"); + } } // add the entry @@ -576,31 +655,76 @@ public final class PKCS12KeyStore extends KeyStoreSpi { * Encrypt private key using Password-based encryption (PBE) * as defined in PKCS#5. * - * NOTE: Currently pbeWithSHAAnd3-KeyTripleDES-CBC algorithmID is + * NOTE: By default, pbeWithSHAAnd3-KeyTripleDES-CBC algorithmID is * used to derive the key and IV. * * @return encrypted private key encoded as EncryptedPrivateKeyInfo */ - private byte[] encryptPrivateKey(byte[] data, char[] password) + private byte[] encryptPrivateKey(byte[] data, + KeyStore.PasswordProtection passwordProtection) throws IOException, NoSuchAlgorithmException, UnrecoverableKeyException { byte[] key = null; try { - // create AlgorithmParameters - AlgorithmParameters algParams = - getAlgorithmParameters("PBEWithSHA1AndDESede"); + String algorithm; + AlgorithmParameters algParams; + AlgorithmId algid; + + // Initialize PBE algorithm and parameters + algorithm = passwordProtection.getProtectionAlgorithm(); + if (algorithm != null) { + AlgorithmParameterSpec algParamSpec = + passwordProtection.getProtectionParameters(); + if (algParamSpec != null) { + algParams = AlgorithmParameters.getInstance(algorithm); + algParams.init(algParamSpec); + } else { + algParams = getAlgorithmParameters(algorithm); + } + ObjectIdentifier pbeOID = mapPBEAlgorithmToOID(algorithm); + if (pbeOID != null) { + algid = new AlgorithmId(pbeOID, algParams); + } else { + throw new IOException("PBE algorithm '" + algorithm + + " 'is not supported for key entry protection"); + } + } else { + // Check default key protection algorithm for PKCS12 keystores + algorithm = AccessController.doPrivileged( + new PrivilegedAction() { + public String run() { + String prop = + Security.getProperty( + KEY_PROTECTION_ALGORITHM[0]); + if (prop == null) { + prop = Security.getProperty( + KEY_PROTECTION_ALGORITHM[1]); + } + return prop; + } + }); + if (algorithm == null) { + algorithm = "PBEWithSHA1AndDESede"; + } + algParams = getAlgorithmParameters(algorithm); + algid = new AlgorithmId(pbeWithSHAAnd3KeyTripleDESCBC_OID, + algParams); + } // Use JCE - SecretKey skey = getPBEKey(password); - Cipher cipher = Cipher.getInstance("PBEWithSHA1AndDESede"); + SecretKey skey = getPBEKey(passwordProtection.getPassword()); + Cipher cipher = Cipher.getInstance(algorithm); cipher.init(Cipher.ENCRYPT_MODE, skey, algParams); byte[] encryptedKey = cipher.doFinal(data); + if (debug != null) { + debug.println(" (Cipher algorithm: " + cipher.getAlgorithm() + + ")"); + } + // wrap encrypted private key in EncryptedPrivateKeyInfo // as defined in PKCS#8 - AlgorithmId algid = - new AlgorithmId(pbeWithSHAAnd3KeyTripleDESCBC_OID, algParams); EncryptedPrivateKeyInfo encrInfo = new EncryptedPrivateKeyInfo(algid, encryptedKey); key = encrInfo.getEncoded(); @@ -615,6 +739,18 @@ public final class PKCS12KeyStore extends KeyStoreSpi { return key; } + /* + * Map a PBE algorithm name onto its object identifier + */ + private ObjectIdentifier mapPBEAlgorithmToOID(String algorithm) { + // Check for PBES2 algorithms + if (algorithm.toLowerCase().startsWith("pbewithhmacsha")) { + return pbes2_OID; + } + + return null; + } + /** * Assigns the given certificate to the given alias. * @@ -649,6 +785,10 @@ public final class PKCS12KeyStore extends KeyStoreSpi { public synchronized void engineDeleteEntry(String alias) throws KeyStoreException { + if (debug != null) { + debug.println("Removing entry at alias '" + alias + "'"); + } + entries.remove(alias.toLowerCase(Locale.ENGLISH)); } @@ -778,11 +918,21 @@ public final class PKCS12KeyStore extends KeyStoreSpi { DerOutputStream authSafeContentInfo = new DerOutputStream(); // -- create safeContent Data ContentInfo + if (debug != null) { + debug.println("Storing " + privateKeyCount + + " protected key(s) in a PKCS#7 data content-type"); + } + byte[] safeContentData = createSafeContent(); ContentInfo dataContentInfo = new ContentInfo(safeContentData); dataContentInfo.encode(authSafeContentInfo); // -- create EncryptedContentInfo + if (debug != null) { + debug.println("Storing certificate(s) in a PKCS#7 encryptedData " + + "content-type"); + } + byte[] encrData = createEncryptedData(password); ContentInfo encrContentInfo = new ContentInfo(ContentInfo.ENCRYPTED_DATA_OID, @@ -812,7 +962,6 @@ public final class PKCS12KeyStore extends KeyStoreSpi { stream.flush(); } - /* * Generate Hash. */ @@ -1143,6 +1292,11 @@ public final class PKCS12KeyStore extends KeyStoreSpi { cipher.init(Cipher.ENCRYPT_MODE, skey, algParams); encryptedData = cipher.doFinal(data); + if (debug != null) { + debug.println(" (Cipher algorithm: " + cipher.getAlgorithm() + + ")"); + } + } catch (Exception e) { throw new IOException("Failed to encrypt" + " safe contents entry: " + e, e); @@ -1240,11 +1394,21 @@ public final class PKCS12KeyStore extends KeyStoreSpi { contentType = safeContents.getContentType(); safeContentsData = null; if (contentType.equals((Object)ContentInfo.DATA_OID)) { + + if (debug != null) { + debug.println("Loading PKCS#7 data content-type"); + } + safeContentsData = safeContents.getData(); } else if (contentType.equals(ContentInfo.ENCRYPTED_DATA_OID)) { if (password == null) { continue; } + + if (debug != null) { + debug.println("Loading PKCS#7 encryptedData content-type"); + } + DerInputStream edi = safeContents.getContent().toDerInputStream(); int edVersion = edi.getInteger(); @@ -1312,6 +1476,11 @@ public final class PKCS12KeyStore extends KeyStoreSpi { m.update(authSafeData); byte[] macResult = m.doFinal(); + if (debug != null) { + debug.println("Checking keystore integrity " + + "(MAC algorithm: " + m.getAlgorithm() + ")"); + } + if (!Arrays.equals(macData.getDigest(), macResult)) { throw new SecurityException("Failed PKCS12" + " integrity checking"); @@ -1417,7 +1586,10 @@ public final class PKCS12KeyStore extends KeyStoreSpi { (new ByteArrayInputStream(certValue.getOctetString())); bagItem = cert; } else { - // log error message for "unsupported PKCS12 bag type" + + if (debug != null) { + debug.println("Unsupported PKCS12 bag type: " + bagId); + } } DerValue[] attrSet; @@ -1453,7 +1625,11 @@ public final class PKCS12KeyStore extends KeyStoreSpi { } else if (attrId.equals((Object)PKCS9LocalKeyId_OID)) { keyId = valSet[0].getOctetString(); } else { - // log error message for "unknown attr" + + if (debug != null) { + debug.println("Unsupported PKCS12 bag attribute: " + + attrId); + } } } } diff --git a/jdk/test/java/security/KeyStore/PBETest.java b/jdk/test/java/security/KeyStore/PBETest.java new file mode 100644 index 00000000000..bc12102f230 --- /dev/null +++ b/jdk/test/java/security/KeyStore/PBETest.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8006591 + * @summary Protect keystore entries using stronger PBE algorithms + */ + +import java.io.*; +import java.security.*; +import javax.crypto.spec.*; + +// Retrieve a keystore entry, protected by the default encryption algorithm. +// Set the keystore entry, protected by a stronger encryption algorithm. + +public class PBETest { + private final static String DIR = System.getProperty("test.src", "."); + private static final String PBE_ALGO = "PBEWithHmacSHA1AndAES_128"; + private static final char[] PASSWORD = "passphrase".toCharArray(); + private static final String KEYSTORE_TYPE = "JKS"; + private static final String KEYSTORE = DIR + "/keystore.jks"; + private static final String NEW_KEYSTORE_TYPE = "PKCS12"; + private static final String NEW_KEYSTORE = PBE_ALGO + ".p12"; + private static final String ALIAS = "vajra"; + + private static final byte[] IV = { + 0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18, + 0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20 + }; + private static final byte[] SALT = { + 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08 + }; + private static final int ITERATION_COUNT = 1024; + + public static void main(String[] args) throws Exception { + + new File(NEW_KEYSTORE).delete(); + + try { + KeyStore keystore = load(KEYSTORE_TYPE, KEYSTORE, PASSWORD); + KeyStore.Entry entry = + keystore.getEntry(ALIAS, + new KeyStore.PasswordProtection(PASSWORD)); + System.out.println("Retrieved entry named '" + ALIAS + "'"); + + // Set entry + KeyStore keystore2 = load(NEW_KEYSTORE_TYPE, null, null); + keystore2.setEntry(ALIAS, entry, + new KeyStore.PasswordProtection(PASSWORD, PBE_ALGO, + new PBEParameterSpec(SALT, ITERATION_COUNT, + new IvParameterSpec(IV)))); + System.out.println("Encrypted entry using: " + PBE_ALGO); + + System.out.println("Storing keystore to: " + NEW_KEYSTORE); + keystore2.store(new FileOutputStream(NEW_KEYSTORE), PASSWORD); + + keystore2 = load(NEW_KEYSTORE_TYPE, NEW_KEYSTORE, PASSWORD); + entry = keystore2.getEntry(ALIAS, + new KeyStore.PasswordProtection(PASSWORD)); + System.out.println("Retrieved entry named '" + ALIAS + "'"); + + } finally { + new File(NEW_KEYSTORE).delete(); + System.out.println("Deleted keystore: " + NEW_KEYSTORE); + } + } + + private static KeyStore load(String type, String path, char[] password) + throws Exception { + + FileInputStream stream = null; + if (path != null) { + stream = new FileInputStream(path); + } + KeyStore keystore = KeyStore.getInstance(type); + System.out.println("Loading keystore from: " + path); + keystore.load(stream, password); + + return keystore; + } +} From b680bc8ca75a06c3b3d0e8720e84b7fbdaa38407 Mon Sep 17 00:00:00 2001 From: Vinnie Ryan Date: Wed, 23 Jan 2013 23:13:54 +0000 Subject: [PATCH 099/138] 8005408: KeyStore API enhancements Reviewed-by: mullan --- .../share/classes/java/security/KeyStore.java | 171 +++- .../java/security/PKCS12Attribute.java | 285 ++++++ .../sun/security/pkcs12/PKCS12KeyStore.java | 876 ++++++++++++++---- .../sun/security/x509/AlgorithmId.java | 15 +- .../security/pkcs12/StorePasswordTest.java | 92 ++ .../security/pkcs12/StoreSecretKeyTest.java | 86 ++ .../security/pkcs12/StoreTrustedCertTest.java | 117 +++ jdk/test/sun/security/pkcs12/trusted.pem | 29 + 8 files changed, 1503 insertions(+), 168 deletions(-) create mode 100644 jdk/src/share/classes/java/security/PKCS12Attribute.java create mode 100644 jdk/test/sun/security/pkcs12/StorePasswordTest.java create mode 100644 jdk/test/sun/security/pkcs12/StoreSecretKeyTest.java create mode 100644 jdk/test/sun/security/pkcs12/StoreTrustedCertTest.java create mode 100644 jdk/test/sun/security/pkcs12/trusted.pem diff --git a/jdk/src/share/classes/java/security/KeyStore.java b/jdk/src/share/classes/java/security/KeyStore.java index c27f7f435df..96565684b0e 100644 --- a/jdk/src/share/classes/java/security/KeyStore.java +++ b/jdk/src/share/classes/java/security/KeyStore.java @@ -26,6 +26,7 @@ package java.security; import java.io.*; +import java.net.URI; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.security.cert.CertificateException; @@ -405,7 +406,44 @@ public class KeyStore { * * @since 1.5 */ - public static interface Entry { } + public static interface Entry { + + /** + * Retrieves the attributes associated with an entry. + *

    + * The default implementation returns an empty {@code Set}. + * + * @return an unmodifiable {@code Set} of attributes, possibly empty + * + * @since 1.8 + */ + public default Set getAttributes() { + return Collections.emptySet(); + } + + /** + * An attribute associated with a keystore entry. + * It comprises a name and one or more values. + * + * @since 1.8 + */ + public interface Attribute { + /** + * Returns the attribute's name. + * + * @return the attribute name + */ + public String getName(); + + /** + * Returns the attribute's value. + * Multi-valued attributes encode their values as a single string. + * + * @return the attribute value + */ + public String getValue(); + } + } /** * A KeyStore entry that holds a PrivateKey @@ -417,6 +455,7 @@ public class KeyStore { private final PrivateKey privKey; private final Certificate[] chain; + private final Set attributes; /** * Constructs a PrivateKeyEntry with a @@ -443,7 +482,39 @@ public class KeyStore { * in the end entity Certificate (at index 0) */ public PrivateKeyEntry(PrivateKey privateKey, Certificate[] chain) { - if (privateKey == null || chain == null) { + this(privateKey, chain, Collections.emptySet()); + } + + /** + * Constructs a {@code PrivateKeyEntry} with a {@code PrivateKey} and + * corresponding certificate chain and associated entry attributes. + * + *

    The specified {@code chain} and {@code attributes} are cloned + * before they are stored in the new {@code PrivateKeyEntry} object. + * + * @param privateKey the {@code PrivateKey} + * @param chain an array of {@code Certificate}s + * representing the certificate chain. + * The chain must be ordered and contain a + * {@code Certificate} at index 0 + * corresponding to the private key. + * @param attributes the attributes + * + * @exception NullPointerException if {@code privateKey}, {@code chain} + * or {@code attributes} is {@code null} + * @exception IllegalArgumentException if the specified chain has a + * length of 0, if the specified chain does not contain + * {@code Certificate}s of the same type, + * or if the {@code PrivateKey} algorithm + * does not match the algorithm of the {@code PublicKey} + * in the end entity {@code Certificate} (at index 0) + * + * @since 1.8 + */ + public PrivateKeyEntry(PrivateKey privateKey, Certificate[] chain, + Set attributes) { + + if (privateKey == null || chain == null || attributes == null) { throw new NullPointerException("invalid null input"); } if (chain.length == 0) { @@ -478,6 +549,9 @@ public class KeyStore { } else { this.chain = clonedChain; } + + this.attributes = + Collections.unmodifiableSet(new HashSet<>(attributes)); } /** @@ -518,6 +592,19 @@ public class KeyStore { return chain[0]; } + /** + * Retrieves the attributes associated with an entry. + *

    + * + * @return an unmodifiable {@code Set} of attributes, possibly empty + * + * @since 1.8 + */ + @Override + public Set getAttributes() { + return attributes; + } + /** * Returns a string representation of this PrivateKeyEntry. * @return a string representation of this PrivateKeyEntry. @@ -543,6 +630,7 @@ public class KeyStore { public static final class SecretKeyEntry implements Entry { private final SecretKey sKey; + private final Set attributes; /** * Constructs a SecretKeyEntry with a @@ -558,6 +646,32 @@ public class KeyStore { throw new NullPointerException("invalid null input"); } this.sKey = secretKey; + this.attributes = Collections.emptySet(); + } + + /** + * Constructs a {@code SecretKeyEntry} with a {@code SecretKey} and + * associated entry attributes. + * + *

    The specified {@code attributes} is cloned before it is stored + * in the new {@code SecretKeyEntry} object. + * + * @param secretKey the {@code SecretKey} + * @param attributes the attributes + * + * @exception NullPointerException if {@code secretKey} or + * {@code attributes} is {@code null} + * + * @since 1.8 + */ + public SecretKeyEntry(SecretKey secretKey, Set attributes) { + + if (secretKey == null || attributes == null) { + throw new NullPointerException("invalid null input"); + } + this.sKey = secretKey; + this.attributes = + Collections.unmodifiableSet(new HashSet<>(attributes)); } /** @@ -569,6 +683,19 @@ public class KeyStore { return sKey; } + /** + * Retrieves the attributes associated with an entry. + *

    + * + * @return an unmodifiable {@code Set} of attributes, possibly empty + * + * @since 1.8 + */ + @Override + public Set getAttributes() { + return attributes; + } + /** * Returns a string representation of this SecretKeyEntry. * @return a string representation of this SecretKeyEntry. @@ -587,6 +714,7 @@ public class KeyStore { public static final class TrustedCertificateEntry implements Entry { private final Certificate cert; + private final Set attributes; /** * Constructs a TrustedCertificateEntry with a @@ -602,6 +730,32 @@ public class KeyStore { throw new NullPointerException("invalid null input"); } this.cert = trustedCert; + this.attributes = Collections.emptySet(); + } + + /** + * Constructs a {@code TrustedCertificateEntry} with a + * trusted {@code Certificate} and associated entry attributes. + * + *

    The specified {@code attributes} is cloned before it is stored + * in the new {@code TrustedCertificateEntry} object. + * + * @param trustedCert the trusted {@code Certificate} + * @param attributes the attributes + * + * @exception NullPointerException if {@code trustedCert} or + * {@code attributes} is {@code null} + * + * @since 1.8 + */ + public TrustedCertificateEntry(Certificate trustedCert, + Set attributes) { + if (trustedCert == null || attributes == null) { + throw new NullPointerException("invalid null input"); + } + this.cert = trustedCert; + this.attributes = + Collections.unmodifiableSet(new HashSet<>(attributes)); } /** @@ -613,6 +767,19 @@ public class KeyStore { return cert; } + /** + * Retrieves the attributes associated with an entry. + *

    + * + * @return an unmodifiable {@code Set} of attributes, possibly empty + * + * @since 1.8 + */ + @Override + public Set getAttributes() { + return attributes; + } + /** * Returns a string representation of this TrustedCertificateEntry. * @return a string representation of this TrustedCertificateEntry. diff --git a/jdk/src/share/classes/java/security/PKCS12Attribute.java b/jdk/src/share/classes/java/security/PKCS12Attribute.java new file mode 100644 index 00000000000..b13a4b1f18a --- /dev/null +++ b/jdk/src/share/classes/java/security/PKCS12Attribute.java @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.security; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.regex.Pattern; +import sun.security.util.*; + +/** + * An attribute associated with a PKCS12 keystore entry. + * The attribute name is an ASN.1 Object Identifier and the attribute + * value is a set of ASN.1 types. + * + * @since 1.8 + */ +public final class PKCS12Attribute implements KeyStore.Entry.Attribute { + + private static final Pattern COLON_SEPARATED_HEX_PAIRS = + Pattern.compile("^[0-9a-fA-F]{2}(:[0-9a-fA-F]{2})+$"); + private String name; + private String value; + private byte[] encoded; + private int hashValue = -1; + + /** + * Constructs a PKCS12 attribute from its name and value. + * The name is an ASN.1 Object Identifier represented as a list of + * dot-separated integers. + * A string value is represented as the string itself. + * A binary value is represented as a string of colon-separated + * pairs of hexadecimal digits. + * Multi-valued attributes are represented as a comma-separated + * list of values, enclosed in square brackets. See + * {@link Arrays.toString}. + *

    + * A string value will be DER-encoded as an ASN.1 UTF8String and a + * binary value will be DER-encoded as an ASN.1 Octet String. + * + * @param name the attribute's identifier + * @param value the attribute's value + * + * @exception NullPointerException if {@code name} or {@code value} + * is {@code null} + * @exception IllegalArgumentException if {@code name} or + * {@code value} is incorrectly formatted + */ + public PKCS12Attribute(String name, String value) { + if (name == null || value == null) { + throw new NullPointerException(); + } + // Validate name + ObjectIdentifier type; + try { + type = new ObjectIdentifier(name); + } catch (IOException e) { + throw new IllegalArgumentException("Incorrect format: name", e); + } + this.name = name; + + // Validate value + int length = value.length(); + String[] values; + if (value.charAt(0) == '[' && value.charAt(length - 1) == ']') { + values = value.substring(1, length - 1).split(", "); + } else { + values = new String[]{ value }; + } + this.value = value; + + try { + this.encoded = encode(type, values); + } catch (IOException e) { + throw new IllegalArgumentException("Incorrect format: value", e); + } + } + + /** + * Constructs a PKCS12 attribute from its ASN.1 DER encoding. + * The DER encoding is specified by the following ASN.1 definition: + *

    +     *
    +     * Attribute ::= SEQUENCE {
    +     *     type   AttributeType,
    +     *     values SET OF AttributeValue
    +     * }
    +     * AttributeType ::= OBJECT IDENTIFIER
    +     * AttributeValue ::= ANY defined by type
    +     *
    +     * 
    + * + * @param encoded the attribute's ASN.1 DER encoding. It is cloned + * to prevent subsequent modificaion. + * + * @exception NullPointerException if {@code encoded} is + * {@code null} + * @exception IllegalArgumentException if {@code encoded} is + * incorrectly formatted + */ + public PKCS12Attribute(byte[] encoded) { + if (encoded == null) { + throw new NullPointerException(); + } + this.encoded = encoded.clone(); + + try { + parse(encoded); + } catch (IOException e) { + throw new IllegalArgumentException("Incorrect format: encoded", e); + } + } + + /** + * Returns the attribute's ASN.1 Object Identifier represented as a + * list of dot-separated integers. + * + * @return the attribute's identifier + */ + @Override + public String getName() { + return name; + } + + /** + * Returns the attribute's ASN.1 DER-encoded value as a string. + * An ASN.1 DER-encoded value is returned in one of the following + * {@code String} formats: + *
      + *
    • the DER encoding of a basic ASN.1 type that has a natural + * string representation is returned as the string itself. + * Such types are currently limited to BOOLEAN, INTEGER, + * OBJECT IDENTIFIER, UTCTime, GeneralizedTime and the + * following six ASN.1 string types: UTF8String, + * PrintableString, T61String, IA5String, BMPString and + * GeneralString. + *
    • the DER encoding of any other ASN.1 type is not decoded but + * returned as a binary string of colon-separated pairs of + * hexadecimal digits. + *
    + * Multi-valued attributes are represented as a comma-separated + * list of values, enclosed in square brackets. See + * {@link Arrays.toString}. + * + * @return the attribute value's string encoding + */ + @Override + public String getValue() { + return value; + } + + /** + * Returns the attribute's ASN.1 DER encoding. + * + * @return a clone of the attribute's DER encoding + */ + public byte[] getEncoded() { + return encoded.clone(); + } + + /** + * Compares this {@code PKCS12Attribute} and a specified object for + * equality. + * + * @param obj the comparison object + * + * @return true if {@code obj} is a {@code PKCS12Attribute} and + * their DER encodings are equal. + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof PKCS12Attribute)) { + return false; + } + return Arrays.equals(encoded, ((PKCS12Attribute) obj).getEncoded()); + } + + /** + * Returns the hashcode for this {@code PKCS12Attribute}. + * The hash code is computed from its DER encoding. + * + * @return the hash code + */ + @Override + public int hashCode() { + if (hashValue == -1) { + Arrays.hashCode(encoded); + } + return hashValue; + } + + /** + * Returns a string representation of this {@code PKCS12Attribute}. + * + * @return a name/value pair separated by an 'equals' symbol + */ + @Override + public String toString() { + return (name + "=" + value); + } + + private byte[] encode(ObjectIdentifier type, String[] values) + throws IOException { + DerOutputStream attribute = new DerOutputStream(); + attribute.putOID(type); + DerOutputStream attrContent = new DerOutputStream(); + for (String value : values) { + if (COLON_SEPARATED_HEX_PAIRS.matcher(value).matches()) { + byte[] bytes = + new BigInteger(value.replace(":", ""), 16).toByteArray(); + if (bytes[0] == 0) { + bytes = Arrays.copyOfRange(bytes, 1, bytes.length); + } + attrContent.putOctetString(bytes); + } else { + attrContent.putUTF8String(value); + } + } + attribute.write(DerValue.tag_Set, attrContent); + DerOutputStream attributeValue = new DerOutputStream(); + attributeValue.write(DerValue.tag_Sequence, attribute); + + return attributeValue.toByteArray(); + } + + private void parse(byte[] encoded) throws IOException { + DerInputStream attributeValue = new DerInputStream(encoded); + DerValue[] attrSeq = attributeValue.getSequence(2); + ObjectIdentifier type = attrSeq[0].getOID(); + DerInputStream attrContent = + new DerInputStream(attrSeq[1].toByteArray()); + DerValue[] attrValueSet = attrContent.getSet(1); + String[] values = new String[attrValueSet.length]; + String printableString; + for (int i = 0; i < attrValueSet.length; i++) { + if (attrValueSet[i].tag == DerValue.tag_OctetString) { + values[i] = Debug.toString(attrValueSet[i].getOctetString()); + } else if ((printableString = attrValueSet[i].getAsString()) + != null) { + values[i] = printableString; + } else if (attrValueSet[i].tag == DerValue.tag_ObjectId) { + values[i] = attrValueSet[i].getOID().toString(); + } else if (attrValueSet[i].tag == DerValue.tag_GeneralizedTime) { + values[i] = attrValueSet[i].getGeneralizedTime().toString(); + } else if (attrValueSet[i].tag == DerValue.tag_UtcTime) { + values[i] = attrValueSet[i].getUTCTime().toString(); + } else if (attrValueSet[i].tag == DerValue.tag_Integer) { + values[i] = attrValueSet[i].getBigInteger().toString(); + } else if (attrValueSet[i].tag == DerValue.tag_Boolean) { + values[i] = String.valueOf(attrValueSet[i].getBoolean()); + } else { + values[i] = Debug.toString(attrValueSet[i].getDataBytes()); + } + } + + this.name = type.toString(); + this.value = values.length == 1 ? values[0] : Arrays.toString(values); + } +} diff --git a/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java b/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java index 1c348017828..a8ce582fa2e 100644 --- a/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java +++ b/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java @@ -30,27 +30,30 @@ import java.security.AccessController; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.Key; -import java.security.KeyStore; import java.security.KeyFactory; -import java.security.PrivateKey; -import java.security.PrivilegedAction; +import java.security.KeyStore; import java.security.KeyStoreSpi; import java.security.KeyStoreException; +import java.security.PKCS12Attribute; +import java.security.PrivateKey; +import java.security.PrivilegedAction; import java.security.UnrecoverableEntryException; import java.security.UnrecoverableKeyException; -import java.security.Security; import java.security.SecureRandom; +import java.security.Security; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.cert.CertificateException; import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.KeySpec; import java.security.spec.PKCS8EncodedKeySpec; import java.util.*; import java.security.AlgorithmParameters; import javax.crypto.spec.PBEParameterSpec; import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; import javax.crypto.SecretKeyFactory; import javax.crypto.SecretKey; import javax.crypto.Cipher; @@ -107,11 +110,12 @@ import sun.security.pkcs.EncryptedPrivateKeyInfo; * OpenSSL PKCS#12 code. All. All. * --------------------------------------------------------------------- * - * NOTE: Currently PKCS12 KeyStore does not support TrustedCertEntries. + * NOTE: PKCS12 KeyStore supports PrivateKeyEntry and TrustedCertficateEntry. * PKCS#12 is mainly used to deliver private keys with their associated * certificate chain and aliases. In a PKCS12 keystore, entries are * identified by the alias, and a localKeyId is required to match the - * private key with the certificate. + * private key with the certificate. Trusted certificate entries are identified + * by the presence of an trustedKeyUsage attribute. * * @author Seema Malkani * @author Jeff Nisewanger @@ -136,6 +140,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { private static final int keyBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 2}; private static final int certBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 3}; + private static final int secretBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 5}; private static final int pkcs9Name[] = {1, 2, 840, 113549, 1, 9, 20}; private static final int pkcs9KeyId[] = {1, 2, 840, 113549, 1, 9, 21}; @@ -147,15 +152,26 @@ public final class PKCS12KeyStore extends KeyStoreSpi { private static final int pbeWithSHAAnd3KeyTripleDESCBC[] = {1, 2, 840, 113549, 1, 12, 1, 3}; private static final int pbes2[] = {1, 2, 840, 113549, 1, 5, 13}; + // TODO: temporary Oracle OID + /* + * { joint-iso-itu-t(2) country(16) us(840) organization(1) oracle(113894) + * jdk(746875) crypto(1) id-at-trustedKeyUsage(1) } + */ + private static final int TrustedKeyUsage[] = + {2, 16, 840, 1, 113894, 746875, 1, 1}; + private static final int AnyExtendedKeyUsage[] = {2, 5, 29, 37, 0}; private static ObjectIdentifier PKCS8ShroudedKeyBag_OID; private static ObjectIdentifier CertBag_OID; + private static ObjectIdentifier SecretBag_OID; private static ObjectIdentifier PKCS9FriendlyName_OID; private static ObjectIdentifier PKCS9LocalKeyId_OID; private static ObjectIdentifier PKCS9CertType_OID; private static ObjectIdentifier pbeWithSHAAnd40BitRC2CBC_OID; private static ObjectIdentifier pbeWithSHAAnd3KeyTripleDESCBC_OID; private static ObjectIdentifier pbes2_OID; + private static ObjectIdentifier TrustedKeyUsage_OID; + private static ObjectIdentifier[] AnyUsage; private int counter = 0; private static final int iterationCount = 1024; @@ -166,6 +182,12 @@ public final class PKCS12KeyStore extends KeyStoreSpi { // in pkcs12 with one private key entry and associated cert-chain private int privateKeyCount = 0; + // secret key count + private int secretKeyCount = 0; + + // certificate count + private int certificateCount = 0; + // the source of randomness private SecureRandom random; @@ -173,6 +195,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { try { PKCS8ShroudedKeyBag_OID = new ObjectIdentifier(keyBag); CertBag_OID = new ObjectIdentifier(certBag); + SecretBag_OID = new ObjectIdentifier(secretBag); PKCS9FriendlyName_OID = new ObjectIdentifier(pkcs9Name); PKCS9LocalKeyId_OID = new ObjectIdentifier(pkcs9KeyId); PKCS9CertType_OID = new ObjectIdentifier(pkcs9certType); @@ -181,38 +204,67 @@ public final class PKCS12KeyStore extends KeyStoreSpi { pbeWithSHAAnd3KeyTripleDESCBC_OID = new ObjectIdentifier(pbeWithSHAAnd3KeyTripleDESCBC); pbes2_OID = new ObjectIdentifier(pbes2); + TrustedKeyUsage_OID = new ObjectIdentifier(TrustedKeyUsage); + AnyUsage = new ObjectIdentifier[]{ + new ObjectIdentifier(AnyExtendedKeyUsage)}; } catch (IOException ioe) { // should not happen } } - // Private keys and their supporting certificate chains - private static class KeyEntry { + // A keystore entry and associated attributes + private static class Entry { Date date; // the creation date of this entry + String alias; + byte[] keyId; + Set attributes; + } + + // A key entry + private static class KeyEntry extends Entry { + } + + // A private key entry and its supporting certificate chain + private static class PrivateKeyEntry extends KeyEntry { byte[] protectedPrivKey; Certificate chain[]; - byte[] keyId; - String alias; }; - // A certificate with its PKCS #9 attributes - private static class CertEntry { + // A secret key + private static class SecretKeyEntry extends KeyEntry { + byte[] protectedSecretKey; + }; + + // A certificate entry + private static class CertEntry extends Entry { final X509Certificate cert; - final byte[] keyId; - final String alias; + ObjectIdentifier[] trustedKeyUsage; + CertEntry(X509Certificate cert, byte[] keyId, String alias) { + this(cert, keyId, alias, null, null); + } + + CertEntry(X509Certificate cert, byte[] keyId, String alias, + ObjectIdentifier[] trustedKeyUsage, + Set attributes) { + this.date = new Date(); this.cert = cert; this.keyId = keyId; this.alias = alias; + this.trustedKeyUsage = trustedKeyUsage; + this.attributes = new HashSet<>(); + if (attributes != null) { + this.attributes.addAll(attributes); + } } } /** - * Private keys and certificates are stored in a hashtable. - * Hash entries are keyed by alias names. + * Private keys and certificates are stored in a map. + * Map entries are keyed by alias names. */ - private Hashtable entries = - new Hashtable(); + private Map entries = + Collections.synchronizedMap(new LinkedHashMap()); private ArrayList keyList = new ArrayList(); private LinkedHashMap certsMap = @@ -237,15 +289,22 @@ public final class PKCS12KeyStore extends KeyStoreSpi { public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException, UnrecoverableKeyException { - KeyEntry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); + Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); Key key = null; - if (entry == null) { + if (entry == null || (!(entry instanceof KeyEntry))) { return null; } - // get the encoded private key - byte[] encrBytes = entry.protectedPrivKey; + // get the encoded private key or secret key + byte[] encrBytes = null; + if (entry instanceof PrivateKeyEntry) { + encrBytes = ((PrivateKeyEntry) entry).protectedPrivKey; + } else if (entry instanceof SecretKeyEntry) { + encrBytes = ((SecretKeyEntry) entry).protectedSecretKey; + } else { + throw new UnrecoverableKeyException("Error locating key"); + } byte[] encryptedKey; AlgorithmParameters algParams; @@ -271,14 +330,14 @@ public final class PKCS12KeyStore extends KeyStoreSpi { } try { - byte[] privateKeyInfo; + byte[] keyInfo; while (true) { try { // Use JCE SecretKey skey = getPBEKey(password); Cipher cipher = Cipher.getInstance(algOid.toString()); cipher.init(Cipher.DECRYPT_MODE, skey, algParams); - privateKeyInfo = cipher.doFinal(encryptedKey); + keyInfo = cipher.doFinal(encryptedKey); break; } catch (Exception e) { if (password.length == 0) { @@ -291,27 +350,52 @@ public final class PKCS12KeyStore extends KeyStoreSpi { } } - PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(privateKeyInfo); - /* * Parse the key algorithm and then use a JCA key factory - * to create the private key. + * to re-create the key. */ - DerValue val = new DerValue(privateKeyInfo); + DerValue val = new DerValue(keyInfo); DerInputStream in = val.toDerInputStream(); int i = in.getInteger(); DerValue[] value = in.getSequence(2); AlgorithmId algId = new AlgorithmId(value[0].getOID()); - String algName = algId.getName(); + String keyAlgo = algId.getName(); - KeyFactory kfac = KeyFactory.getInstance(algName); - key = kfac.generatePrivate(kspec); + // decode private key + if (entry instanceof PrivateKeyEntry) { + KeyFactory kfac = KeyFactory.getInstance(keyAlgo); + PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(keyInfo); + key = kfac.generatePrivate(kspec); - if (debug != null) { - debug.println("Retrieved a protected private key at alias '" + - alias + "'"); + if (debug != null) { + debug.println("Retrieved a protected private key (" + + key.getClass().getName() + ") at alias '" + alias + + "'"); + } + + // decode secret key + } else { + SecretKeyFactory sKeyFactory = + SecretKeyFactory.getInstance(keyAlgo); + byte[] keyBytes = in.getOctetString(); + SecretKeySpec secretKeySpec = + new SecretKeySpec(keyBytes, keyAlgo); + + // Special handling required for PBE: needs a PBEKeySpec + if (keyAlgo.startsWith("PBE")) { + KeySpec pbeKeySpec = + sKeyFactory.getKeySpec(secretKeySpec, PBEKeySpec.class); + key = sKeyFactory.generateSecret(pbeKeySpec); + } else { + key = sKeyFactory.generateSecret(secretKeySpec); + } + + if (debug != null) { + debug.println("Retrieved a protected secret key (" + + key.getClass().getName() + ") at alias '" + alias + + "'"); + } } - } catch (Exception e) { UnrecoverableKeyException uke = new UnrecoverableKeyException("Get Key failed: " + @@ -334,19 +418,19 @@ public final class PKCS12KeyStore extends KeyStoreSpi { * key entry without a certificate chain). */ public Certificate[] engineGetCertificateChain(String alias) { - KeyEntry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); - if (entry != null) { - if (entry.chain == null) { + Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); + if (entry != null && entry instanceof PrivateKeyEntry) { + if (((PrivateKeyEntry) entry).chain == null) { return null; } else { if (debug != null) { debug.println("Retrieved a " + - entry.chain.length + + ((PrivateKeyEntry) entry).chain.length + "-certificate chain at alias '" + alias + "'"); } - return entry.chain.clone(); + return ((PrivateKeyEntry) entry).chain.clone(); } } else { return null; @@ -369,9 +453,28 @@ public final class PKCS12KeyStore extends KeyStoreSpi { * does not contain a certificate. */ public Certificate engineGetCertificate(String alias) { - KeyEntry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); - if (entry != null) { - if (entry.chain == null) { + Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); + if (entry == null) { + return null; + } + if (entry instanceof CertEntry && + ((CertEntry) entry).trustedKeyUsage != null) { + + if (debug != null) { + if (Arrays.equals(AnyUsage, + ((CertEntry) entry).trustedKeyUsage)) { + debug.println("Retrieved a certificate at alias '" + alias + + "' (trusted for any purpose)"); + } else { + debug.println("Retrieved a certificate at alias '" + alias + + "' (trusted for limited purposes)"); + } + } + + return ((CertEntry) entry).cert; + + } else if (entry instanceof PrivateKeyEntry) { + if (((PrivateKeyEntry) entry).chain == null) { return null; } else { @@ -380,8 +483,9 @@ public final class PKCS12KeyStore extends KeyStoreSpi { "'"); } - return entry.chain[0]; + return ((PrivateKeyEntry) entry).chain[0]; } + } else { return null; } @@ -396,7 +500,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { * not exist */ public Date engineGetCreationDate(String alias) { - KeyEntry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); + Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); if (entry != null) { return new Date(entry.date.getTime()); } else { @@ -434,7 +538,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { new KeyStore.PasswordProtection(password); try { - setKeyEntry(alias, key, passwordProtection, chain); + setKeyEntry(alias, key, passwordProtection, chain, null); } finally { try { @@ -446,57 +550,94 @@ public final class PKCS12KeyStore extends KeyStoreSpi { } /* - * Sets a key entry + * Sets a key entry (with attributes, when present) */ private void setKeyEntry(String alias, Key key, - KeyStore.PasswordProtection passwordProtection, Certificate[] chain) + KeyStore.PasswordProtection passwordProtection, Certificate[] chain, + Set attributes) throws KeyStoreException { try { - KeyEntry entry = new KeyEntry(); - entry.date = new Date(); + Entry entry; if (key instanceof PrivateKey) { + PrivateKeyEntry keyEntry = new PrivateKeyEntry(); + keyEntry.date = new Date(); + if ((key.getFormat().equals("PKCS#8")) || (key.getFormat().equals("PKCS8"))) { - // Encrypt the private key if (debug != null) { - debug.println("Setting a protected private key at " + - "alias '" + alias + "'"); - } + debug.println("Setting a protected private key (" + + key.getClass().getName() + ") at alias '" + alias + + "'"); + } - entry.protectedPrivKey = + // Encrypt the private key + keyEntry.protectedPrivKey = encryptPrivateKey(key.getEncoded(), passwordProtection); } else { throw new KeyStoreException("Private key is not encoded" + "as PKCS#8"); } - } else { - throw new KeyStoreException("Key is not a PrivateKey"); - } - // clone the chain - if (chain != null) { - // validate cert-chain - if ((chain.length > 1) && (!validateChain(chain))) - throw new KeyStoreException("Certificate chain is " + - "not validate"); - entry.chain = chain.clone(); + // clone the chain + if (chain != null) { + // validate cert-chain + if ((chain.length > 1) && (!validateChain(chain))) + throw new KeyStoreException("Certificate chain is " + + "not valid"); + keyEntry.chain = chain.clone(); + certificateCount += chain.length; + + if (debug != null) { + debug.println("Setting a " + chain.length + + "-certificate chain at alias '" + alias + "'"); + } + } + privateKeyCount++; + entry = keyEntry; + + } else if (key instanceof SecretKey) { + SecretKeyEntry keyEntry = new SecretKeyEntry(); + keyEntry.date = new Date(); + + // Encode secret key in a PKCS#8 + DerOutputStream pkcs8 = new DerOutputStream(); + DerOutputStream secretKeyInfo = new DerOutputStream(); + secretKeyInfo.putInteger(0); + AlgorithmId algId = AlgorithmId.get(key.getAlgorithm()); + algId.encode(secretKeyInfo); + secretKeyInfo.putOctetString(key.getEncoded()); + pkcs8.write(DerValue.tag_Sequence, secretKeyInfo); + + // Encrypt the secret key (using same PBE as for private keys) + keyEntry.protectedSecretKey = + encryptPrivateKey(pkcs8.toByteArray(), passwordProtection); if (debug != null) { - debug.println("Setting a " + chain.length + - "-certificate chain at alias '" + alias + "'"); + debug.println("Setting a protected secret key (" + + key.getClass().getName() + ") at alias '" + alias + + "'"); } + secretKeyCount++; + entry = keyEntry; + + } else { + throw new KeyStoreException("Unsupported Key type"); } + entry.attributes = new HashSet<>(); + if (attributes != null) { + entry.attributes.addAll(attributes); + } // set the keyId to current date entry.keyId = ("Time " + (entry.date).getTime()).getBytes("UTF8"); // set the alias entry.alias = alias.toLowerCase(Locale.ENGLISH); - // add the entry entries.put(alias.toLowerCase(Locale.ENGLISH), entry); + } catch (Exception nsae) { throw new KeyStoreException("Key protection " + " algorithm not found: " + nsae, nsae); @@ -530,7 +671,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { Certificate[] chain) throws KeyStoreException { - // key must be encoded as EncryptedPrivateKeyInfo + // Private key must be encoded as EncryptedPrivateKeyInfo // as defined in PKCS#8 try { new EncryptedPrivateKeyInfo(key); @@ -539,11 +680,12 @@ public final class PKCS12KeyStore extends KeyStoreSpi { + " as PKCS#8 EncryptedPrivateKeyInfo: " + ioe, ioe); } - KeyEntry entry = new KeyEntry(); + PrivateKeyEntry entry = new PrivateKeyEntry(); entry.date = new Date(); if (debug != null) { - debug.println("Setting a protected key at alias '" + alias + "'"); + debug.println("Setting a protected private key at alias '" + + alias + "'"); } try { @@ -557,7 +699,8 @@ public final class PKCS12KeyStore extends KeyStoreSpi { entry.protectedPrivKey = key.clone(); if (chain != null) { - entry.chain = chain.clone(); + entry.chain = chain.clone(); + certificateCount += chain.length; if (debug != null) { debug.println("Setting a " + entry.chain.length + @@ -566,6 +709,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { } // add the entry + privateKeyCount++; entries.put(alias.toLowerCase(Locale.ENGLISH), entry); } @@ -644,6 +788,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { PBEKeySpec keySpec = new PBEKeySpec(password); SecretKeyFactory skFac = SecretKeyFactory.getInstance("PBE"); skey = skFac.generateSecret(keySpec); + keySpec.clearPassword(); } catch (Exception e) { throw new IOException("getSecretKey failed: " + e.getMessage(), e); @@ -695,7 +840,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { new PrivilegedAction() { public String run() { String prop = - Security.getProperty( + Security.getProperty KEY_PROTECTION_ALGORITHM[0]); if (prop == null) { prop = Security.getProperty( @@ -762,17 +907,36 @@ public final class PKCS12KeyStore extends KeyStoreSpi { * @param cert the certificate * * @exception KeyStoreException if the given alias already exists and does - * identify a key entry, or on an attempt to create a - * trusted cert entry which is currently not supported. + * not identify a trusted certificate entry, or this operation fails + * for some other reason. */ public synchronized void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException { - KeyEntry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); - if (entry != null) { + setCertEntry(alias, cert, null); + } + + /* + * Sets a trusted cert entry (with attributes, when present) + */ + private void setCertEntry(String alias, Certificate cert, + Set attributes) throws KeyStoreException { + + Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); + if (entry != null && entry instanceof KeyEntry) { throw new KeyStoreException("Cannot overwrite own certificate"); - } else - throw new KeyStoreException("TrustedCertEntry not supported"); + } + + CertEntry certEntry = + new CertEntry((X509Certificate) cert, null, alias, AnyUsage, + attributes); + certificateCount++; + entries.put(alias, certEntry); + + if (debug != null) { + debug.println("Setting a trusted certificate at alias '" + alias + + "'"); + } } /** @@ -789,6 +953,18 @@ public final class PKCS12KeyStore extends KeyStoreSpi { debug.println("Removing entry at alias '" + alias + "'"); } + Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); + if (entry instanceof PrivateKeyEntry) { + PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry; + if (keyEntry.chain != null) { + certificateCount -= keyEntry.chain.length; + } + privateKeyCount--; + } else if (entry instanceof CertEntry) { + certificateCount--; + } else if (entry instanceof SecretKeyEntry) { + secretKeyCount--; + } entries.remove(alias.toLowerCase(Locale.ENGLISH)); } @@ -798,7 +974,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { * @return enumeration of the alias names */ public Enumeration engineAliases() { - return entries.keys(); + return Collections.enumeration(entries.keySet()); } /** @@ -829,8 +1005,8 @@ public final class PKCS12KeyStore extends KeyStoreSpi { * key entry, false otherwise. */ public boolean engineIsKeyEntry(String alias) { - KeyEntry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); - if (entry != null) { + Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); + if (entry != null && entry instanceof KeyEntry) { return true; } else { return false; @@ -845,8 +1021,13 @@ public final class PKCS12KeyStore extends KeyStoreSpi { * trusted certificate entry, false otherwise. */ public boolean engineIsCertificateEntry(String alias) { - // TrustedCertEntry is not supported - return false; + Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); + if (entry != null && entry instanceof CertEntry && + ((CertEntry) entry).trustedKeyUsage != null) { + return true; + } else { + return false; + } } /** @@ -868,11 +1049,18 @@ public final class PKCS12KeyStore extends KeyStoreSpi { public String engineGetCertificateAlias(Certificate cert) { Certificate certElem = null; - for (Enumeration e = entries.keys(); e.hasMoreElements(); ) { + for (Enumeration e = engineAliases(); e.hasMoreElements(); ) { String alias = e.nextElement(); - KeyEntry entry = entries.get(alias); - if (entry.chain != null) { - certElem = entry.chain[0]; + Entry entry = entries.get(alias); + if (entry instanceof PrivateKeyEntry) { + if (((PrivateKeyEntry) entry).chain != null) { + certElem = ((PrivateKeyEntry) entry).chain[0]; + } + } else if (entry instanceof CertEntry && + ((CertEntry) entry).trustedKeyUsage != null) { + certElem = ((CertEntry) entry).cert; + } else { + continue; } if (certElem.equals(cert)) { return alias; @@ -918,26 +1106,32 @@ public final class PKCS12KeyStore extends KeyStoreSpi { DerOutputStream authSafeContentInfo = new DerOutputStream(); // -- create safeContent Data ContentInfo - if (debug != null) { - debug.println("Storing " + privateKeyCount + - " protected key(s) in a PKCS#7 data content-type"); - } + if (privateKeyCount > 0 || secretKeyCount > 0) { - byte[] safeContentData = createSafeContent(); - ContentInfo dataContentInfo = new ContentInfo(safeContentData); - dataContentInfo.encode(authSafeContentInfo); + if (debug != null) { + debug.println("Storing " + privateKeyCount + + " protected key(s) in a PKCS#7 data content-type"); + } + + byte[] safeContentData = createSafeContent(); + ContentInfo dataContentInfo = new ContentInfo(safeContentData); + dataContentInfo.encode(authSafeContentInfo); + } // -- create EncryptedContentInfo - if (debug != null) { - debug.println("Storing certificate(s) in a PKCS#7 encryptedData " + - "content-type"); - } + if (certificateCount > 0) { - byte[] encrData = createEncryptedData(password); - ContentInfo encrContentInfo = + if (debug != null) { + debug.println("Storing " + certificateCount + + " certificate(s) in a PKCS#7 encryptedData content-type"); + } + + byte[] encrData = createEncryptedData(password); + ContentInfo encrContentInfo = new ContentInfo(ContentInfo.ENCRYPTED_DATA_OID, new DerValue(encrData)); - encrContentInfo.encode(authSafeContentInfo); + encrContentInfo.encode(authSafeContentInfo); + } // wrap as SequenceOf ContentInfos DerOutputStream cInfo = new DerOutputStream(); @@ -962,6 +1156,207 @@ public final class PKCS12KeyStore extends KeyStoreSpi { stream.flush(); } + /** + * Gets a KeyStore.Entry for the specified alias + * with the specified protection parameter. + * + * @param alias get the KeyStore.Entry for this alias + * @param protParam the ProtectionParameter + * used to protect the Entry, + * which may be null + * + * @return the KeyStore.Entry for the specified alias, + * or null if there is no such entry + * + * @exception KeyStoreException if the operation failed + * @exception NoSuchAlgorithmException if the algorithm for recovering the + * entry cannot be found + * @exception UnrecoverableEntryException if the specified + * protParam were insufficient or invalid + * @exception UnrecoverableKeyException if the entry is a + * PrivateKeyEntry or SecretKeyEntry + * and the specified protParam does not contain + * the information needed to recover the key (e.g. wrong password) + * + * @since 1.5 + */ + @Override + public KeyStore.Entry engineGetEntry(String alias, + KeyStore.ProtectionParameter protParam) + throws KeyStoreException, NoSuchAlgorithmException, + UnrecoverableEntryException { + + if (!engineContainsAlias(alias)) { + return null; + } + + Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); + if (protParam == null) { + if (engineIsCertificateEntry(alias)) { + if (entry instanceof CertEntry && + ((CertEntry) entry).trustedKeyUsage != null) { + + if (debug != null) { + debug.println("Retrieved a trusted certificate at " + + "alias '" + alias + "'"); + } + + return new KeyStore.TrustedCertificateEntry( + ((CertEntry)entry).cert, getAttributes(entry)); + } + } else { + throw new UnrecoverableKeyException + ("requested entry requires a password"); + } + } + + if (protParam instanceof KeyStore.PasswordProtection) { + if (engineIsCertificateEntry(alias)) { + throw new UnsupportedOperationException + ("trusted certificate entries are not password-protected"); + } else if (engineIsKeyEntry(alias)) { + KeyStore.PasswordProtection pp = + (KeyStore.PasswordProtection)protParam; + char[] password = pp.getPassword(); + + Key key = engineGetKey(alias, password); + if (key instanceof PrivateKey) { + Certificate[] chain = engineGetCertificateChain(alias); + + return new KeyStore.PrivateKeyEntry((PrivateKey)key, chain, + getAttributes(entry)); + + } else if (key instanceof SecretKey) { + + return new KeyStore.SecretKeyEntry((SecretKey)key, + getAttributes(entry)); + } + } else if (!engineIsKeyEntry(alias)) { + throw new UnsupportedOperationException + ("untrusted certificate entries are not " + + "password-protected"); + } + } + + throw new UnsupportedOperationException(); + } + + /** + * Saves a KeyStore.Entry under the specified alias. + * The specified protection parameter is used to protect the + * Entry. + * + *

    If an entry already exists for the specified alias, + * it is overridden. + * + * @param alias save the KeyStore.Entry under this alias + * @param entry the Entry to save + * @param protParam the ProtectionParameter + * used to protect the Entry, + * which may be null + * + * @exception KeyStoreException if this operation fails + * + * @since 1.5 + */ + @Override + public synchronized void engineSetEntry(String alias, KeyStore.Entry entry, + KeyStore.ProtectionParameter protParam) throws KeyStoreException { + + // get password + if (protParam != null && + !(protParam instanceof KeyStore.PasswordProtection)) { + throw new KeyStoreException("unsupported protection parameter"); + } + KeyStore.PasswordProtection pProtect = null; + if (protParam != null) { + pProtect = (KeyStore.PasswordProtection)protParam; + } + + // set entry + if (entry instanceof KeyStore.TrustedCertificateEntry) { + if (protParam != null && pProtect.getPassword() != null) { + // pre-1.5 style setCertificateEntry did not allow password + throw new KeyStoreException + ("trusted certificate entries are not password-protected"); + } else { + KeyStore.TrustedCertificateEntry tce = + (KeyStore.TrustedCertificateEntry)entry; + setCertEntry(alias, tce.getTrustedCertificate(), + tce.getAttributes()); + + return; + } + } else if (entry instanceof KeyStore.PrivateKeyEntry) { + if (pProtect == null || pProtect.getPassword() == null) { + // pre-1.5 style setKeyEntry required password + throw new KeyStoreException + ("non-null password required to create PrivateKeyEntry"); + } else { + KeyStore.PrivateKeyEntry pke = (KeyStore.PrivateKeyEntry)entry; + setKeyEntry(alias, pke.getPrivateKey(), pProtect, + pke.getCertificateChain(), pke.getAttributes()); + + return; + } + } else if (entry instanceof KeyStore.SecretKeyEntry) { + if (pProtect == null || pProtect.getPassword() == null) { + // pre-1.5 style setKeyEntry required password + throw new KeyStoreException + ("non-null password required to create SecretKeyEntry"); + } else { + KeyStore.SecretKeyEntry ske = (KeyStore.SecretKeyEntry)entry; + setKeyEntry(alias, ske.getSecretKey(), pProtect, + (Certificate[])null, ske.getAttributes()); + + return; + } + } + + throw new KeyStoreException + ("unsupported entry type: " + entry.getClass().getName()); + } + + /* + * Assemble the entry attributes + */ + private Set getAttributes(Entry entry) { + + if (entry.attributes == null) { + entry.attributes = new HashSet<>(); + } + + // friendlyName + entry.attributes.add(new PKCS12Attribute( + PKCS9FriendlyName_OID.toString(), entry.alias)); + + // localKeyID + byte[] keyIdValue = entry.keyId; + if (keyIdValue != null) { + entry.attributes.add(new PKCS12Attribute( + PKCS9LocalKeyId_OID.toString(), Debug.toString(keyIdValue))); + } + + // trustedKeyUsage + if (entry instanceof CertEntry) { + ObjectIdentifier[] trustedKeyUsageValue = + ((CertEntry) entry).trustedKeyUsage; + if (trustedKeyUsageValue != null) { + if (trustedKeyUsageValue.length == 1) { // omit brackets + entry.attributes.add(new PKCS12Attribute( + TrustedKeyUsage_OID.toString(), + trustedKeyUsageValue[0].toString())); + } else { // multi-valued + entry.attributes.add(new PKCS12Attribute( + TrustedKeyUsage_OID.toString(), + Arrays.toString(trustedKeyUsageValue))); + } + } + } + + return entry.attributes; + } + /* * Generate Hash. */ @@ -1036,11 +1431,12 @@ public final class PKCS12KeyStore extends KeyStoreSpi { /* - * Create PKCS#12 Attributes, friendlyName and localKeyId. + * Create PKCS#12 Attributes, friendlyName, localKeyId and trustedKeyUsage. * * Although attributes are optional, they could be required. * For e.g. localKeyId attribute is required to match the * private key with the associated end-entity certificate. + * The trustedKeyUsage attribute is used to denote a trusted certificate. * * PKCS8ShroudedKeyBags include unique localKeyID and friendlyName. * CertBags may or may not include attributes depending on the type @@ -1062,20 +1458,28 @@ public final class PKCS12KeyStore extends KeyStoreSpi { * friendlyName unique same/ same/ unique * unique unique/ * null + * trustedKeyUsage - - - true * * Note: OpenSSL adds friendlyName for end-entity cert only, and * removes the localKeyID and friendlyName for CA certs. * If the CertBag did not have a friendlyName, most vendors will * add it, and assign it to the DN of the cert. */ - private byte[] getBagAttributes(String alias, byte[] keyId) - throws IOException { + private byte[] getBagAttributes(String alias, byte[] keyId, + Set attributes) throws IOException { + return getBagAttributes(alias, keyId, null, attributes); + } + + private byte[] getBagAttributes(String alias, byte[] keyId, + ObjectIdentifier[] trustedUsage, + Set attributes) throws IOException { byte[] localKeyID = null; byte[] friendlyName = null; + byte[] trustedKeyUsage = null; - // return null if both attributes are null - if ((alias == null) && (keyId == null)) { + // return null if all three attributes are null + if ((alias == null) && (keyId == null) && (trustedKeyUsage == null)) { return null; } @@ -1106,6 +1510,20 @@ public final class PKCS12KeyStore extends KeyStoreSpi { localKeyID = bagAttrValue2.toByteArray(); } + // Encode the trustedKeyUsage oid. + if (trustedUsage != null) { + DerOutputStream bagAttr3 = new DerOutputStream(); + bagAttr3.putOID(TrustedKeyUsage_OID); + DerOutputStream bagAttrContent3 = new DerOutputStream(); + DerOutputStream bagAttrValue3 = new DerOutputStream(); + for (ObjectIdentifier usage : trustedUsage) { + bagAttrContent3.putOID(usage); + } + bagAttr3.write(DerValue.tag_Set, bagAttrContent3); + bagAttrValue3.write(DerValue.tag_Sequence, bagAttr3); + trustedKeyUsage = bagAttrValue3.toByteArray(); + } + DerOutputStream attrs = new DerOutputStream(); if (friendlyName != null) { attrs.write(friendlyName); @@ -1113,11 +1531,20 @@ public final class PKCS12KeyStore extends KeyStoreSpi { if (localKeyID != null) { attrs.write(localKeyID); } + if (trustedKeyUsage != null) { + attrs.write(trustedKeyUsage); + } + + if (attributes != null) { + for (KeyStore.Entry.Attribute attribute : attributes) { + attrs.write(((PKCS12Attribute) attribute).getEncoded()); + } + } + bagAttrs.write(DerValue.tag_Set, attrs); return bagAttrs.toByteArray(); } - /* * Create EncryptedData content type, that contains EncryptedContentInfo. * Includes certificates in individual SafeBags of type CertBag. @@ -1128,17 +1555,26 @@ public final class PKCS12KeyStore extends KeyStoreSpi { throws CertificateException, IOException { DerOutputStream out = new DerOutputStream(); - for (Enumeration e = entries.keys(); e.hasMoreElements(); ) { + for (Enumeration e = engineAliases(); e.hasMoreElements(); ) { String alias = e.nextElement(); - KeyEntry entry = entries.get(alias); + Entry entry = entries.get(alias); // certificate chain - int chainLen; - if (entry.chain == null) { - chainLen = 0; - } else { - chainLen = entry.chain.length; + int chainLen = 1; + Certificate[] certs = null; + + if (entry instanceof PrivateKeyEntry) { + PrivateKeyEntry keyEntry = (PrivateKeyEntry) entry; + if (keyEntry.chain == null) { + chainLen = 0; + } else { + chainLen = keyEntry.chain.length; + } + certs = keyEntry.chain; + + } else if (entry instanceof CertEntry) { + certs = new Certificate[]{((CertEntry) entry).cert}; } for (int i = 0; i < chainLen; i++) { @@ -1152,7 +1588,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { // write encoded certs in a context-specific tag DerOutputStream certValue = new DerOutputStream(); - X509Certificate cert = (X509Certificate)entry.chain[i]; + X509Certificate cert = (X509Certificate) certs[i]; certValue.putOctetString(cert.getEncoded()); certBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0), certValue); @@ -1175,7 +1611,18 @@ public final class PKCS12KeyStore extends KeyStoreSpi { byte[] bagAttrs = null; if (i == 0) { // Only End-Entity Cert should have a localKeyId. - bagAttrs = getBagAttributes(entry.alias, entry.keyId); + if (entry instanceof KeyEntry) { + KeyEntry keyEntry = (KeyEntry) entry; + bagAttrs = + getBagAttributes(keyEntry.alias, keyEntry.keyId, + keyEntry.attributes); + } else { + CertEntry certEntry = (CertEntry) entry; + bagAttrs = + getBagAttributes(certEntry.alias, certEntry.keyId, + certEntry.trustedKeyUsage, + certEntry.attributes); + } } else { // Trusted root CA certs and Intermediate CA certs do not // need to have a localKeyId, and hence localKeyId is null @@ -1184,7 +1631,8 @@ public final class PKCS12KeyStore extends KeyStoreSpi { // certificate chain to have unique or null localKeyID. // However, IE/OpenSSL do not impose this restriction. bagAttrs = getBagAttributes( - cert.getSubjectX500Principal().getName(), null); + cert.getSubjectX500Principal().getName(), null, + entry.attributes); } if (bagAttrs != null) { safeBag.write(bagAttrs); @@ -1214,6 +1662,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { /* * Create SafeContent Data content type. + * Includes encrypted secret key in a SafeBag of type SecretBag. * Includes encrypted private key in a SafeBag of type PKCS8ShroudedKeyBag. * Each PKCS8ShroudedKeyBag includes pkcs12 attributes * (see comments in getBagAttributes) @@ -1222,33 +1671,74 @@ public final class PKCS12KeyStore extends KeyStoreSpi { throws CertificateException, IOException { DerOutputStream out = new DerOutputStream(); - for (Enumeration e = entries.keys(); e.hasMoreElements(); ) { + for (Enumeration e = engineAliases(); e.hasMoreElements(); ) { String alias = e.nextElement(); - KeyEntry entry = entries.get(alias); - - // Create SafeBag of type pkcs8ShroudedKeyBag - DerOutputStream safeBag = new DerOutputStream(); - safeBag.putOID(PKCS8ShroudedKeyBag_OID); - - // get the encrypted private key - byte[] encrBytes = entry.protectedPrivKey; - EncryptedPrivateKeyInfo encrInfo = null; - try { - encrInfo = new EncryptedPrivateKeyInfo(encrBytes); - } catch (IOException ioe) { - throw new IOException("Private key not stored as " - + "PKCS#8 EncryptedPrivateKeyInfo" + ioe.getMessage()); + Entry entry = entries.get(alias); + if (entry == null || (!(entry instanceof KeyEntry))) { + continue; } + DerOutputStream safeBag = new DerOutputStream(); + KeyEntry keyEntry = (KeyEntry) entry; - // Wrap the EncryptedPrivateKeyInfo in a context-specific tag. - DerOutputStream bagValue = new DerOutputStream(); - bagValue.write(encrInfo.getEncoded()); - safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, + // DER encode the private key + if (keyEntry instanceof PrivateKeyEntry) { + // Create SafeBag of type pkcs8ShroudedKeyBag + safeBag.putOID(PKCS8ShroudedKeyBag_OID); + + // get the encrypted private key + byte[] encrBytes = ((PrivateKeyEntry)keyEntry).protectedPrivKey; + EncryptedPrivateKeyInfo encrInfo = null; + try { + encrInfo = new EncryptedPrivateKeyInfo(encrBytes); + + } catch (IOException ioe) { + throw new IOException("Private key not stored as " + + "PKCS#8 EncryptedPrivateKeyInfo" + + ioe.getMessage()); + } + + // Wrap the EncryptedPrivateKeyInfo in a context-specific tag. + DerOutputStream bagValue = new DerOutputStream(); + bagValue.write(encrInfo.getEncoded()); + safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte) 0), bagValue); + // DER encode the secret key + } else if (keyEntry instanceof SecretKeyEntry) { + // Create SafeBag of type SecretBag + safeBag.putOID(SecretBag_OID); + + // Create a SecretBag + DerOutputStream secretBag = new DerOutputStream(); + secretBag.putOID(PKCS8ShroudedKeyBag_OID); + + // Write secret key in a context-specific tag + DerOutputStream secretKeyValue = new DerOutputStream(); + secretKeyValue.putOctetString( + ((SecretKeyEntry) keyEntry).protectedSecretKey); + secretBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0), secretKeyValue); + + // Wrap SecretBag in a Sequence + DerOutputStream secretBagSeq = new DerOutputStream(); + secretBagSeq.write(DerValue.tag_Sequence, secretBag); + byte[] secretBagValue = secretBagSeq.toByteArray(); + + // Wrap the secret bag in a context-specific tag. + DerOutputStream bagValue = new DerOutputStream(); + bagValue.write(secretBagValue); + + // Write SafeBag value + safeBag.write(DerValue.createTag(DerValue.TAG_CONTEXT, + true, (byte) 0), bagValue); + } else { + continue; // skip this entry + } + // write SafeBag Attributes - byte[] bagAttrs = getBagAttributes(alias, entry.keyId); + byte[] bagAttrs = + getBagAttributes(alias, entry.keyId, entry.attributes); safeBag.write(bagAttrs); // wrap as Sequence @@ -1377,8 +1867,10 @@ public final class PKCS12KeyStore extends KeyStoreSpi { DerValue[] safeContentsArray = as.getSequence(2); int count = safeContentsArray.length; - // reset the count at the start + // reset the counters at the start privateKeyCount = 0; + secretKeyCount = 0; + certificateCount = 0; /* * Spin over the ContentInfos. @@ -1445,7 +1937,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { continue; } throw new IOException( - "failed to decrypt safe contents entry: " + e, e); + "failed to decrypt safe contents entry: " + e, e); } } } else { @@ -1493,9 +1985,10 @@ public final class PKCS12KeyStore extends KeyStoreSpi { /* * Match up private keys with certificate chains. */ - KeyEntry[] list = keyList.toArray(new KeyEntry[keyList.size()]); + PrivateKeyEntry[] list = + keyList.toArray(new PrivateKeyEntry[keyList.size()]); for (int m = 0; m < list.length; m++) { - KeyEntry entry = list[m]; + PrivateKeyEntry entry = list[m]; if (entry.keyId != null) { ArrayList chain = new ArrayList(); @@ -1513,6 +2006,22 @@ public final class PKCS12KeyStore extends KeyStoreSpi { entry.chain = chain.toArray(new Certificate[chain.size()]); } } + + if (debug != null) { + if (privateKeyCount > 0) { + debug.println("Loaded " + privateKeyCount + + " protected private key(s)"); + } + if (secretKeyCount > 0) { + debug.println("Loaded " + secretKeyCount + + " protected secret key(s)"); + } + if (certificateCount > 0) { + debug.println("Loaded " + certificateCount + + " certificate(s)"); + } + } + certEntries.clear(); certsMap.clear(); keyList.clear(); @@ -1523,7 +2032,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { * @param entry the KeyEntry to match * @return a certificate, null if not found */ - private X509Certificate findMatchedCertificate(KeyEntry entry) { + private X509Certificate findMatchedCertificate(PrivateKeyEntry entry) { CertEntry keyIdMatch = null; CertEntry aliasMatch = null; for (CertEntry ce: certEntries) { @@ -1567,7 +2076,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { } bagValue = bagValue.data.getDerValue(); if (bagId.equals((Object)PKCS8ShroudedKeyBag_OID)) { - KeyEntry kEntry = new KeyEntry(); + PrivateKeyEntry kEntry = new PrivateKeyEntry(); kEntry.protectedPrivKey = bagValue.toByteArray(); bagItem = kEntry; privateKeyCount++; @@ -1585,6 +2094,20 @@ public final class PKCS12KeyStore extends KeyStoreSpi { cert = (X509Certificate)cf.generateCertificate (new ByteArrayInputStream(certValue.getOctetString())); bagItem = cert; + certificateCount++; + } else if (bagId.equals((Object)SecretBag_OID)) { + DerInputStream ss = new DerInputStream(bagValue.toByteArray()); + DerValue[] secretValues = ss.getSequence(2); + ObjectIdentifier secretId = secretValues[0].getOID(); + if (!secretValues[1].isContextSpecific((byte)0)) { + throw new IOException( + "unsupported PKCS12 secret value type " + + secretValues[1].tag); + } + DerValue secretValue = secretValues[1].data.getDerValue(); + SecretKeyEntry kEntry = new SecretKeyEntry(); + kEntry.protectedSecretKey = secretValue.getOctetString(); + bagItem = kEntry; } else { if (debug != null) { @@ -1594,7 +2117,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { DerValue[] attrSet; try { - attrSet = sbi.getSet(2); + attrSet = sbi.getSet(3); } catch (IOException e) { // entry does not have attributes // Note: CA certs can have no attributes @@ -1604,11 +2127,13 @@ public final class PKCS12KeyStore extends KeyStoreSpi { String alias = null; byte[] keyId = null; + ObjectIdentifier[] trustedKeyUsage = null; + Set attributes = new HashSet<>(); if (attrSet != null) { for (int j = 0; j < attrSet.length; j++) { - DerInputStream as = - new DerInputStream(attrSet[j].toByteArray()); + byte[] encoded = attrSet[j].toByteArray(); + DerInputStream as = new DerInputStream(encoded); DerValue[] attrSeq = as.getSequence(2); ObjectIdentifier attrId = attrSeq[0].getOID(); DerInputStream vs = @@ -1624,12 +2149,14 @@ public final class PKCS12KeyStore extends KeyStoreSpi { alias = valSet[0].getBMPString(); } else if (attrId.equals((Object)PKCS9LocalKeyId_OID)) { keyId = valSet[0].getOctetString(); - } else { - - if (debug != null) { - debug.println("Unsupported PKCS12 bag attribute: " + - attrId); + } else if + (attrId.equals((Object)TrustedKeyUsage_OID)) { + trustedKeyUsage = new ObjectIdentifier[valSet.length]; + for (int k = 0; k < valSet.length; k++) { + trustedKeyUsage[k] = valSet[k].getOID(); } + } else { + attributes.add(new PKCS12Attribute(encoded)); } } } @@ -1645,16 +2172,19 @@ public final class PKCS12KeyStore extends KeyStoreSpi { */ if (bagItem instanceof KeyEntry) { KeyEntry entry = (KeyEntry)bagItem; - if (keyId == null) { - // Insert a localKeyID for the privateKey - // Note: This is a workaround to allow null localKeyID - // attribute in pkcs12 with one private key entry and - // associated cert-chain - if (privateKeyCount == 1) { - keyId = "01".getBytes("UTF8"); - } else { - continue; - } + + if (bagItem instanceof PrivateKeyEntry) { + if (keyId == null) { + // Insert a localKeyID for the privateKey + // Note: This is a workaround to allow null localKeyID + // attribute in pkcs12 with one private key entry and + // associated cert-chain + if (privateKeyCount == 1) { + keyId = "01".getBytes("UTF8"); + } else { + continue; + } + } } entry.keyId = keyId; // restore date if it exists @@ -1672,11 +2202,16 @@ public final class PKCS12KeyStore extends KeyStoreSpi { date = new Date(); } entry.date = date; - keyList.add(entry); - if (alias == null) + + if (bagItem instanceof PrivateKeyEntry) { + keyList.add((PrivateKeyEntry) entry); + } + if (alias == null) { alias = getUnfriendlyName(); + } entry.alias = alias; entries.put(alias.toLowerCase(Locale.ENGLISH), entry); + } else if (bagItem instanceof X509Certificate) { X509Certificate cert = (X509Certificate)bagItem; // Insert a localKeyID for the corresponding cert @@ -1689,7 +2224,18 @@ public final class PKCS12KeyStore extends KeyStoreSpi { keyId = "01".getBytes("UTF8"); } } - certEntries.add(new CertEntry(cert, keyId, alias)); + if (alias == null) { + alias = getUnfriendlyName(); + } + // Trusted certificate + if (trustedKeyUsage != null) { + CertEntry certEntry = + new CertEntry(cert, keyId, alias, trustedKeyUsage, + attributes); + entries.put(alias.toLowerCase(Locale.ENGLISH), certEntry); + } else { + certEntries.add(new CertEntry(cert, keyId, alias)); + } X500Principal subjectDN = cert.getSubjectX500Principal(); if (subjectDN != null) { if (!certsMap.containsKey(subjectDN)) { diff --git a/jdk/src/share/classes/sun/security/x509/AlgorithmId.java b/jdk/src/share/classes/sun/security/x509/AlgorithmId.java index e2d6c60c111..f34d973fc73 100644 --- a/jdk/src/share/classes/sun/security/x509/AlgorithmId.java +++ b/jdk/src/share/classes/sun/security/x509/AlgorithmId.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -502,6 +502,11 @@ public class AlgorithmId implements Serializable, DerEncoder { return AlgorithmId.ECDH_oid; } + // Secret key algorithms + if (name.equalsIgnoreCase("AES")) { + return AlgorithmId.AES_oid; + } + // Common signature types if (name.equalsIgnoreCase("MD5withRSA") || name.equalsIgnoreCase("MD5/RSA")) { @@ -660,6 +665,12 @@ public class AlgorithmId implements Serializable, DerEncoder { public static final ObjectIdentifier RSA_oid; public static final ObjectIdentifier RSAEncryption_oid; + /* + * COMMON SECRET KEY TYPES + */ + public static final ObjectIdentifier AES_oid = + oid(2, 16, 840, 1, 101, 3, 4, 1); + /* * COMMON SIGNATURE ALGORITHMS */ @@ -893,6 +904,8 @@ public class AlgorithmId implements Serializable, DerEncoder { nameTable.put(EC_oid, "EC"); nameTable.put(ECDH_oid, "ECDH"); + nameTable.put(AES_oid, "AES"); + nameTable.put(sha1WithECDSA_oid, "SHA1withECDSA"); nameTable.put(sha224WithECDSA_oid, "SHA224withECDSA"); nameTable.put(sha256WithECDSA_oid, "SHA256withECDSA"); diff --git a/jdk/test/sun/security/pkcs12/StorePasswordTest.java b/jdk/test/sun/security/pkcs12/StorePasswordTest.java new file mode 100644 index 00000000000..fc2f77c51c1 --- /dev/null +++ b/jdk/test/sun/security/pkcs12/StorePasswordTest.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8005408 + * @summary KeyStore API enhancements + */ + +import java.io.*; +import java.security.*; +import java.util.*; +import javax.crypto.*; +import javax.crypto.spec.*; +import java.security.spec.InvalidKeySpecException; + +// Store a password in a keystore and retrieve it again. + +public class StorePasswordTest { + private final static String DIR = System.getProperty("test.src", "."); + private static final char[] PASSWORD = "passphrase".toCharArray(); + private static final String KEYSTORE = "pwdstore.p12"; + private static final String ALIAS = "my password"; + private static final String USER_PASSWORD = "hello1"; + + public static void main(String[] args) throws Exception { + + new File(KEYSTORE).delete(); + + try { + + KeyStore keystore = KeyStore.getInstance("PKCS12"); + keystore.load(null, null); + + // Set entry + keystore.setEntry(ALIAS, + new KeyStore.SecretKeyEntry(convertPassword(USER_PASSWORD)), + new KeyStore.PasswordProtection(PASSWORD)); + + System.out.println("Storing keystore to: " + KEYSTORE); + keystore.store(new FileOutputStream(KEYSTORE), PASSWORD); + + System.out.println("Loading keystore from: " + KEYSTORE); + keystore.load(new FileInputStream(KEYSTORE), PASSWORD); + System.out.println("Loaded keystore with " + keystore.size() + + " entries"); + KeyStore.Entry entry = keystore.getEntry(ALIAS, + new KeyStore.PasswordProtection(PASSWORD)); + System.out.println("Retrieved entry: " + entry); + + SecretKey key = (SecretKey) keystore.getKey(ALIAS, PASSWORD); + SecretKeyFactory factory = + SecretKeyFactory.getInstance(key.getAlgorithm()); + PBEKeySpec keySpec = + (PBEKeySpec) factory.getKeySpec(key, PBEKeySpec.class); + char[] pwd = keySpec.getPassword(); + System.out.println("Recovered credential: " + new String(pwd)); + + if (!Arrays.equals(USER_PASSWORD.toCharArray(), pwd)) { + throw new Exception("Failed to recover the stored password"); + } + } finally { + new File(KEYSTORE).delete(); + } + } + + private static SecretKey convertPassword(String password) + throws NoSuchAlgorithmException, InvalidKeySpecException { + SecretKeyFactory factory = SecretKeyFactory.getInstance("PBE"); + return factory.generateSecret(new PBEKeySpec(password.toCharArray())); + } +} diff --git a/jdk/test/sun/security/pkcs12/StoreSecretKeyTest.java b/jdk/test/sun/security/pkcs12/StoreSecretKeyTest.java new file mode 100644 index 00000000000..9a91148a41b --- /dev/null +++ b/jdk/test/sun/security/pkcs12/StoreSecretKeyTest.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8005408 + * @summary KeyStore API enhancements + */ + +import java.io.*; +import java.security.*; +import java.util.*; +import javax.crypto.*; +import javax.crypto.spec.*; + +// Store a secret key in a keystore and retrieve it again. + +public class StoreSecretKeyTest { + private final static String DIR = System.getProperty("test.src", "."); + private static final char[] PASSWORD = "passphrase".toCharArray(); + private static final String KEYSTORE = "keystore.p12"; + private static final String ALIAS = "my secret key"; + + public static void main(String[] args) throws Exception { + + new File(KEYSTORE).delete(); + + try { + + KeyStore keystore = KeyStore.getInstance("PKCS12"); + keystore.load(null, null); + + // Set entry + keystore.setEntry(ALIAS, + new KeyStore.SecretKeyEntry(generateSecretKey("AES", 128)), + new KeyStore.PasswordProtection(PASSWORD)); + + System.out.println("Storing keystore to: " + KEYSTORE); + keystore.store(new FileOutputStream(KEYSTORE), PASSWORD); + + System.out.println("Loading keystore from: " + KEYSTORE); + keystore.load(new FileInputStream(KEYSTORE), PASSWORD); + System.out.println("Loaded keystore with " + keystore.size() + + " entries"); + KeyStore.Entry entry = keystore.getEntry(ALIAS, + new KeyStore.PasswordProtection(PASSWORD)); + System.out.println("Retrieved entry: " + entry); + + if (entry instanceof KeyStore.SecretKeyEntry) { + System.out.println("Retrieved secret key entry: " + + entry); + } else { + throw new Exception("Not a secret key entry"); + } + } finally { + new File(KEYSTORE).delete(); + } + } + + private static SecretKey generateSecretKey(String algorithm, int size) + throws NoSuchAlgorithmException { + KeyGenerator generator = KeyGenerator.getInstance(algorithm); + generator.init(size); + return generator.generateKey(); + } +} diff --git a/jdk/test/sun/security/pkcs12/StoreTrustedCertTest.java b/jdk/test/sun/security/pkcs12/StoreTrustedCertTest.java new file mode 100644 index 00000000000..a1481749d7e --- /dev/null +++ b/jdk/test/sun/security/pkcs12/StoreTrustedCertTest.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8005408 + * @summary KeyStore API enhancements + */ + +import java.io.*; +import java.security.*; +import java.security.cert.*; +import java.util.*; +import java.security.cert.Certificate; +import javax.crypto.*; +import javax.crypto.spec.*; + +// Store a trusted certificate in a keystore and retrieve it again. + +public class StoreTrustedCertTest { + private final static String DIR = System.getProperty("test.src", "."); + private static final char[] PASSWORD = "passphrase".toCharArray(); + private static final String KEYSTORE = "truststore.p12"; + private static final String CERT = DIR + "/trusted.pem"; + private static final String ALIAS = "my trustedcert"; + private static final String ALIAS2 = "my trustedcert with attributes"; + + public static void main(String[] args) throws Exception { + + new File(KEYSTORE).delete(); + + try { + KeyStore keystore = KeyStore.getInstance("PKCS12"); + keystore.load(null, null); + + Certificate cert = loadCertificate(CERT); + Set attributes = new HashSet<>(); + attributes.add(new PKCS12Attribute("1.3.5.7.9", "that's odd")); + attributes.add(new PKCS12Attribute("2.4.6.8.10", "that's even")); + + // Set trusted certificate entry + keystore.setEntry(ALIAS, + new KeyStore.TrustedCertificateEntry(cert), null); + + // Set trusted certificate entry with attributes + keystore.setEntry(ALIAS2, + new KeyStore.TrustedCertificateEntry(cert, attributes), null); + + System.out.println("Storing keystore to: " + KEYSTORE); + keystore.store(new FileOutputStream(KEYSTORE), PASSWORD); + + System.out.println("Loading keystore from: " + KEYSTORE); + keystore.load(new FileInputStream(KEYSTORE), PASSWORD); + System.out.println("Loaded keystore with " + keystore.size() + + " entries"); + + KeyStore.Entry entry = keystore.getEntry(ALIAS, null); + if (entry instanceof KeyStore.TrustedCertificateEntry) { + System.out.println("Retrieved trusted certificate entry: " + + entry); + } else { + throw new Exception("Not a trusted certificate entry"); + } + System.out.println(); + + entry = keystore.getEntry(ALIAS2, null); + if (entry instanceof KeyStore.TrustedCertificateEntry) { + KeyStore.TrustedCertificateEntry trustedEntry = + (KeyStore.TrustedCertificateEntry) entry; + Set entryAttributes = + trustedEntry.getAttributes(); + + if (entryAttributes.containsAll(attributes)) { + System.out.println("Retrieved trusted certificate entry " + + "with attributes: " + entry); + } else { + throw new Exception("Failed to retrieve entry attributes"); + } + } else { + throw new Exception("Not a trusted certificate entry"); + } + + } finally { + new File(KEYSTORE).delete(); + } + } + + private static Certificate loadCertificate(String certFile) + throws Exception { + X509Certificate cert = null; + try (FileInputStream certStream = new FileInputStream(certFile)) { + CertificateFactory factory = + CertificateFactory.getInstance("X.509"); + return factory.generateCertificate(certStream); + } + } +} diff --git a/jdk/test/sun/security/pkcs12/trusted.pem b/jdk/test/sun/security/pkcs12/trusted.pem new file mode 100644 index 00000000000..32e7b84f357 --- /dev/null +++ b/jdk/test/sun/security/pkcs12/trusted.pem @@ -0,0 +1,29 @@ +-----BEGIN CERTIFICATE----- +MIIF5DCCBMygAwIBAgIQGVCD3zqdD1ZMZZ/zLAPnQzANBgkqhkiG9w0BAQUFADCBvDELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQgaHR0cHM6Ly93d3cudmVyaXNpZ24uY29t +L3JwYSAoYykxMDE2MDQGA1UEAxMtVmVyaVNpZ24gQ2xhc3MgMyBJbnRlcm5hdGlvbmFsIFNlcnZl +ciBDQSAtIEczMB4XDTEyMDcxMDAwMDAwMFoXDTEzMDczMTIzNTk1OVowgbgxCzAJBgNVBAYTAlVT +MRMwEQYDVQQIEwpDYWxpZm9ybmlhMRcwFQYDVQQHFA5SZWR3b29kIFNob3JlczEbMBkGA1UEChQS +T3JhY2xlIENvcnBvcmF0aW9uMRIwEAYDVQQLFAlHbG9iYWwgSVQxMzAxBgNVBAsUKlRlcm1zIG9m +IHVzZSBhdCB3d3cudmVyaXNpZ24uY29tL3JwYSAoYykwNTEVMBMGA1UEAxQMKi5vcmFjbGUuY29t +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz/dOCGrWzPj62q0ZkF59Oj9Fli4wHAuX +U4/S0yBXF8j6K7TKWFTQkGZt3+08KUhmLm1CE1DbbyRJT292YNXYXunNaKdABob8kaBO/NESUOEJ +0SZh7fd0xCSJAAPiwOMrM5jLeb/dEpU6nP74Afrhu5ffvKdcvTRGguj9H2oVsisTK8Z1HsiiwcJG +JXcrjvdCZoPU4FHvK03XZPAqPHKNSaJOrux6kRIWYjQMlmL+qDOb0nNHa6gBdi+VqqJHJHeAM677 +dcUd0jn2m2OWtUnrM3MJZQof7/z27RTdX5J8np0ChkUgm63biDgRZO7uZP0DARQ0I6lZMlrarT8/ +sct3twIDAQABo4IB4jCCAd4wFwYDVR0RBBAwDoIMKi5vcmFjbGUuY29tMAkGA1UdEwQCMAAwCwYD +VR0PBAQDAgWgMEQGA1UdIAQ9MDswOQYLYIZIAYb4RQEHFwMwKjAoBggrBgEFBQcCARYcaHR0cHM6 +Ly93d3cudmVyaXNpZ24uY29tL3JwYTAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwbgYI +KwYBBQUHAQwEYjBgoV6gXDBaMFgwVhYJaW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUS2u5KJYGDLvQ +UjibKaxLB4shBRgwJhYkaHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nbzEuZ2lmMHIGCCsG +AQUFBwEBBGYwZDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AudmVyaXNpZ24uY29tMDwGCCsGAQUF +BzAChjBodHRwOi8vc3ZyaW50bC1nMy1haWEudmVyaXNpZ24uY29tL1NWUkludGxHMy5jZXIwQQYD +VR0fBDowODA2oDSgMoYwaHR0cDovL3N2cmludGwtZzMtY3JsLnZlcmlzaWduLmNvbS9TVlJJbnRs +RzMuY3JsMB8GA1UdIwQYMBaAFNebfNgioBX33a1fzimbWMO8RgC1MA0GCSqGSIb3DQEBBQUAA4IB +AQAITRBlEo+qXLwCL53Db2BGnhDgnSomjne8aCmU7Yt4Kp91tzJdhNuaC/wwDuzD2dPJqzemae3s +wKiOXrmDQZDj9NNTdkrXHnCvDR4TpOynWe3zBa0bwKnV2cIRKcv482yV53u0kALyFZbagYPwOOz3 +YJA/2SqdcDn9Ztc/ABQ1SkyXyA5j4LJdf2g7BtYrFxjy0RG6We2iM781WSB/9MCNKyHgiwd3KpLf +urdSKLzy1elNAyt1P3UHwBIIvZ6sJIr/eeELc54Lxt6PtQCXx8qwxYTYXWPXbLgKBHdebgrmAbPK +TfD69wysvjk6vwSHjmvaqB4R4WRcgkuT+1gxx+ve +-----END CERTIFICATE----- From 930299d58c883aa52beeb2172e35d77d3709bd3c Mon Sep 17 00:00:00 2001 From: Sean Mullan Date: Wed, 23 Jan 2013 20:46:39 -0500 Subject: [PATCH 100/138] 8006813: Compilation error in PKCS12KeyStore.java Reviewed-by: valeriep --- jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java b/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java index a8ce582fa2e..5d0c0533a1b 100644 --- a/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java +++ b/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java @@ -840,7 +840,7 @@ public final class PKCS12KeyStore extends KeyStoreSpi { new PrivilegedAction() { public String run() { String prop = - Security.getProperty + Security.getProperty( KEY_PROTECTION_ALGORITHM[0]); if (prop == null) { prop = Security.getProperty( From a1d5ea2b9dcf2b96de276e2463840fc1d856f20e Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Thu, 24 Jan 2013 09:47:09 +0000 Subject: [PATCH 101/138] 8006524: JSR-3: Allows java.beans to be optional Reviewed-by: dfuchs, mchung --- .../share/classes/javax/management/MXBean.java | 8 ++++++++ .../classes/javax/management/monitor/package.html | 15 ++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/jdk/src/share/classes/javax/management/MXBean.java b/jdk/src/share/classes/javax/management/MXBean.java index c691438737f..4ff10d360af 100644 --- a/jdk/src/share/classes/javax/management/MXBean.java +++ b/jdk/src/share/classes/javax/management/MXBean.java @@ -907,6 +907,14 @@ public interface ModuleMXBean {

  • Otherwise, J is not reconstructible.

  • +

    Rule 2 is not applicable to subset Profiles of Java SE that do not + include the {@code java.beans} package. When targeting a runtime that does + not include the {@code java.beans} package, and where there is a mismatch + between the compile-time and runtime environment whereby J is + compiled with a public constructor and the {@code ConstructorProperties} + annotation, then J is not reconstructible unless another rule + applies.

    +

    Here are examples showing different ways to code a type {@code NamedNumber} that consists of an {@code int} and a {@code String}. In each case, the {@code CompositeType} looks like this:

    diff --git a/jdk/src/share/classes/javax/management/monitor/package.html b/jdk/src/share/classes/javax/management/monitor/package.html index 282dd926369..a5050d77f0a 100644 --- a/jdk/src/share/classes/javax/management/monitor/package.html +++ b/jdk/src/share/classes/javax/management/monitor/package.html @@ -60,19 +60,20 @@ questions. v. A value x is extracted from v as follows:

      +
    • If v is a {@link javax.management.openmbean.CompositeData CompositeData} and if v.{@link javax.management.openmbean.CompositeData#get(String) get}(e) returns a value then x is that value.
    • If v is an array and e is the string "length" then x is the length of the array.
    • -
    • If the above rules do not produce a value, and if {@link - java.beans.Introspector#getBeanInfo(Class) Introspector.getBeanInfo} - for the class of v (v.getClass()) contains a - {@link java.beans.PropertyDescriptor PropertyDescriptor} with the name - e, then x is the result of calling the property's {@link - java.beans.PropertyDescriptor#getReadMethod() read method} on - v.
    • + +
    • If the above rules do not produce a value, and if introspection, as + if by calling {@link java.beans.Introspector#getBeanInfo(Class) + Introspector.getBeanInfo}, for the class of v + (v.getClass()) identifies a property with the name + e, then x is the result of reading the property value.
    • +

    The third rule means for example that if the attribute From 3727e751f6d05ca2cd618ea3d35de855759ae734 Mon Sep 17 00:00:00 2001 From: Vinnie Ryan Date: Thu, 24 Jan 2013 16:44:15 +0000 Subject: [PATCH 102/138] 8006855: PKCS12 test failures due to unsupported algorithm Reviewed-by: mullan --- .../sun/security/pkcs12/PKCS12KeyStore.java | 6 +++--- jdk/test/java/security/KeyStore/PBETest.java | 3 ++- .../security/pkcs12/StoreSecretKeyTest.java | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java b/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java index 5d0c0533a1b..f84fe27f20a 100644 --- a/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java +++ b/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java @@ -887,13 +887,13 @@ public final class PKCS12KeyStore extends KeyStoreSpi { /* * Map a PBE algorithm name onto its object identifier */ - private ObjectIdentifier mapPBEAlgorithmToOID(String algorithm) { + private ObjectIdentifier mapPBEAlgorithmToOID(String algorithm) + throws NoSuchAlgorithmException { // Check for PBES2 algorithms if (algorithm.toLowerCase().startsWith("pbewithhmacsha")) { return pbes2_OID; } - - return null; + return AlgorithmId.get(algorithm).getOID(); } /** diff --git a/jdk/test/java/security/KeyStore/PBETest.java b/jdk/test/java/security/KeyStore/PBETest.java index bc12102f230..be437d03bbd 100644 --- a/jdk/test/java/security/KeyStore/PBETest.java +++ b/jdk/test/java/security/KeyStore/PBETest.java @@ -36,7 +36,8 @@ import javax.crypto.spec.*; public class PBETest { private final static String DIR = System.getProperty("test.src", "."); - private static final String PBE_ALGO = "PBEWithHmacSHA1AndAES_128"; + //private static final String PBE_ALGO = "PBEWithHmacSHA1AndAES_128"; + private static final String PBE_ALGO = "PBEWithSHA1AndDESede"; private static final char[] PASSWORD = "passphrase".toCharArray(); private static final String KEYSTORE_TYPE = "JKS"; private static final String KEYSTORE = DIR + "/keystore.jks"; diff --git a/jdk/test/sun/security/pkcs12/StoreSecretKeyTest.java b/jdk/test/sun/security/pkcs12/StoreSecretKeyTest.java index 9a91148a41b..f002ef7506a 100644 --- a/jdk/test/sun/security/pkcs12/StoreSecretKeyTest.java +++ b/jdk/test/sun/security/pkcs12/StoreSecretKeyTest.java @@ -43,6 +43,14 @@ public class StoreSecretKeyTest { public static void main(String[] args) throws Exception { + // Skip test if AES is unavailable + try { + SecretKeyFactory.getInstance("AES"); + } catch (NoSuchAlgorithmException nsae) { + System.out.println("AES is unavailable. Skipping test..."); + return; + } + new File(KEYSTORE).delete(); try { @@ -79,6 +87,17 @@ public class StoreSecretKeyTest { private static SecretKey generateSecretKey(String algorithm, int size) throws NoSuchAlgorithmException { + + // Failover to DES if the requested secret key factory is unavailable + SecretKeyFactory keyFactory; + try { + keyFactory = SecretKeyFactory.getInstance(algorithm); + } catch (NoSuchAlgorithmException nsae) { + keyFactory = SecretKeyFactory.getInstance("DES"); + algorithm = "DES"; + size = 56; + } + KeyGenerator generator = KeyGenerator.getInstance(algorithm); generator.init(size); return generator.generateKey(); From 183657c9ab51ef9ae3ebee9576dce9971afed0de Mon Sep 17 00:00:00 2001 From: Kumar Srinivasan Date: Thu, 24 Jan 2013 09:34:07 -0800 Subject: [PATCH 103/138] 8006850: [pack200] disable pack200 tests until JSR-308 is implemented Reviewed-by: alanb --- jdk/test/ProblemList.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jdk/test/ProblemList.txt b/jdk/test/ProblemList.txt index 7b763477192..5e181ac7e03 100644 --- a/jdk/test/ProblemList.txt +++ b/jdk/test/ProblemList.txt @@ -321,6 +321,9 @@ sun/jvmstat/monitor/MonitoredVm/CR6672135.java generic-all tools/pack200/CommandLineTests.java generic-all tools/pack200/Pack200Test.java generic-all +# 8001163 +tools/pack200/AttributeTests.java generic-all + # 7150569 tools/launcher/UnicodeTest.java macosx-all From 546eee6d156e725bd2bb38ca26b639547abfae2b Mon Sep 17 00:00:00 2001 From: Vinnie Ryan Date: Thu, 24 Jan 2013 18:21:09 +0000 Subject: [PATCH 104/138] 8006863: javadoc cleanup for 8005408 Reviewed-by: alanb --- jdk/src/share/classes/java/security/PKCS12Attribute.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jdk/src/share/classes/java/security/PKCS12Attribute.java b/jdk/src/share/classes/java/security/PKCS12Attribute.java index b13a4b1f18a..e3898628820 100644 --- a/jdk/src/share/classes/java/security/PKCS12Attribute.java +++ b/jdk/src/share/classes/java/security/PKCS12Attribute.java @@ -56,7 +56,7 @@ public final class PKCS12Attribute implements KeyStore.Entry.Attribute { * pairs of hexadecimal digits. * Multi-valued attributes are represented as a comma-separated * list of values, enclosed in square brackets. See - * {@link Arrays.toString}. + * {@link Arrays#toString(java.lang.Object[])}. *

    * A string value will be DER-encoded as an ASN.1 UTF8String and a * binary value will be DER-encoded as an ASN.1 Octet String. @@ -163,7 +163,7 @@ public final class PKCS12Attribute implements KeyStore.Entry.Attribute { * * Multi-valued attributes are represented as a comma-separated * list of values, enclosed in square brackets. See - * {@link Arrays.toString}. + * {@link Arrays#toString(java.lang.Object[])}. * * @return the attribute value's string encoding */ From 3807bcee8bf9de16bdfed5be541c3852d3d3a250 Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Thu, 24 Jan 2013 16:54:11 -0800 Subject: [PATCH 105/138] 8006895: Clarify that FunctionalInferface is only informative Reviewed-by: briangoetz --- .../classes/java/lang/FunctionalInterface.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/jdk/src/share/classes/java/lang/FunctionalInterface.java b/jdk/src/share/classes/java/lang/FunctionalInterface.java index 0d4ae72a697..a93ed51f91c 100644 --- a/jdk/src/share/classes/java/lang/FunctionalInterface.java +++ b/jdk/src/share/classes/java/lang/FunctionalInterface.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,9 +28,9 @@ package java.lang; import java.lang.annotation.*; /** - * Indicates that an interface type declaration is intended to be a - * functional interface as defined by the Java Language - * Specification. + * An informative annotation type used to indicate that an interface + * type declaration is intended to be a functional interface as + * defined by the Java Language Specification. * * Conceptually, a functional interface has exactly one abstract * method. Since {@linkplain java.lang.reflect.Method#isDefault() @@ -52,6 +52,11 @@ import java.lang.annotation.*; *

  • The annotated type satisfies the requirements of a functional interface. * * + *

    However, the compiler will treat any interface meeting the + * definition of a functional interface as a functional interface + * regardless of whether or not a {@code FunctionalInterface} + * annotation is present on the interface declaration. + * * @jls 4.3.2. The Class Object * @jls 9.8 Functional Interfaces * @jls 9.4.3 Interface Method Body From e349682e3d3114436f2c38d58499bd1a07962957 Mon Sep 17 00:00:00 2001 From: Frank Ding Date: Fri, 25 Jan 2013 17:00:18 +0800 Subject: [PATCH 106/138] 7183373: URLClassloader.close() does not close JAR files whose resources have been loaded via getResource() Reviewed-by: chegar --- .../share/classes/sun/misc/URLClassPath.java | 3 +- .../sun/misc/URLClassPath/JarLoaderTest.java | 78 +++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 jdk/test/sun/misc/URLClassPath/JarLoaderTest.java diff --git a/jdk/src/share/classes/sun/misc/URLClassPath.java b/jdk/src/share/classes/sun/misc/URLClassPath.java index eb6b86aecbd..40567231267 100644 --- a/jdk/src/share/classes/sun/misc/URLClassPath.java +++ b/jdk/src/share/classes/sun/misc/URLClassPath.java @@ -508,7 +508,8 @@ public class URLClassPath { } } else { // our best guess for the other cases - InputStream is = url.openStream(); + uc.setUseCaches(false); + InputStream is = uc.getInputStream(); is.close(); } return url; diff --git a/jdk/test/sun/misc/URLClassPath/JarLoaderTest.java b/jdk/test/sun/misc/URLClassPath/JarLoaderTest.java new file mode 100644 index 00000000000..cd9cbc88bf4 --- /dev/null +++ b/jdk/test/sun/misc/URLClassPath/JarLoaderTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * Portions Copyright (c) 2013 IBM Corporation + */ + +/* @test + * @bug 7183373 + * @summary URLClassLoader fails to close handles to Jar files opened during + * getResource() + */ + +import java.io.*; +import java.net.*; +import java.util.zip.*; + +public class JarLoaderTest { + public static void main(String[] args) throws Exception { + // Create a JAR file + File f = new File("urlcl" + 1 + ".jar"); + ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(f)); + + // add a file + zos.putNextEntry(new ZipEntry("TestResource")); + byte[] b = "This is a test resource".getBytes(); + zos.write(b, 0, b.length); + zos.close(); + + // Load the file using cl.getResource() + URLClassLoader cl = new URLClassLoader(new URL[] { new URL("jar:" + + f.toURI().toURL() + "!/")}, null); + cl.getResource("TestResource"); + + // Close the class loader - this should free up all of its Closeables, + // including the JAR file + cl.close(); + + // Try to delete the JAR file + f.delete(); + + // Check to see if the file was deleted + if (f.exists()) { + System.out.println( + "Test FAILED: Closeables failed to close handle to jar file"); + // Delete the jar using a workaround + for (URL u : cl.getURLs()) { + if (u.getProtocol().equals("jar")) { + ((JarURLConnection)u.openConnection()).getJarFile().close(); + } + f.delete(); + } + throw new RuntimeException("File could not be deleted"); + } else { + System.out.println("Test PASSED"); + } + } +} From 355fea6f2ad9b5de84980428f497f9cba9aac241 Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Fri, 25 Jan 2013 13:09:47 +0000 Subject: [PATCH 107/138] 8006565: java.lang.instrument specification should make it clear that -javaagent is optional Reviewed-by: sla, dcubed, mchung --- jdk/src/share/classes/java/lang/instrument/package.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/jdk/src/share/classes/java/lang/instrument/package.html b/jdk/src/share/classes/java/lang/instrument/package.html index 6e9febdef9b..eb73ee37654 100644 --- a/jdk/src/share/classes/java/lang/instrument/package.html +++ b/jdk/src/share/classes/java/lang/instrument/package.html @@ -53,8 +53,10 @@ dependent.

    Command-Line Interface

    -On implementations with a command-line interface, an agent is started by -adding this option to the command-line: +An implementation is not required to provide a way to start agents from the +command-line interface. On implementations that do provide a way to start agents +from the command-line interface, an agent is started by adding this option to +the command-line:

    -javaagent:jarpath[=options]
    From 241fc73e259f64cb6880b882da0950935f7f10df Mon Sep 17 00:00:00 2001 From: Vinnie Ryan Date: Fri, 25 Jan 2013 16:19:39 +0000 Subject: [PATCH 108/138] 8006946: PKCS12 test failure due to incorrect alias name Reviewed-by: mullan --- .../share/classes/sun/security/pkcs12/PKCS12KeyStore.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java b/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java index f84fe27f20a..a085030e742 100644 --- a/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java +++ b/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java @@ -2224,11 +2224,11 @@ public final class PKCS12KeyStore extends KeyStoreSpi { keyId = "01".getBytes("UTF8"); } } - if (alias == null) { - alias = getUnfriendlyName(); - } // Trusted certificate if (trustedKeyUsage != null) { + if (alias == null) { + alias = getUnfriendlyName(); + } CertEntry certEntry = new CertEntry(cert, keyId, alias, trustedKeyUsage, attributes); From 2add9b3fcf703eb92cf3b3a6ee479f30f403ad72 Mon Sep 17 00:00:00 2001 From: Vinnie Ryan Date: Fri, 25 Jan 2013 17:47:37 +0000 Subject: [PATCH 109/138] 8006951: Avoid storing duplicate PKCS12 attributes Reviewed-by: mullan --- .../sun/security/pkcs12/PKCS12KeyStore.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java b/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java index a085030e742..eefdbff811f 100644 --- a/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java +++ b/jdk/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java @@ -136,6 +136,13 @@ public final class PKCS12KeyStore extends KeyStoreSpi { "keystore.PKCS12.keyProtectionAlgorithm" }; + // friendlyName, localKeyId, trustedKeyUsage + private static final String[] CORE_ATTRIBUTES = { + "1.2.840.113549.1.9.20", + "1.2.840.113549.1.9.21", + "2.16.840.1.113894.746875.1.1" + }; + private static final Debug debug = Debug.getInstance("pkcs12"); private static final int keyBag[] = {1, 2, 840, 113549, 1, 12, 10, 1, 2}; @@ -1537,6 +1544,13 @@ public final class PKCS12KeyStore extends KeyStoreSpi { if (attributes != null) { for (KeyStore.Entry.Attribute attribute : attributes) { + String attributeName = attribute.getName(); + // skip friendlyName, localKeyId and trustedKeyUsage + if (CORE_ATTRIBUTES[0].equals(attributeName) || + CORE_ATTRIBUTES[1].equals(attributeName) || + CORE_ATTRIBUTES[2].equals(attributeName)) { + continue; + } attrs.write(((PKCS12Attribute) attribute).getEncoded()); } } From 3e5a0c70ab984e32590b76c7dfd3f4d2ae16afc2 Mon Sep 17 00:00:00 2001 From: Kurchi Subhra Hazra Date: Fri, 25 Jan 2013 11:52:10 -0800 Subject: [PATCH 110/138] 7017962: Obsolete link is used in URL class level spec Change the link to an archived document Reviewed-by: chegar, mduigou --- jdk/src/share/classes/java/net/URL.java | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/jdk/src/share/classes/java/net/URL.java b/jdk/src/share/classes/java/net/URL.java index a17fa946aa4..62271037126 100644 --- a/jdk/src/share/classes/java/net/URL.java +++ b/jdk/src/share/classes/java/net/URL.java @@ -38,17 +38,21 @@ import sun.security.util.SecurityConstants; * directory, or it can be a reference to a more complicated object, * such as a query to a database or to a search engine. More * information on the types of URLs and their formats can be found at: - *
    - * - * http://www.socs.uts.edu.au/MosaicDocs-old/url-primer.html - *
    + * + * Types of URL *

    - * In general, a URL can be broken into several parts. The previous - * example of a URL indicates that the protocol to use is + * In general, a URL can be broken into several parts. Consider the + * following example: + *

    + *     http://www.example.com/docs/resource1.html
    + * 
    + *

    + * The URL above indicates that the protocol to use is * http (HyperText Transfer Protocol) and that the * information resides on a host machine named - * www.socs.uts.edu.au. The information on that host - * machine is named /MosaicDocs-old/url-primer.html. The exact + * www.example.com. The information on that host + * machine is named /docs/resource1.html. The exact * meaning of this name on the host machine is both protocol * dependent and host dependent. The information normally resides in * a file, but it could be generated on the fly. This component of @@ -61,7 +65,7 @@ import sun.security.util.SecurityConstants; * http is 80. An alternative port could be * specified as: *

    - *     http://www.socs.uts.edu.au:80/MosaicDocs-old/url-primer.html
    + *     http://www.example.com:1080/docs/resource1.html
      * 
    *

    * The syntax of URL is defined by Date: Fri, 25 Jan 2013 16:13:32 -0800 Subject: [PATCH 111/138] 8005632: Extend java.util.Logger to use Supplier for messages Reviewed-by: briangoetz, mduigou --- .../classes/java/util/logging/Logger.java | 272 +++++++++++++++++- .../util/logging/LoggerSupplierAPIsTest.java | 264 +++++++++++++++++ 2 files changed, 531 insertions(+), 5 deletions(-) create mode 100644 jdk/test/java/util/logging/LoggerSupplierAPIsTest.java diff --git a/jdk/src/share/classes/java/util/logging/Logger.java b/jdk/src/share/classes/java/util/logging/Logger.java index e1f2bbaf5f8..4e52c235c0c 100644 --- a/jdk/src/share/classes/java/util/logging/Logger.java +++ b/jdk/src/share/classes/java/util/logging/Logger.java @@ -30,6 +30,7 @@ import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; import java.security.*; import java.lang.ref.WeakReference; +import java.util.function.Supplier; /** * A Logger object is used to log messages for a specific @@ -96,6 +97,33 @@ import java.lang.ref.WeakReference; * for example a format string "{0} {1}" would format two parameters * as strings. *

    + * A set of methods alternatively take a "msgSupplier" instead of a "msg" + * argument. These methods take a {@link Supplier}{@code } function + * which is invoked to construct the desired log message only when the message + * actually is to be logged based on the effective log level thus eliminating + * unnecessary message construction. For example, if the developer wants to + * log system health status for diagnosis, with the String-accepting version, + * the code would look like: +

    +
    +   class DiagnosisMessages {
    +     static String systemHealthStatus() {
    +       // collect system health information
    +       ...
    +     }
    +   }
    +   ...
    +   logger.log(Level.FINER, DiagnosisMessages.systemHealthStatus());
    + 
    + * With the above code, the health status is collected unnecessarily even when + * the log level FINER is disabled. With the Supplier-accepting version as + * below, the status will only be collected when the log level FINER is + * enabled. +
    +
    +   logger.log(Level.FINER, DiagnosisMessages::systemHealthStatus);
    + 
    + *

    * When mapping ResourceBundle names to ResourceBundles, the Logger * will first try to use the Thread's ContextClassLoader. If that * is null it will try the SystemClassLoader instead. As a temporary @@ -566,6 +594,27 @@ public class Logger { doLog(lr); } + /** + * Log a message, which is only to be constructed if the logging level + * is such that the message will actually be logged. + *

    + * If the logger is currently enabled for the given message + * level then the message is constructed by invoking the provided + * supplier function and forwarded to all the registered output + * Handler objects. + *

    + * @param level One of the message level identifiers, e.g., SEVERE + * @param msgSupplier A function, which when called, produces the + * desired log message + */ + public void log(Level level, Supplier msgSupplier) { + if (level.intValue() < levelValue || levelValue == offValue) { + return; + } + LogRecord lr = new LogRecord(level, msgSupplier.get()); + doLog(lr); + } + /** * Log a message, with one object parameter. *

    @@ -615,7 +664,7 @@ public class Logger { * which is forwarded to all registered output handlers. *

    * Note that the thrown argument is stored in the LogRecord thrown - * property, rather than the LogRecord parameters property. Thus is it + * property, rather than the LogRecord parameters property. Thus it is * processed specially by output Formatters and is not treated * as a formatting parameter to the LogRecord message property. *

    @@ -632,6 +681,34 @@ public class Logger { doLog(lr); } + /** + * Log a lazily constructed message, with associated Throwable information. + *

    + * If the logger is currently enabled for the given message level then the + * message is constructed by invoking the provided supplier function. The + * message and the given {@link Throwable} are then stored in a {@link + * LogRecord} which is forwarded to all registered output handlers. + *

    + * Note that the thrown argument is stored in the LogRecord thrown + * property, rather than the LogRecord parameters property. Thus it is + * processed specially by output Formatters and is not treated + * as a formatting parameter to the LogRecord message property. + *

    + * @param level One of the message level identifiers, e.g., SEVERE + * @param thrown Throwable associated with log message. + * @param msgSupplier A function, which when called, produces the + * desired log message + * @since 1.8 + */ + public void log(Level level, Throwable thrown, Supplier msgSupplier) { + if (level.intValue() < levelValue || levelValue == offValue) { + return; + } + LogRecord lr = new LogRecord(level, msgSupplier.get()); + lr.setThrown(thrown); + doLog(lr); + } + //================================================================ // Start of convenience methods WITH className and methodName //================================================================ @@ -659,6 +736,33 @@ public class Logger { doLog(lr); } + /** + * Log a lazily constructed message, specifying source class and method, + * with no arguments. + *

    + * If the logger is currently enabled for the given message + * level then the message is constructed by invoking the provided + * supplier function and forwarded to all the registered output + * Handler objects. + *

    + * @param level One of the message level identifiers, e.g., SEVERE + * @param sourceClass name of class that issued the logging request + * @param sourceMethod name of method that issued the logging request + * @param msgSupplier A function, which when called, produces the + * desired log message + * @since 1.8 + */ + public void logp(Level level, String sourceClass, String sourceMethod, + Supplier msgSupplier) { + if (level.intValue() < levelValue || levelValue == offValue) { + return; + } + LogRecord lr = new LogRecord(level, msgSupplier.get()); + lr.setSourceClassName(sourceClass); + lr.setSourceMethodName(sourceMethod); + doLog(lr); + } + /** * Log a message, specifying source class and method, * with a single object parameter to the log message. @@ -721,7 +825,7 @@ public class Logger { * which is forwarded to all registered output handlers. *

    * Note that the thrown argument is stored in the LogRecord thrown - * property, rather than the LogRecord parameters property. Thus is it + * property, rather than the LogRecord parameters property. Thus it is * processed specially by output Formatters and is not treated * as a formatting parameter to the LogRecord message property. *

    @@ -732,7 +836,7 @@ public class Logger { * @param thrown Throwable associated with log message. */ public void logp(Level level, String sourceClass, String sourceMethod, - String msg, Throwable thrown) { + String msg, Throwable thrown) { if (level.intValue() < levelValue || levelValue == offValue) { return; } @@ -743,6 +847,40 @@ public class Logger { doLog(lr); } + /** + * Log a lazily constructed message, specifying source class and method, + * with associated Throwable information. + *

    + * If the logger is currently enabled for the given message level then the + * message is constructed by invoking the provided supplier function. The + * message and the given {@link Throwable} are then stored in a {@link + * LogRecord} which is forwarded to all registered output handlers. + *

    + * Note that the thrown argument is stored in the LogRecord thrown + * property, rather than the LogRecord parameters property. Thus it is + * processed specially by output Formatters and is not treated + * as a formatting parameter to the LogRecord message property. + *

    + * @param level One of the message level identifiers, e.g., SEVERE + * @param sourceClass name of class that issued the logging request + * @param sourceMethod name of method that issued the logging request + * @param thrown Throwable associated with log message. + * @param msgSupplier A function, which when called, produces the + * desired log message + * @since 1.8 + */ + public void logp(Level level, String sourceClass, String sourceMethod, + Throwable thrown, Supplier msgSupplier) { + if (level.intValue() < levelValue || levelValue == offValue) { + return; + } + LogRecord lr = new LogRecord(level, msgSupplier.get()); + lr.setSourceClassName(sourceClass); + lr.setSourceMethodName(sourceMethod); + lr.setThrown(thrown); + doLog(lr); + } + //========================================================================= // Start of convenience methods WITH className, methodName and bundle name. @@ -869,7 +1007,7 @@ public class Logger { * then the msg string is not localized. *

    * Note that the thrown argument is stored in the LogRecord thrown - * property, rather than the LogRecord parameters property. Thus is it + * property, rather than the LogRecord parameters property. Thus it is * processed specially by output Formatters and is not treated * as a formatting parameter to the LogRecord message property. *

    @@ -1014,7 +1152,7 @@ public class Logger { * LogRecord's message is set to "THROW". *

    * Note that the thrown argument is stored in the LogRecord thrown - * property, rather than the LogRecord parameters property. Thus is it + * property, rather than the LogRecord parameters property. Thus it is * processed specially by output Formatters and is not treated * as a formatting parameter to the LogRecord message property. *

    @@ -1149,6 +1287,130 @@ public class Logger { log(Level.FINEST, msg); } + //======================================================================= + // Start of simple convenience methods using level names as method names + // and use Supplier + //======================================================================= + + /** + * Log a SEVERE message, which is only to be constructed if the logging + * level is such that the message will actually be logged. + *

    + * If the logger is currently enabled for the SEVERE message + * level then the message is constructed by invoking the provided + * supplier function and forwarded to all the registered output + * Handler objects. + *

    + * @param msgSupplier A function, which when called, produces the + * desired log message + * @since 1.8 + */ + public void severe(Supplier msgSupplier) { + log(Level.SEVERE, msgSupplier); + } + + /** + * Log a WARNING message, which is only to be constructed if the logging + * level is such that the message will actually be logged. + *

    + * If the logger is currently enabled for the WARNING message + * level then the message is constructed by invoking the provided + * supplier function and forwarded to all the registered output + * Handler objects. + *

    + * @param msgSupplier A function, which when called, produces the + * desired log message + * @since 1.8 + */ + public void warning(Supplier msgSupplier) { + log(Level.WARNING, msgSupplier); + } + + /** + * Log a INFO message, which is only to be constructed if the logging + * level is such that the message will actually be logged. + *

    + * If the logger is currently enabled for the INFO message + * level then the message is constructed by invoking the provided + * supplier function and forwarded to all the registered output + * Handler objects. + *

    + * @param msgSupplier A function, which when called, produces the + * desired log message + * @since 1.8 + */ + public void info(Supplier msgSupplier) { + log(Level.INFO, msgSupplier); + } + + /** + * Log a CONFIG message, which is only to be constructed if the logging + * level is such that the message will actually be logged. + *

    + * If the logger is currently enabled for the CONFIG message + * level then the message is constructed by invoking the provided + * supplier function and forwarded to all the registered output + * Handler objects. + *

    + * @param msgSupplier A function, which when called, produces the + * desired log message + * @since 1.8 + */ + public void config(Supplier msgSupplier) { + log(Level.CONFIG, msgSupplier); + } + + /** + * Log a FINE message, which is only to be constructed if the logging + * level is such that the message will actually be logged. + *

    + * If the logger is currently enabled for the FINE message + * level then the message is constructed by invoking the provided + * supplier function and forwarded to all the registered output + * Handler objects. + *

    + * @param msgSupplier A function, which when called, produces the + * desired log message + * @since 1.8 + */ + public void fine(Supplier msgSupplier) { + log(Level.FINE, msgSupplier); + } + + /** + * Log a FINER message, which is only to be constructed if the logging + * level is such that the message will actually be logged. + *

    + * If the logger is currently enabled for the FINER message + * level then the message is constructed by invoking the provided + * supplier function and forwarded to all the registered output + * Handler objects. + *

    + * @param msgSupplier A function, which when called, produces the + * desired log message + * @since 1.8 + */ + public void finer(Supplier msgSupplier) { + log(Level.FINER, msgSupplier); + } + + /** + * Log a FINEST message, which is only to be constructed if the logging + * level is such that the message will actually be logged. + *

    + * If the logger is currently enabled for the FINEST message + * level then the message is constructed by invoking the provided + * supplier function and forwarded to all the registered output + * Handler objects. + *

    + * @param msgSupplier A function, which when called, produces the + * desired log message + * @since 1.8 + */ + public void finest(Supplier msgSupplier) { + log(Level.FINEST, msgSupplier); + } + //================================================================ // End of convenience methods //================================================================ diff --git a/jdk/test/java/util/logging/LoggerSupplierAPIsTest.java b/jdk/test/java/util/logging/LoggerSupplierAPIsTest.java new file mode 100644 index 00000000000..4333ba18016 --- /dev/null +++ b/jdk/test/java/util/logging/LoggerSupplierAPIsTest.java @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8005263 + * @run testng LoggerSupplierAPIsTest + */ + +import java.util.logging.Logger; +import java.util.logging.Level; +import java.util.logging.Handler; +import java.util.logging.LogRecord; +import java.util.function.Supplier; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.Collections; + +import org.testng.annotations.Test; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +@Test(groups="unit") +public class LoggerSupplierAPIsTest { + static class CountingSupplier implements Supplier { + AtomicInteger sno = new AtomicInteger(); + + @Override + public String get() { + return "Log message " + sno.getAndIncrement(); + } + + public int getCount() { return sno.get(); } + public void reset() { sno.set(0); } + } + + static class CountingHandler extends Handler { + AtomicInteger count = new AtomicInteger(); + ArrayList ar = new ArrayList<>(); + + @Override + public void close() { reset(); } + + @Override + public void flush() { reset(); } + + @Override + public void publish(LogRecord lr) { + // Can do some more assertion here? + // System.out.println(lr.getMessage()); + count.incrementAndGet(); + } + + public int getCount() { + return count.get(); + } + + public List getLogs() { + return Collections.unmodifiableList(ar); + } + + public void reset() { + count.set(0); + ar.clear(); + } + } + + static class HelperEx extends Exception { + final Level level; + HelperEx(Level l) { level = l; } + Level getLevel() { return level; } + } + + static class SystemInfo { + public static String Java() { + return "Java: " + System.getProperty("java.version") + + " installed at " + System.getProperty("java.home"); + } + + public static String OS() { + return "OS: " + System.getProperty("os.name") + + " " + System.getProperty("os.version") + + " " + System.getProperty("os.arch"); + } + } + + static final CountingSupplier supplier = new CountingSupplier(); + static final CountingHandler handler = new CountingHandler(); + static final Logger logger; + static final Level[] levels = { Level.ALL, Level.OFF, Level.SEVERE, + Level.WARNING, Level.INFO, Level.CONFIG, + Level.FINE, Level.FINER, Level.FINEST }; + static final int[] invokes = { 7, 0, 1, 2, 3, 4, 5, 6, 7 }; + static final int[] log_count = { 10, 0, 1, 2, 3, 4, 6, 8, 10 }; + + static { + logger = Logger.getLogger("LoggerSupplierApisTest"); + logger.setUseParentHandlers(false); + logger.addHandler(handler); + } + + public void setup() { + supplier.reset(); + handler.reset(); + } + + private void testLog() { + logger.log(Level.SEVERE, supplier); + logger.log(Level.WARNING, supplier); + logger.log(Level.INFO, supplier); + logger.log(Level.CONFIG, supplier); + logger.log(Level.FINE, supplier); + logger.log(Level.FINER, supplier); + logger.log(Level.FINEST, supplier); + // Lambda expression + logger.log(Level.FINE, () -> + "Timestamp: " + System.currentTimeMillis() + + ", user home directory: " + System.getProperty("user.home")); + // Method reference + logger.log(Level.FINER, SystemInfo::Java); + logger.log(Level.FINEST, SystemInfo::OS); + } + + private void testLogThrown() { + logger.log(Level.SEVERE, new HelperEx(Level.SEVERE), supplier); + logger.log(Level.WARNING, new HelperEx(Level.WARNING), supplier); + logger.log(Level.INFO, new HelperEx(Level.INFO), supplier); + logger.log(Level.CONFIG, new HelperEx(Level.CONFIG), supplier); + logger.log(Level.FINE, new HelperEx(Level.FINE), supplier); + logger.log(Level.FINER, new HelperEx(Level.FINER), supplier); + logger.log(Level.FINEST, new HelperEx(Level.FINEST), supplier); + // Lambda expression + logger.log(Level.FINE, new HelperEx(Level.FINE), () -> + "Timestamp: " + System.currentTimeMillis() + + ", user home directory: " + System.getProperty("user.home")); + // Method reference + logger.log(Level.FINER, new HelperEx(Level.FINER), SystemInfo::Java); + logger.log(Level.FINEST, new HelperEx(Level.FINEST), SystemInfo::OS); + } + + private void testLogp() { + final String cls = getClass().getName(); + final String mtd = "testLogp"; + + logger.logp(Level.SEVERE, cls, mtd, supplier); + logger.logp(Level.WARNING, cls, mtd, supplier); + logger.logp(Level.INFO, cls, mtd, supplier); + logger.logp(Level.CONFIG, cls, mtd, supplier); + logger.logp(Level.FINE, cls, mtd, supplier); + logger.logp(Level.FINER, cls, mtd, supplier); + logger.logp(Level.FINEST, cls, mtd, supplier); + // Lambda expression + logger.logp(Level.FINE, cls, mtd, () -> + "Timestamp: " + System.currentTimeMillis() + + ", user home directory: " + System.getProperty("user.home")); + // Method reference + logger.logp(Level.FINER, cls, mtd, SystemInfo::Java); + logger.logp(Level.FINEST, cls, mtd, SystemInfo::OS); + } + + private void testLogpThrown() { + final String cls = getClass().getName(); + final String mtd = "testLogpThrown"; + + logger.logp(Level.SEVERE, cls, mtd, new HelperEx(Level.SEVERE), supplier); + logger.logp(Level.WARNING, cls, mtd, new HelperEx(Level.WARNING), supplier); + logger.logp(Level.INFO, cls, mtd, new HelperEx(Level.INFO), supplier); + logger.logp(Level.CONFIG, cls, mtd, new HelperEx(Level.CONFIG), supplier); + logger.logp(Level.FINE, cls, mtd, new HelperEx(Level.FINE), supplier); + logger.logp(Level.FINER, cls, mtd, new HelperEx(Level.FINER), supplier); + logger.logp(Level.FINEST, cls, mtd, new HelperEx(Level.FINEST), supplier); + // Lambda expression + logger.logp(Level.FINE, cls, mtd, new HelperEx(Level.FINE), () -> + "Timestamp: " + System.currentTimeMillis() + + ", user home directory: " + System.getProperty("user.home")); + // Method reference + logger.logp(Level.FINER, cls, mtd, new HelperEx(Level.FINER), SystemInfo::Java); + logger.logp(Level.FINEST, cls, mtd, new HelperEx(Level.FINEST), SystemInfo::OS); + } + + private void testLevelConvenientMethods() { + logger.severe(supplier); + logger.warning(supplier); + logger.info(supplier); + logger.config(supplier); + logger.fine(supplier); + logger.finer(supplier); + logger.finest(supplier); + // Lambda expression + logger.fine(() -> + "Timestamp: " + System.currentTimeMillis() + + ", user home directory: " + System.getProperty("user.home")); + // Method reference + logger.finer(SystemInfo::Java); + logger.finest(SystemInfo::OS); + } + + private void validate(int index, boolean thrown, String methodName) { + assertEquals(supplier.getCount(), invokes[index]); + assertEquals(handler.getCount(), log_count[index]); + // Verify associated Throwable is right + if (thrown) { + for (LogRecord r: handler.getLogs()) { + assertTrue(r.getThrown() instanceof HelperEx, "Validate Thrown"); + HelperEx e = (HelperEx) r.getThrown(); + assertEquals(r.getLevel(), e.getLevel(), "Validate Thrown Log Level"); + } + } + + if (methodName != null) { + for (LogRecord r: handler.getLogs()) { + assertEquals(r.getSourceClassName(), getClass().getName()); + assertEquals(r.getSourceMethodName(), methodName); + } + } + } + + public void verifyLogLevel() { + for (int i = 0; i < levels.length; i++) { + logger.setLevel(levels[i]); + + setup(); + testLog(); + validate(i, false, null); + + setup(); + testLogThrown(); + validate(i, true, null); + + setup(); + testLogp(); + validate(i, false, "testLogp"); + + setup(); + testLogpThrown(); + validate(i, true, "testLogpThrown"); + + setup(); + testLevelConvenientMethods(); + validate(i, false, null); + } + } +} From 5dc4c11b2b2c6c988205be13bffbc9f18188a5bb Mon Sep 17 00:00:00 2001 From: Akhil Arora Date: Fri, 25 Jan 2013 16:13:39 -0800 Subject: [PATCH 112/138] 8004201: Add static utility methods to primitives to be used for redution operations Reviewed-by: darcy, mduigou, briangoetz, dholmes --- jdk/src/share/classes/java/lang/Boolean.java | 42 +++++ jdk/src/share/classes/java/lang/Double.java | 42 +++++ jdk/src/share/classes/java/lang/Float.java | 42 +++++ jdk/src/share/classes/java/lang/Integer.java | 41 +++++ jdk/src/share/classes/java/lang/Long.java | 41 +++++ .../java/lang/PrimitiveSumMinMaxTest.java | 160 ++++++++++++++++++ 6 files changed, 368 insertions(+) create mode 100644 jdk/test/java/lang/PrimitiveSumMinMaxTest.java diff --git a/jdk/src/share/classes/java/lang/Boolean.java b/jdk/src/share/classes/java/lang/Boolean.java index 2c4754c8a61..fbbeba72ce0 100644 --- a/jdk/src/share/classes/java/lang/Boolean.java +++ b/jdk/src/share/classes/java/lang/Boolean.java @@ -290,4 +290,46 @@ public final class Boolean implements java.io.Serializable, public static int compare(boolean x, boolean y) { return (x == y) ? 0 : (x ? 1 : -1); } + + /** + * Returns the result of applying the logical AND operator to the + * specified {@code boolean} operands. + * + * @param a the first operand + * @param b the second operand + * @return the logical AND of {@code a} and {@code b} + * @see java.util.function.BinaryOperator + * @since 1.8 + */ + public static boolean logicalAnd(boolean a, boolean b) { + return a && b; + } + + /** + * Returns the result of applying the logical OR operator to the + * specified {@code boolean} operands. + * + * @param a the first operand + * @param b the second operand + * @return the logical OR of {@code a} and {@code b} + * @see java.util.function.BinaryOperator + * @since 1.8 + */ + public static boolean logicalOr(boolean a, boolean b) { + return a || b; + } + + /** + * Returns the result of applying the logical XOR operator to the + * specified {@code boolean} operands. + * + * @param a the first operand + * @param b the second operand + * @return the logical XOR of {@code a} and {@code b} + * @see java.util.function.BinaryOperator + * @since 1.8 + */ + public static boolean logicalXor(boolean a, boolean b) { + return a ^ b; + } } diff --git a/jdk/src/share/classes/java/lang/Double.java b/jdk/src/share/classes/java/lang/Double.java index a20f79e6cb3..f79980f4bb8 100644 --- a/jdk/src/share/classes/java/lang/Double.java +++ b/jdk/src/share/classes/java/lang/Double.java @@ -1021,6 +1021,48 @@ public final class Double extends Number implements Comparable { 1)); // (0.0, -0.0) or (NaN, !NaN) } + /** + * Adds two {@code double} values together as per the + operator. + * + * @param a the first operand + * @param b the second operand + * @return the sum of {@code a} and {@code b} + * @jls 4.2.4 Floating-Point Operations + * @see java.util.function.BinaryOperator + * @since 1.8 + */ + public static double sum(double a, double b) { + return a + b; + } + + /** + * Returns the greater of two {@code double} values + * as if by calling {@link Math#max(double, double) Math.max}. + * + * @param a the first operand + * @param b the second operand + * @return the greater of {@code a} and {@code b} + * @see java.util.function.BinaryOperator + * @since 1.8 + */ + public static double max(double a, double b) { + return Math.max(a, b); + } + + /** + * Returns the smaller of two {@code double} values + * as if by calling {@link Math#min(double, double) Math.min}. + * + * @param a the first operand + * @param b the second operand + * @return the smaller of {@code a} and {@code b}. + * @see java.util.function.BinaryOperator + * @since 1.8 + */ + public static double min(double a, double b) { + return Math.min(a, b); + } + /** use serialVersionUID from JDK 1.0.2 for interoperability */ private static final long serialVersionUID = -9172774392245257468L; } diff --git a/jdk/src/share/classes/java/lang/Float.java b/jdk/src/share/classes/java/lang/Float.java index 0c071e2b0f4..bdb8f63fbf1 100644 --- a/jdk/src/share/classes/java/lang/Float.java +++ b/jdk/src/share/classes/java/lang/Float.java @@ -926,6 +926,48 @@ public final class Float extends Number implements Comparable { 1)); // (0.0, -0.0) or (NaN, !NaN) } + /** + * Adds two {@code float} values together as per the + operator. + * + * @param a the first operand + * @param b the second operand + * @return the sum of {@code a} and {@code b} + * @jls 4.2.4 Floating-Point Operations + * @see java.util.function.BinaryOperator + * @since 1.8 + */ + public static float sum(float a, float b) { + return a + b; + } + + /** + * Returns the greater of two {@code float} values + * as if by calling {@link Math#max(float, float) Math.max}. + * + * @param a the first operand + * @param b the second operand + * @return the greater of {@code a} and {@code b} + * @see java.util.function.BinaryOperator + * @since 1.8 + */ + public static float max(float a, float b) { + return Math.max(a, b); + } + + /** + * Returns the smaller of two {@code float} values + * as if by calling {@link Math#min(float, float) Math.min}. + * + * @param a the first operand + * @param b the second operand + * @return the smaller of {@code a} and {@code b} + * @see java.util.function.BinaryOperator + * @since 1.8 + */ + public static float min(float a, float b) { + return Math.min(a, b); + } + /** use serialVersionUID from JDK 1.0.2 for interoperability */ private static final long serialVersionUID = -2671257302660747028L; } diff --git a/jdk/src/share/classes/java/lang/Integer.java b/jdk/src/share/classes/java/lang/Integer.java index b27f46358c5..a0eeecf2d98 100644 --- a/jdk/src/share/classes/java/lang/Integer.java +++ b/jdk/src/share/classes/java/lang/Integer.java @@ -1513,6 +1513,47 @@ public final class Integer extends Number implements Comparable { ((i << 24)); } + /** + * Adds two integers together as per the + operator. + * + * @param a the first operand + * @param b the second operand + * @return the sum of {@code a} and {@code b} + * @see java.util.function.BinaryOperator + * @since 1.8 + */ + public static int sum(int a, int b) { + return a + b; + } + + /** + * Returns the greater of two {@code int} values + * as if by calling {@link Math#max(int, int) Math.max}. + * + * @param a the first operand + * @param b the second operand + * @return the greater of {@code a} and {@code b} + * @see java.util.function.BinaryOperator + * @since 1.8 + */ + public static int max(int a, int b) { + return Math.max(a, b); + } + + /** + * Returns the smaller of two {@code int} values + * as if by calling {@link Math#min(int, int) Math.min}. + * + * @param a the first operand + * @param b the second operand + * @return the smaller of {@code a} and {@code b} + * @see java.util.function.BinaryOperator + * @since 1.8 + */ + public static int min(int a, int b) { + return Math.min(a, b); + } + /** use serialVersionUID from JDK 1.0.2 for interoperability */ @Native private static final long serialVersionUID = 1360826667806852920L; } diff --git a/jdk/src/share/classes/java/lang/Long.java b/jdk/src/share/classes/java/lang/Long.java index 35cb2a4b64d..b67c8520547 100644 --- a/jdk/src/share/classes/java/lang/Long.java +++ b/jdk/src/share/classes/java/lang/Long.java @@ -1540,6 +1540,47 @@ public final class Long extends Number implements Comparable { ((i >>> 16) & 0xffff0000L) | (i >>> 48); } + /** + * Adds two {@code long} values together as per the + operator. + * + * @param a the first operand + * @param b the second operand + * @return the sum of {@code a} and {@code b} + * @see java.util.function.BinaryOperator + * @since 1.8 + */ + public static long sum(long a, long b) { + return a + b; + } + + /** + * Returns the greater of two {@code long} values + * as if by calling {@link Math#max(long, long) Math.max}. + * + * @param a the first operand + * @param b the second operand + * @return the greater of {@code a} and {@code b} + * @see java.util.function.BinaryOperator + * @since 1.8 + */ + public static long max(long a, long b) { + return Math.max(a, b); + } + + /** + * Returns the smaller of two {@code long} values + * as if by calling {@link Math#min(long, long) Math.min}. + * + * @param a the first operand + * @param b the second operand + * @return the smaller of {@code a} and {@code b} + * @see java.util.function.BinaryOperator + * @since 1.8 + */ + public static long min(long a, long b) { + return Math.min(a, b); + } + /** use serialVersionUID from JDK 1.0.2 for interoperability */ @Native private static final long serialVersionUID = 4290774380558885855L; } diff --git a/jdk/test/java/lang/PrimitiveSumMinMaxTest.java b/jdk/test/java/lang/PrimitiveSumMinMaxTest.java new file mode 100644 index 00000000000..1c44109c83f --- /dev/null +++ b/jdk/test/java/lang/PrimitiveSumMinMaxTest.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.Test; + +import java.util.Comparator; +import java.util.function.BinaryOperator; +import java.util.function.IntBinaryOperator; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import java.util.function.DoubleBinaryOperator; +import java.util.function.LongBinaryOperator; + +/** + * @test + * @run testng PrimitiveSumMinMaxTest + * @summary test conversion of primitive wrapper sum, min, max, and compareTo methods to functional interfaces + * + * @author Brian Goetz + */ +@Test +public class PrimitiveSumMinMaxTest { + + public void testBooleanMethods() { + BinaryOperator and = Boolean::logicalAnd; + BinaryOperator or = Boolean::logicalOr; + BinaryOperator xor = Boolean::logicalXor; + Comparator cmp = Boolean::compare; + + assertTrue(and.operate(true, true)); + assertFalse(and.operate(true, false)); + assertFalse(and.operate(false, true)); + assertFalse(and.operate(false, false)); + + assertTrue(or.operate(true, true)); + assertTrue(or.operate(true, false)); + assertTrue(or.operate(false, true)); + assertFalse(or.operate(false, false)); + + assertFalse(xor.operate(true, true)); + assertTrue(xor.operate(true, false)); + assertTrue(xor.operate(false, true)); + assertFalse(xor.operate(false, false)); + + assertEquals(Boolean.TRUE.compareTo(Boolean.TRUE), cmp.compare(true, true)); + assertEquals(Boolean.TRUE.compareTo(Boolean.FALSE), cmp.compare(true, false)); + assertEquals(Boolean.FALSE.compareTo(Boolean.TRUE), cmp.compare(false, true)); + assertEquals(Boolean.FALSE.compareTo(Boolean.FALSE), cmp.compare(false, false)); + }; + + public void testIntMethods() { + BinaryOperator sum1 = Integer::sum; + IntBinaryOperator sum2 = Integer::sum; + BinaryOperator max1 = Integer::max; + IntBinaryOperator max2 = Integer::max; + BinaryOperator min1 = Integer::min; + IntBinaryOperator min2 = Integer::min; + Comparator cmp = Integer::compare; + + int[] numbers = { -1, 0, 1, 100, Integer.MAX_VALUE, Integer.MIN_VALUE }; + for (int i : numbers) { + for (int j : numbers) { + assertEquals(i+j, (int) sum1.operate(i, j)); + assertEquals(i+j, sum2.operateAsInt(i, j)); + assertEquals(Math.max(i,j), (int) max1.operate(i, j)); + assertEquals(Math.max(i,j), max2.operateAsInt(i, j)); + assertEquals(Math.min(i,j), (int) min1.operate(i, j)); + assertEquals(Math.min(i,j), min2.operateAsInt(i, j)); + assertEquals(((Integer) i).compareTo(j), cmp.compare(i, j)); + } + } + } + + public void testLongMethods() { + BinaryOperator sum1 = Long::sum; + LongBinaryOperator sum2 = Long::sum; + BinaryOperator max1 = Long::max; + LongBinaryOperator max2 = Long::max; + BinaryOperator min1 = Long::min; + LongBinaryOperator min2 = Long::min; + Comparator cmp = Long::compare; + + long[] numbers = { -1, 0, 1, 100, Long.MAX_VALUE, Long.MIN_VALUE }; + for (long i : numbers) { + for (long j : numbers) { + assertEquals(i+j, (long) sum1.operate(i, j)); + assertEquals(i+j, sum2.operateAsLong(i, j)); + assertEquals(Math.max(i,j), (long) max1.operate(i, j)); + assertEquals(Math.max(i,j), max2.operateAsLong(i, j)); + assertEquals(Math.min(i,j), (long) min1.operate(i, j)); + assertEquals(Math.min(i,j), min2.operateAsLong(i, j)); + assertEquals(((Long) i).compareTo(j), cmp.compare(i, j)); + } + } + } + + public void testFloatMethods() { + BinaryOperator sum1 = Float::sum; + BinaryOperator max1 = Float::max; + BinaryOperator min1 = Float::min; + Comparator cmp = Float::compare; + + float[] numbers = { -1, 0, 1, 100, Float.MAX_VALUE, Float.MIN_VALUE }; + for (float i : numbers) { + for (float j : numbers) { + assertEquals(i+j, (float) sum1.operate(i, j)); + assertEquals(Math.max(i,j), (float) max1.operate(i, j)); + assertEquals(Math.min(i,j), (float) min1.operate(i, j)); + assertEquals(((Float) i).compareTo(j), cmp.compare(i, j)); + } + } + } + + public void testDoubleMethods() { + BinaryOperator sum1 = Double::sum; + DoubleBinaryOperator sum2 = Double::sum; + BinaryOperator max1 = Double::max; + DoubleBinaryOperator max2 = Double::max; + BinaryOperator min1 = Double::min; + DoubleBinaryOperator min2 = Double::min; + Comparator cmp = Double::compare; + + double[] numbers = { -1, 0, 1, 100, Double.MAX_VALUE, Double.MIN_VALUE }; + for (double i : numbers) { + for (double j : numbers) { + assertEquals(i+j, (double) sum1.operate(i, j)); + assertEquals(i+j, sum2.operateAsDouble(i, j)); + assertEquals(Math.max(i,j), (double) max1.operate(i, j)); + assertEquals(Math.max(i,j), max2.operateAsDouble(i, j)); + assertEquals(Math.min(i,j), (double) min1.operate(i, j)); + assertEquals(Math.min(i,j), min2.operateAsDouble(i, j)); + assertEquals(((Double) i).compareTo(j), cmp.compare(i, j)); + } + } + } + +} From 43b8609138476f6367da1dd7c0ea19273d5898e6 Mon Sep 17 00:00:00 2001 From: Jim Gish Date: Tue, 22 Jan 2013 11:14:13 -0500 Subject: [PATCH 113/138] 4247235: (spec str) StringBuffer.insert(int, char[]) specification is inconsistent Add blanket null-handling statement to StringBuilder and StringBuffer Reviewed-by: mduigou --- .../java/lang/AbstractStringBuilder.java | 14 ++++---------- jdk/src/share/classes/java/lang/String.java | 16 +++++----------- .../share/classes/java/lang/StringBuffer.java | 13 +++++-------- .../share/classes/java/lang/StringBuilder.java | 18 ++++-------------- 4 files changed, 18 insertions(+), 43 deletions(-) diff --git a/jdk/src/share/classes/java/lang/AbstractStringBuilder.java b/jdk/src/share/classes/java/lang/AbstractStringBuilder.java index 03999c445a5..6e4881cb832 100644 --- a/jdk/src/share/classes/java/lang/AbstractStringBuilder.java +++ b/jdk/src/share/classes/java/lang/AbstractStringBuilder.java @@ -35,6 +35,10 @@ import java.util.Arrays; * particular sequence of characters, but the length and content of the * sequence can be changed through certain method calls. * + *

    Unless otherwise noted, passing a {@code null} argument to a constructor + * or method in this class will cause a {@link NullPointerException} to be + * thrown. + * * @author Michael McCloskey * @author Martin Buchholz * @author Ulf Zibis @@ -334,8 +338,6 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * @param srcEnd stop copying at this offset. * @param dst the array to copy the data into. * @param dstBegin offset into {@code dst}. - * @throws NullPointerException if {@code dst} is - * {@code null}. * @throws IndexOutOfBoundsException if any of the following is true: *

      *
    • {@code srcBegin} is negative @@ -1282,8 +1284,6 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * object, then the index of the first character of the first * such substring is returned; if it does not occur as a * substring, {@code -1} is returned. - * @throws java.lang.NullPointerException if {@code str} is - * {@code null}. */ public int indexOf(String str) { return indexOf(str, 0); @@ -1303,8 +1303,6 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * @param fromIndex the index from which to start the search. * @return the index within this string of the first occurrence of the * specified substring, starting at the specified index. - * @throws java.lang.NullPointerException if {@code str} is - * {@code null}. */ public int indexOf(String str, int fromIndex) { return String.indexOf(value, 0, count, str, fromIndex); @@ -1325,8 +1323,6 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * within this object, then the index of the first character of * the last such substring is returned. If it does not occur as * a substring, {@code -1} is returned. - * @throws java.lang.NullPointerException if {@code str} is - * {@code null}. */ public int lastIndexOf(String str) { return lastIndexOf(str, count); @@ -1346,8 +1342,6 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence { * @param fromIndex the index to start the search from. * @return the index within this sequence of the last occurrence of the * specified substring. - * @throws java.lang.NullPointerException if {@code str} is - * {@code null}. */ public int lastIndexOf(String str, int fromIndex) { return String.lastIndexOf(value, 0, count, str, fromIndex); diff --git a/jdk/src/share/classes/java/lang/String.java b/jdk/src/share/classes/java/lang/String.java index a9bc2f37648..7679a45a73a 100644 --- a/jdk/src/share/classes/java/lang/String.java +++ b/jdk/src/share/classes/java/lang/String.java @@ -33,6 +33,7 @@ import java.util.Arrays; import java.util.Comparator; import java.util.Formatter; import java.util.Locale; +import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; @@ -871,6 +872,8 @@ public final class String if (srcBegin > srcEnd) { throw new StringIndexOutOfBoundsException(srcEnd - srcBegin); } + Objects.requireNonNull(dst); + int j = dstBegin; int n = srcEnd; int i = srcBegin; @@ -2112,7 +2115,6 @@ public final class String * * @param s the sequence to search for * @return true if this string contains {@code s}, false otherwise - * @throws NullPointerException if {@code s} is {@code null} * @since 1.5 */ public boolean contains(CharSequence s) { @@ -2219,8 +2221,6 @@ public final class String * @param target The sequence of char values to be replaced * @param replacement The replacement sequence of char values * @return The resulting string - * @throws NullPointerException if {@code target} or - * {@code replacement} is {@code null}. * @since 1.5 */ public String replace(CharSequence target, CharSequence replacement) { @@ -2833,9 +2833,6 @@ public final class String * href="../util/Formatter.html#detail">Details section of the * formatter class specification. * - * @throws NullPointerException - * If the {@code format} is {@code null} - * * @return A formatted string * * @see java.util.Formatter @@ -2865,8 +2862,8 @@ public final class String * limited by the maximum dimension of a Java array as defined by * The Java™ Virtual Machine Specification. * The behaviour on a - * {@code null} argument depends on the conversion. + * {@code null} argument depends on the + * conversion. * * @throws java.util.IllegalFormatException * If a format string contains an illegal syntax, a format @@ -2877,9 +2874,6 @@ public final class String * href="../util/Formatter.html#detail">Details section of the * formatter class specification * - * @throws NullPointerException - * If the {@code format} is {@code null} - * * @return A formatted string * * @see java.util.Formatter diff --git a/jdk/src/share/classes/java/lang/StringBuffer.java b/jdk/src/share/classes/java/lang/StringBuffer.java index 5d7754a0cc7..f31a53c98e6 100644 --- a/jdk/src/share/classes/java/lang/StringBuffer.java +++ b/jdk/src/share/classes/java/lang/StringBuffer.java @@ -77,7 +77,11 @@ package java.lang; * the capacity, it is not necessary to allocate a new internal * buffer array. If the internal buffer overflows, it is * automatically made larger. - * + *

      + * Unless otherwise noted, passing a {@code null} argument to a constructor + * or method in this class will cause a {@link NullPointerException} to be + * thrown. + *

      * As of release JDK 5, this class has been supplemented with an equivalent * class designed for use by a single thread, {@link StringBuilder}. The * {@code StringBuilder} class should generally be used in preference to @@ -123,7 +127,6 @@ package java.lang; * {@code 16} plus the length of the string argument. * * @param str the initial contents of the buffer. - * @exception NullPointerException if {@code str} is {@code null} */ public StringBuffer(String str) { super(str.length() + 16); @@ -141,7 +144,6 @@ package java.lang; * {@code 16} is returned. * * @param seq the sequence to copy. - * @exception NullPointerException if {@code seq} is {@code null} * @since 1.5 */ public StringBuffer(CharSequence seq) { @@ -228,7 +230,6 @@ package java.lang; } /** - * @throws NullPointerException {@inheritDoc} * @throws IndexOutOfBoundsException {@inheritDoc} */ @Override @@ -584,7 +585,6 @@ package java.lang; } /** - * @throws NullPointerException {@inheritDoc} * @since 1.4 */ @Override @@ -594,7 +594,6 @@ package java.lang; } /** - * @throws NullPointerException {@inheritDoc} * @since 1.4 */ @Override @@ -603,7 +602,6 @@ package java.lang; } /** - * @throws NullPointerException {@inheritDoc} * @since 1.4 */ @Override @@ -613,7 +611,6 @@ package java.lang; } /** - * @throws NullPointerException {@inheritDoc} * @since 1.4 */ @Override diff --git a/jdk/src/share/classes/java/lang/StringBuilder.java b/jdk/src/share/classes/java/lang/StringBuilder.java index e9defe23f6f..bfa2d83d232 100644 --- a/jdk/src/share/classes/java/lang/StringBuilder.java +++ b/jdk/src/share/classes/java/lang/StringBuilder.java @@ -64,6 +64,10 @@ package java.lang; * use by multiple threads. If such synchronization is required then it is * recommended that {@link java.lang.StringBuffer} be used. * + *

      Unless otherwise noted, passing a {@code null} argument to a constructor + * or method in this class will cause a {@link NullPointerException} to be + * thrown. + * * @author Michael McCloskey * @see java.lang.StringBuffer * @see java.lang.String @@ -103,7 +107,6 @@ public final class StringBuilder * {@code 16} plus the length of the string argument. * * @param str the initial contents of the buffer. - * @throws NullPointerException if {@code str} is {@code null} */ public StringBuilder(String str) { super(str.length() + 16); @@ -117,7 +120,6 @@ public final class StringBuilder * {@code CharSequence} argument. * * @param seq the sequence to copy. - * @throws NullPointerException if {@code seq} is {@code null} */ public StringBuilder(CharSequence seq) { this(seq.length() + 16); @@ -373,33 +375,21 @@ public final class StringBuilder return this; } - /** - * @throws NullPointerException {@inheritDoc} - */ @Override public int indexOf(String str) { return super.indexOf(str); } - /** - * @throws NullPointerException {@inheritDoc} - */ @Override public int indexOf(String str, int fromIndex) { return super.indexOf(str, fromIndex); } - /** - * @throws NullPointerException {@inheritDoc} - */ @Override public int lastIndexOf(String str) { return super.lastIndexOf(str); } - /** - * @throws NullPointerException {@inheritDoc} - */ @Override public int lastIndexOf(String str, int fromIndex) { return super.lastIndexOf(str, fromIndex); From 080750fe6526c03b940a229b946f3964f0385380 Mon Sep 17 00:00:00 2001 From: Zhengyu Gu Date: Tue, 22 Jan 2013 14:27:41 -0500 Subject: [PATCH 114/138] 6871190: Don't terminate JVM if it is running in a non-interactive session Don't handle CTRL_LOGOFF_EVENT event when the process is running in a non-interactive session Reviewed-by: ctornqvi, acorn --- hotspot/src/os/windows/vm/os_windows.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/hotspot/src/os/windows/vm/os_windows.cpp b/hotspot/src/os/windows/vm/os_windows.cpp index b8498d5a1e2..c854b5237df 100644 --- a/hotspot/src/os/windows/vm/os_windows.cpp +++ b/hotspot/src/os/windows/vm/os_windows.cpp @@ -1874,8 +1874,22 @@ static BOOL WINAPI consoleHandler(DWORD event) { } return TRUE; break; + case CTRL_LOGOFF_EVENT: { + // Don't terminate JVM if it is running in a non-interactive session, + // such as a service process. + USEROBJECTFLAGS flags; + HANDLE handle = GetProcessWindowStation(); + if (handle != NULL && + GetUserObjectInformation(handle, UOI_FLAGS, &flags, + sizeof( USEROBJECTFLAGS), NULL)) { + // If it is a non-interactive session, let next handler to deal + // with it. + if ((flags.dwFlags & WSF_VISIBLE) == 0) { + return FALSE; + } + } + } case CTRL_CLOSE_EVENT: - case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: os::signal_raise(SIGTERM); return TRUE; From 44cdae9a57aa4678b234f269baaddcdb364070fe Mon Sep 17 00:00:00 2001 From: Goetz Lindenmaier Date: Tue, 22 Jan 2013 11:31:25 -0800 Subject: [PATCH 115/138] 8005055: pass outputStream to more opto debug routines Pass the output stream to node->dump() and everything reachable from there Reviewed-by: kvn --- hotspot/src/share/vm/compiler/oopMap.cpp | 8 +- hotspot/src/share/vm/opto/callnode.cpp | 60 +++++------ hotspot/src/share/vm/opto/callnode.hpp | 6 +- hotspot/src/share/vm/opto/machnode.cpp | 2 +- hotspot/src/share/vm/opto/node.cpp | 123 +++++++++++------------ hotspot/src/share/vm/opto/node.hpp | 9 +- hotspot/src/share/vm/opto/optoreg.hpp | 2 +- hotspot/src/share/vm/opto/regalloc.cpp | 1 + hotspot/src/share/vm/opto/regmask.cpp | 58 +++++------ hotspot/src/share/vm/opto/regmask.hpp | 2 +- 10 files changed, 137 insertions(+), 134 deletions(-) diff --git a/hotspot/src/share/vm/compiler/oopMap.cpp b/hotspot/src/share/vm/compiler/oopMap.cpp index 7c51c669b5c..8e6834c710d 100644 --- a/hotspot/src/share/vm/compiler/oopMap.cpp +++ b/hotspot/src/share/vm/compiler/oopMap.cpp @@ -542,17 +542,17 @@ void print_register_type(OopMapValue::oop_types x, VMReg optional, st->print("Oop"); break; case OopMapValue::value_value: - st->print("Value" ); + st->print("Value"); break; case OopMapValue::narrowoop_value: - tty->print("NarrowOop" ); + st->print("NarrowOop"); break; case OopMapValue::callee_saved_value: - st->print("Callers_" ); + st->print("Callers_"); optional->print_on(st); break; case OopMapValue::derived_oop_value: - st->print("Derived_oop_" ); + st->print("Derived_oop_"); optional->print_on(st); break; default: diff --git a/hotspot/src/share/vm/opto/callnode.cpp b/hotspot/src/share/vm/opto/callnode.cpp index e8c23b38e33..c90b76d4adb 100644 --- a/hotspot/src/share/vm/opto/callnode.cpp +++ b/hotspot/src/share/vm/opto/callnode.cpp @@ -165,13 +165,13 @@ uint ReturnNode::match_edge(uint idx) const { #ifndef PRODUCT -void ReturnNode::dump_req() const { +void ReturnNode::dump_req(outputStream *st) const { // Dump the required inputs, enclosed in '(' and ')' uint i; // Exit value of loop - for( i=0; iprint("returns"); - if( in(i) ) tty->print("%c%d ", Compile::current()->node_arena()->contains(in(i)) ? ' ' : 'o', in(i)->_idx); - else tty->print("_ "); + for (i = 0; i < req(); i++) { // For all required inputs + if (i == TypeFunc::Parms) st->print("returns"); + if (in(i)) st->print("%c%d ", Compile::current()->node_arena()->contains(in(i)) ? ' ' : 'o', in(i)->_idx); + else st->print("_ "); } } #endif @@ -208,13 +208,13 @@ uint RethrowNode::match_edge(uint idx) const { } #ifndef PRODUCT -void RethrowNode::dump_req() const { +void RethrowNode::dump_req(outputStream *st) const { // Dump the required inputs, enclosed in '(' and ')' uint i; // Exit value of loop - for( i=0; iprint("exception"); - if( in(i) ) tty->print("%c%d ", Compile::current()->node_arena()->contains(in(i)) ? ' ' : 'o', in(i)->_idx); - else tty->print("_ "); + for (i = 0; i < req(); i++) { // For all required inputs + if (i == TypeFunc::Parms) st->print("exception"); + if (in(i)) st->print("%c%d ", Compile::current()->node_arena()->contains(in(i)) ? ' ' : 'o', in(i)->_idx); + else st->print("_ "); } } #endif @@ -330,7 +330,8 @@ static void format_helper( PhaseRegAlloc *regalloc, outputStream* st, Node *n, c st->print(" %s%d]=#ScObj" INT32_FORMAT, msg, i, sco_n); return; } - if( OptoReg::is_valid(regalloc->get_reg_first(n))) { // Check for undefined + if (regalloc->node_regs_max_index() > 0 && + OptoReg::is_valid(regalloc->get_reg_first(n))) { // Check for undefined char buf[50]; regalloc->dump_register(n,buf); st->print(" %s%d]=%s",msg,i,buf); @@ -381,7 +382,7 @@ static void format_helper( PhaseRegAlloc *regalloc, outputStream* st, Node *n, c //------------------------------format----------------------------------------- void JVMState::format(PhaseRegAlloc *regalloc, const Node *n, outputStream* st) const { st->print(" #"); - if( _method ) { + if (_method) { _method->print_short_name(st); st->print(" @ bci:%d ",_bci); } else { @@ -393,21 +394,22 @@ void JVMState::format(PhaseRegAlloc *regalloc, const Node *n, outputStream* st) MachSafePointNode *mcall = n->as_MachSafePoint(); uint i; // Print locals - for( i = 0; i < (uint)loc_size(); i++ ) - format_helper( regalloc, st, mcall->local(this, i), "L[", i, &scobjs ); + for (i = 0; i < (uint)loc_size(); i++) + format_helper(regalloc, st, mcall->local(this, i), "L[", i, &scobjs); // Print stack for (i = 0; i < (uint)stk_size(); i++) { if ((uint)(_stkoff + i) >= mcall->len()) st->print(" oob "); else - format_helper( regalloc, st, mcall->stack(this, i), "STK[", i, &scobjs ); + format_helper(regalloc, st, mcall->stack(this, i), "STK[", i, &scobjs); } for (i = 0; (int)i < nof_monitors(); i++) { Node *box = mcall->monitor_box(this, i); Node *obj = mcall->monitor_obj(this, i); - if ( OptoReg::is_valid(regalloc->get_reg_first(box)) ) { + if (regalloc->node_regs_max_index() > 0 && + OptoReg::is_valid(regalloc->get_reg_first(box))) { box = BoxLockNode::box_node(box); - format_helper( regalloc, st, box, "MON-BOX[", i, &scobjs ); + format_helper(regalloc, st, box, "MON-BOX[", i, &scobjs); } else { OptoReg::Name box_reg = BoxLockNode::reg(box); st->print(" MON-BOX%d=%s+%d", @@ -420,7 +422,7 @@ void JVMState::format(PhaseRegAlloc *regalloc, const Node *n, outputStream* st) if (BoxLockNode::box_node(box)->is_eliminated()) obj_msg = "MON-OBJ(LOCK ELIMINATED)["; } - format_helper( regalloc, st, obj, obj_msg, i, &scobjs ); + format_helper(regalloc, st, obj, obj_msg, i, &scobjs); } for (i = 0; i < (uint)scobjs.length(); i++) { @@ -463,9 +465,9 @@ void JVMState::format(PhaseRegAlloc *regalloc, const Node *n, outputStream* st) st->print(" ["); cifield = iklass->nonstatic_field_at(0); cifield->print_name_on(st); - format_helper( regalloc, st, fld_node, ":", 0, &scobjs ); + format_helper(regalloc, st, fld_node, ":", 0, &scobjs); } else { - format_helper( regalloc, st, fld_node, "[", 0, &scobjs ); + format_helper(regalloc, st, fld_node, "[", 0, &scobjs); } for (uint j = 1; j < nf; j++) { fld_node = mcall->in(first_ind+j); @@ -473,9 +475,9 @@ void JVMState::format(PhaseRegAlloc *regalloc, const Node *n, outputStream* st) st->print(", ["); cifield = iklass->nonstatic_field_at(j); cifield->print_name_on(st); - format_helper( regalloc, st, fld_node, ":", j, &scobjs ); + format_helper(regalloc, st, fld_node, ":", j, &scobjs); } else { - format_helper( regalloc, st, fld_node, ", [", j, &scobjs ); + format_helper(regalloc, st, fld_node, ", [", j, &scobjs); } } } @@ -483,7 +485,7 @@ void JVMState::format(PhaseRegAlloc *regalloc, const Node *n, outputStream* st) } } st->print_cr(""); - if (caller() != NULL) caller()->format(regalloc, n, st); + if (caller() != NULL) caller()->format(regalloc, n, st); } @@ -586,15 +588,15 @@ JVMState* JVMState::clone_deep(Compile* C) const { uint CallNode::cmp( const Node &n ) const { return _tf == ((CallNode&)n)._tf && _jvms == ((CallNode&)n)._jvms; } #ifndef PRODUCT -void CallNode::dump_req() const { +void CallNode::dump_req(outputStream *st) const { // Dump the required inputs, enclosed in '(' and ')' uint i; // Exit value of loop - for( i=0; iprint("("); - if( in(i) ) tty->print("%c%d ", Compile::current()->node_arena()->contains(in(i)) ? ' ' : 'o', in(i)->_idx); - else tty->print("_ "); + for (i = 0; i < req(); i++) { // For all required inputs + if (i == TypeFunc::Parms) st->print("("); + if (in(i)) st->print("%c%d ", Compile::current()->node_arena()->contains(in(i)) ? ' ' : 'o', in(i)->_idx); + else st->print("_ "); } - tty->print(")"); + st->print(")"); } void CallNode::dump_spec(outputStream *st) const { diff --git a/hotspot/src/share/vm/opto/callnode.hpp b/hotspot/src/share/vm/opto/callnode.hpp index fee091f798d..0aa35c214e5 100644 --- a/hotspot/src/share/vm/opto/callnode.hpp +++ b/hotspot/src/share/vm/opto/callnode.hpp @@ -126,7 +126,7 @@ public: virtual uint ideal_reg() const { return NotAMachineReg; } virtual uint match_edge(uint idx) const; #ifndef PRODUCT - virtual void dump_req() const; + virtual void dump_req(outputStream *st = tty) const; #endif }; @@ -147,7 +147,7 @@ class RethrowNode : public Node { virtual uint match_edge(uint idx) const; virtual uint ideal_reg() const { return NotAMachineReg; } #ifndef PRODUCT - virtual void dump_req() const; + virtual void dump_req(outputStream *st = tty) const; #endif }; @@ -579,7 +579,7 @@ public: virtual uint match_edge(uint idx) const; #ifndef PRODUCT - virtual void dump_req() const; + virtual void dump_req(outputStream *st = tty) const; virtual void dump_spec(outputStream *st) const; #endif }; diff --git a/hotspot/src/share/vm/opto/machnode.cpp b/hotspot/src/share/vm/opto/machnode.cpp index 30970102d5f..b9b014e050b 100644 --- a/hotspot/src/share/vm/opto/machnode.cpp +++ b/hotspot/src/share/vm/opto/machnode.cpp @@ -506,7 +506,7 @@ int MachConstantNode::constant_offset() { #ifndef PRODUCT void MachNullCheckNode::format( PhaseRegAlloc *ra_, outputStream *st ) const { int reg = ra_->get_reg_first(in(1)->in(_vidx)); - tty->print("%s %s", Name(), Matcher::regName[reg]); + st->print("%s %s", Name(), Matcher::regName[reg]); } #endif diff --git a/hotspot/src/share/vm/opto/node.cpp b/hotspot/src/share/vm/opto/node.cpp index 9983c84d8e2..16157f68bd7 100644 --- a/hotspot/src/share/vm/opto/node.cpp +++ b/hotspot/src/share/vm/opto/node.cpp @@ -1476,35 +1476,35 @@ static bool is_disconnected(const Node* n) { } #ifdef ASSERT -static void dump_orig(Node* orig) { +static void dump_orig(Node* orig, outputStream *st) { Compile* C = Compile::current(); - if (NotANode(orig)) orig = NULL; - if (orig != NULL && !C->node_arena()->contains(orig)) orig = NULL; - if (orig == NULL) return; - tty->print(" !orig="); + if (NotANode(orig)) orig = NULL; + if (orig != NULL && !C->node_arena()->contains(orig)) orig = NULL; + if (orig == NULL) return; + st->print(" !orig="); Node* fast = orig->debug_orig(); // tortoise & hare algorithm to detect loops - if (NotANode(fast)) fast = NULL; + if (NotANode(fast)) fast = NULL; while (orig != NULL) { bool discon = is_disconnected(orig); // if discon, print [123] else 123 - if (discon) tty->print("["); + if (discon) st->print("["); if (!Compile::current()->node_arena()->contains(orig)) - tty->print("o"); - tty->print("%d", orig->_idx); - if (discon) tty->print("]"); + st->print("o"); + st->print("%d", orig->_idx); + if (discon) st->print("]"); orig = orig->debug_orig(); - if (NotANode(orig)) orig = NULL; - if (orig != NULL && !C->node_arena()->contains(orig)) orig = NULL; - if (orig != NULL) tty->print(","); + if (NotANode(orig)) orig = NULL; + if (orig != NULL && !C->node_arena()->contains(orig)) orig = NULL; + if (orig != NULL) st->print(","); if (fast != NULL) { // Step fast twice for each single step of orig: fast = fast->debug_orig(); - if (NotANode(fast)) fast = NULL; + if (NotANode(fast)) fast = NULL; if (fast != NULL && fast != orig) { fast = fast->debug_orig(); - if (NotANode(fast)) fast = NULL; + if (NotANode(fast)) fast = NULL; } if (fast == orig) { - tty->print("..."); + st->print("..."); break; } } @@ -1531,35 +1531,34 @@ void Node::set_debug_orig(Node* orig) { //------------------------------dump------------------------------------------ // Dump a Node -void Node::dump() const { +void Node::dump(const char* suffix, outputStream *st) const { Compile* C = Compile::current(); bool is_new = C->node_arena()->contains(this); _in_dump_cnt++; - tty->print("%c%d\t%s\t=== ", - is_new ? ' ' : 'o', _idx, Name()); + st->print("%c%d\t%s\t=== ", is_new ? ' ' : 'o', _idx, Name()); // Dump the required and precedence inputs - dump_req(); - dump_prec(); + dump_req(st); + dump_prec(st); // Dump the outputs - dump_out(); + dump_out(st); if (is_disconnected(this)) { #ifdef ASSERT - tty->print(" [%d]",debug_idx()); - dump_orig(debug_orig()); + st->print(" [%d]",debug_idx()); + dump_orig(debug_orig(), st); #endif - tty->cr(); + st->cr(); _in_dump_cnt--; return; // don't process dead nodes } // Dump node-specific info - dump_spec(tty); + dump_spec(st); #ifdef ASSERT // Dump the non-reset _debug_idx - if( Verbose && WizardMode ) { - tty->print(" [%d]",debug_idx()); + if (Verbose && WizardMode) { + st->print(" [%d]",debug_idx()); } #endif @@ -1569,88 +1568,88 @@ void Node::dump() const { const TypeInstPtr *toop = t->isa_instptr(); const TypeKlassPtr *tkls = t->isa_klassptr(); ciKlass* klass = toop ? toop->klass() : (tkls ? tkls->klass() : NULL ); - if( klass && klass->is_loaded() && klass->is_interface() ) { - tty->print(" Interface:"); - } else if( toop ) { - tty->print(" Oop:"); - } else if( tkls ) { - tty->print(" Klass:"); + if (klass && klass->is_loaded() && klass->is_interface()) { + st->print(" Interface:"); + } else if (toop) { + st->print(" Oop:"); + } else if (tkls) { + st->print(" Klass:"); } - t->dump(); - } else if( t == Type::MEMORY ) { - tty->print(" Memory:"); - MemNode::dump_adr_type(this, adr_type(), tty); - } else if( Verbose || WizardMode ) { - tty->print(" Type:"); - if( t ) { - t->dump(); + t->dump_on(st); + } else if (t == Type::MEMORY) { + st->print(" Memory:"); + MemNode::dump_adr_type(this, adr_type(), st); + } else if (Verbose || WizardMode) { + st->print(" Type:"); + if (t) { + t->dump_on(st); } else { - tty->print("no type"); + st->print("no type"); } } else if (t->isa_vect() && this->is_MachSpillCopy()) { // Dump MachSpillcopy vector type. - t->dump(); + t->dump_on(st); } if (is_new) { - debug_only(dump_orig(debug_orig())); + debug_only(dump_orig(debug_orig(), st)); Node_Notes* nn = C->node_notes_at(_idx); if (nn != NULL && !nn->is_clear()) { if (nn->jvms() != NULL) { - tty->print(" !jvms:"); - nn->jvms()->dump_spec(tty); + st->print(" !jvms:"); + nn->jvms()->dump_spec(st); } } } - tty->cr(); + if (suffix) st->print(suffix); _in_dump_cnt--; } //------------------------------dump_req-------------------------------------- -void Node::dump_req() const { +void Node::dump_req(outputStream *st) const { // Dump the required input edges for (uint i = 0; i < req(); i++) { // For all required inputs Node* d = in(i); if (d == NULL) { - tty->print("_ "); + st->print("_ "); } else if (NotANode(d)) { - tty->print("NotANode "); // uninitialized, sentinel, garbage, etc. + st->print("NotANode "); // uninitialized, sentinel, garbage, etc. } else { - tty->print("%c%d ", Compile::current()->node_arena()->contains(d) ? ' ' : 'o', d->_idx); + st->print("%c%d ", Compile::current()->node_arena()->contains(d) ? ' ' : 'o', d->_idx); } } } //------------------------------dump_prec------------------------------------- -void Node::dump_prec() const { +void Node::dump_prec(outputStream *st) const { // Dump the precedence edges int any_prec = 0; for (uint i = req(); i < len(); i++) { // For all precedence inputs Node* p = in(i); if (p != NULL) { - if( !any_prec++ ) tty->print(" |"); - if (NotANode(p)) { tty->print("NotANode "); continue; } - tty->print("%c%d ", Compile::current()->node_arena()->contains(in(i)) ? ' ' : 'o', in(i)->_idx); + if (!any_prec++) st->print(" |"); + if (NotANode(p)) { st->print("NotANode "); continue; } + st->print("%c%d ", Compile::current()->node_arena()->contains(in(i)) ? ' ' : 'o', in(i)->_idx); } } } //------------------------------dump_out-------------------------------------- -void Node::dump_out() const { +void Node::dump_out(outputStream *st) const { // Delimit the output edges - tty->print(" [["); + st->print(" [["); // Dump the output edges for (uint i = 0; i < _outcnt; i++) { // For all outputs Node* u = _out[i]; if (u == NULL) { - tty->print("_ "); + st->print("_ "); } else if (NotANode(u)) { - tty->print("NotANode "); + st->print("NotANode "); } else { - tty->print("%c%d ", Compile::current()->node_arena()->contains(u) ? ' ' : 'o', u->_idx); + st->print("%c%d ", Compile::current()->node_arena()->contains(u) ? ' ' : 'o', u->_idx); } } - tty->print("]] "); + st->print("]] "); } //------------------------------dump_nodes------------------------------------- diff --git a/hotspot/src/share/vm/opto/node.hpp b/hotspot/src/share/vm/opto/node.hpp index f0c1df3a8b0..0dafb00182f 100644 --- a/hotspot/src/share/vm/opto/node.hpp +++ b/hotspot/src/share/vm/opto/node.hpp @@ -994,12 +994,13 @@ public: #ifndef PRODUCT Node* find(int idx) const; // Search the graph for the given idx. Node* find_ctrl(int idx) const; // Search control ancestors for the given idx. - void dump() const; // Print this node, + void dump() const { dump("\n"); } // Print this node. + void dump(const char* suffix, outputStream *st = tty) const;// Print this node. void dump(int depth) const; // Print this node, recursively to depth d void dump_ctrl(int depth) const; // Print control nodes, to depth d - virtual void dump_req() const; // Print required-edge info - virtual void dump_prec() const; // Print precedence-edge info - virtual void dump_out() const; // Print the output edge info + virtual void dump_req(outputStream *st = tty) const; // Print required-edge info + virtual void dump_prec(outputStream *st = tty) const; // Print precedence-edge info + virtual void dump_out(outputStream *st = tty) const; // Print the output edge info virtual void dump_spec(outputStream *st) const {}; // Print per-node info void verify_edges(Unique_Node_List &visited); // Verify bi-directional edges void verify() const; // Check Def-Use info for my subgraph diff --git a/hotspot/src/share/vm/opto/optoreg.hpp b/hotspot/src/share/vm/opto/optoreg.hpp index e6427ea459a..a21311b9c6d 100644 --- a/hotspot/src/share/vm/opto/optoreg.hpp +++ b/hotspot/src/share/vm/opto/optoreg.hpp @@ -77,7 +77,7 @@ class OptoReg VALUE_OBJ_CLASS_SPEC { // (We would like to have an operator+ for RegName, but it is not // a class, so this would be illegal in C++.) - static void dump( int ); + static void dump(int, outputStream *st = tty); // Get the stack slot number of an OptoReg::Name static unsigned int reg2stack( OptoReg::Name r) { diff --git a/hotspot/src/share/vm/opto/regalloc.cpp b/hotspot/src/share/vm/opto/regalloc.cpp index 79dde4066c1..7ac02165662 100644 --- a/hotspot/src/share/vm/opto/regalloc.cpp +++ b/hotspot/src/share/vm/opto/regalloc.cpp @@ -40,6 +40,7 @@ PhaseRegAlloc::PhaseRegAlloc( uint unique, PhaseCFG &cfg, Phase(Register_Allocation), _cfg(cfg), _matcher(matcher), _node_oops(Thread::current()->resource_area()), _node_regs(0), + _node_regs_max_index(0), _framesize(0xdeadbeef) { int i; diff --git a/hotspot/src/share/vm/opto/regmask.cpp b/hotspot/src/share/vm/opto/regmask.cpp index 59413388ca7..f286a873471 100644 --- a/hotspot/src/share/vm/opto/regmask.cpp +++ b/hotspot/src/share/vm/opto/regmask.cpp @@ -108,13 +108,13 @@ int find_hihghest_bit( uint32 mask ) { //------------------------------dump------------------------------------------- #ifndef PRODUCT -void OptoReg::dump( int r ) { - switch( r ) { - case Special: tty->print("r---"); break; - case Bad: tty->print("rBAD"); break; +void OptoReg::dump(int r, outputStream *st) { + switch (r) { + case Special: st->print("r---"); break; + case Bad: st->print("rBAD"); break; default: - if( r < _last_Mach_Reg ) tty->print(Matcher::regName[r]); - else tty->print("rS%d",r); + if (r < _last_Mach_Reg) st->print(Matcher::regName[r]); + else st->print("rS%d",r); break; } } @@ -404,53 +404,53 @@ uint RegMask::Size() const { #ifndef PRODUCT //------------------------------print------------------------------------------ -void RegMask::dump( ) const { - tty->print("["); +void RegMask::dump(outputStream *st) const { + st->print("["); RegMask rm = *this; // Structure copy into local temp OptoReg::Name start = rm.find_first_elem(); // Get a register - if( OptoReg::is_valid(start) ) { // Check for empty mask + if (OptoReg::is_valid(start)) { // Check for empty mask rm.Remove(start); // Yank from mask - OptoReg::dump(start); // Print register + OptoReg::dump(start, st); // Print register OptoReg::Name last = start; // Now I have printed an initial register. // Print adjacent registers as "rX-rZ" instead of "rX,rY,rZ". // Begin looping over the remaining registers. - while( 1 ) { // + while (1) { // OptoReg::Name reg = rm.find_first_elem(); // Get a register - if( !OptoReg::is_valid(reg) ) + if (!OptoReg::is_valid(reg)) break; // Empty mask, end loop rm.Remove(reg); // Yank from mask - if( last+1 == reg ) { // See if they are adjacent + if (last+1 == reg) { // See if they are adjacent // Adjacent registers just collect into long runs, no printing. last = reg; } else { // Ending some kind of run - if( start == last ) { // 1-register run; no special printing - } else if( start+1 == last ) { - tty->print(","); // 2-register run; print as "rX,rY" - OptoReg::dump(last); + if (start == last) { // 1-register run; no special printing + } else if (start+1 == last) { + st->print(","); // 2-register run; print as "rX,rY" + OptoReg::dump(last, st); } else { // Multi-register run; print as "rX-rZ" - tty->print("-"); - OptoReg::dump(last); + st->print("-"); + OptoReg::dump(last, st); } - tty->print(","); // Seperate start of new run + st->print(","); // Seperate start of new run start = last = reg; // Start a new register run - OptoReg::dump(start); // Print register + OptoReg::dump(start, st); // Print register } // End of if ending a register run or not } // End of while regmask not empty - if( start == last ) { // 1-register run; no special printing - } else if( start+1 == last ) { - tty->print(","); // 2-register run; print as "rX,rY" - OptoReg::dump(last); + if (start == last) { // 1-register run; no special printing + } else if (start+1 == last) { + st->print(","); // 2-register run; print as "rX,rY" + OptoReg::dump(last, st); } else { // Multi-register run; print as "rX-rZ" - tty->print("-"); - OptoReg::dump(last); + st->print("-"); + OptoReg::dump(last, st); } - if( rm.is_AllStack() ) tty->print("..."); + if (rm.is_AllStack()) st->print("..."); } - tty->print("]"); + st->print("]"); } #endif diff --git a/hotspot/src/share/vm/opto/regmask.hpp b/hotspot/src/share/vm/opto/regmask.hpp index e4c31dcefb2..7e3376b38a3 100644 --- a/hotspot/src/share/vm/opto/regmask.hpp +++ b/hotspot/src/share/vm/opto/regmask.hpp @@ -310,7 +310,7 @@ public: #ifndef PRODUCT void print() const { dump(); } - void dump() const; // Print a mask + void dump(outputStream *st = tty) const; // Print a mask #endif static const RegMask Empty; // Common empty mask From 9e0c61f822a69a413754fdae79e2b343eb6611e8 Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Tue, 22 Jan 2013 15:34:16 -0800 Subject: [PATCH 116/138] 6896617: Optimize sun.nio.cs.ISO_8859_1$Encode.encodeArrayLoop() on x86 Use SSE4.2 and AVX2 instructions for encodeArray intrinsic. Reviewed-by: roland --- hotspot/src/cpu/x86/vm/assembler_x86.cpp | 14 +- hotspot/src/cpu/x86/vm/assembler_x86.hpp | 4 + hotspot/src/cpu/x86/vm/macroAssembler_x86.cpp | 122 +++++++ hotspot/src/cpu/x86/vm/macroAssembler_x86.hpp | 4 + hotspot/src/cpu/x86/vm/x86_32.ad | 17 + hotspot/src/cpu/x86/vm/x86_64.ad | 17 + hotspot/src/share/vm/adlc/formssel.cpp | 4 +- hotspot/src/share/vm/classfile/vmSymbols.hpp | 5 + hotspot/src/share/vm/opto/c2_globals.hpp | 3 + hotspot/src/share/vm/opto/classes.hpp | 1 + hotspot/src/share/vm/opto/escape.cpp | 41 ++- hotspot/src/share/vm/opto/lcm.cpp | 1 + hotspot/src/share/vm/opto/library_call.cpp | 49 +++ hotspot/src/share/vm/opto/loopTransform.cpp | 2 + hotspot/src/share/vm/opto/macro.cpp | 15 +- hotspot/src/share/vm/opto/matcher.cpp | 9 + hotspot/src/share/vm/opto/memnode.cpp | 20 ++ hotspot/src/share/vm/opto/memnode.hpp | 16 + .../test/compiler/6896617/Test6896617.java | 331 ++++++++++++++++++ 19 files changed, 663 insertions(+), 12 deletions(-) create mode 100644 hotspot/test/compiler/6896617/Test6896617.java diff --git a/hotspot/src/cpu/x86/vm/assembler_x86.cpp b/hotspot/src/cpu/x86/vm/assembler_x86.cpp index 8b474ba2409..795461d6abf 100644 --- a/hotspot/src/cpu/x86/vm/assembler_x86.cpp +++ b/hotspot/src/cpu/x86/vm/assembler_x86.cpp @@ -2263,6 +2263,18 @@ void Assembler::packuswb(XMMRegister dst, XMMRegister src) { emit_simd_arith(0x67, dst, src, VEX_SIMD_66); } +void Assembler::vpackuswb(XMMRegister dst, XMMRegister nds, XMMRegister src, bool vector256) { + assert(VM_Version::supports_avx() && !vector256 || VM_Version::supports_avx2(), "256 bit integer vectors requires AVX2"); + emit_vex_arith(0x67, dst, nds, src, VEX_SIMD_66, vector256); +} + +void Assembler::vpermq(XMMRegister dst, XMMRegister src, int imm8, bool vector256) { + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_3A, true, vector256); + emit_int8(0x00); + emit_int8(0xC0 | encode); + emit_int8(imm8); +} + void Assembler::pcmpestri(XMMRegister dst, Address src, int imm8) { assert(VM_Version::supports_sse4_2(), ""); InstructionMark im(this); @@ -2475,7 +2487,7 @@ void Assembler::vptest(XMMRegister dst, Address src) { assert(dst != xnoreg, "sanity"); int dst_enc = dst->encoding(); // swap src<->dst for encoding - vex_prefix(src, dst_enc, dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_38, false, vector256); + vex_prefix(src, 0, dst_enc, VEX_SIMD_66, VEX_OPCODE_0F_38, false, vector256); emit_int8(0x17); emit_operand(dst, src); } diff --git a/hotspot/src/cpu/x86/vm/assembler_x86.hpp b/hotspot/src/cpu/x86/vm/assembler_x86.hpp index bcef330e4cf..8dad416b09f 100644 --- a/hotspot/src/cpu/x86/vm/assembler_x86.hpp +++ b/hotspot/src/cpu/x86/vm/assembler_x86.hpp @@ -1395,6 +1395,10 @@ private: // Pack with unsigned saturation void packuswb(XMMRegister dst, XMMRegister src); void packuswb(XMMRegister dst, Address src); + void vpackuswb(XMMRegister dst, XMMRegister nds, XMMRegister src, bool vector256); + + // Pemutation of 64bit words + void vpermq(XMMRegister dst, XMMRegister src, int imm8, bool vector256); // SSE4.2 string instructions void pcmpestri(XMMRegister xmm1, XMMRegister xmm2, int imm8); diff --git a/hotspot/src/cpu/x86/vm/macroAssembler_x86.cpp b/hotspot/src/cpu/x86/vm/macroAssembler_x86.cpp index e9e414077f5..e4b221b289b 100644 --- a/hotspot/src/cpu/x86/vm/macroAssembler_x86.cpp +++ b/hotspot/src/cpu/x86/vm/macroAssembler_x86.cpp @@ -6209,6 +6209,128 @@ void MacroAssembler::generate_fill(BasicType t, bool aligned, } BIND(L_exit); } + +// encode char[] to byte[] in ISO_8859_1 +void MacroAssembler::encode_iso_array(Register src, Register dst, Register len, + XMMRegister tmp1Reg, XMMRegister tmp2Reg, + XMMRegister tmp3Reg, XMMRegister tmp4Reg, + Register tmp5, Register result) { + // rsi: src + // rdi: dst + // rdx: len + // rcx: tmp5 + // rax: result + ShortBranchVerifier sbv(this); + assert_different_registers(src, dst, len, tmp5, result); + Label L_done, L_copy_1_char, L_copy_1_char_exit; + + // set result + xorl(result, result); + // check for zero length + testl(len, len); + jcc(Assembler::zero, L_done); + movl(result, len); + + // Setup pointers + lea(src, Address(src, len, Address::times_2)); // char[] + lea(dst, Address(dst, len, Address::times_1)); // byte[] + negptr(len); + + if (UseSSE42Intrinsics || UseAVX >= 2) { + Label L_chars_8_check, L_copy_8_chars, L_copy_8_chars_exit; + Label L_chars_16_check, L_copy_16_chars, L_copy_16_chars_exit; + + if (UseAVX >= 2) { + Label L_chars_32_check, L_copy_32_chars, L_copy_32_chars_exit; + movl(tmp5, 0xff00ff00); // create mask to test for Unicode chars in vector + movdl(tmp1Reg, tmp5); + vpbroadcastd(tmp1Reg, tmp1Reg); + jmpb(L_chars_32_check); + + bind(L_copy_32_chars); + vmovdqu(tmp3Reg, Address(src, len, Address::times_2, -64)); + vmovdqu(tmp4Reg, Address(src, len, Address::times_2, -32)); + vpor(tmp2Reg, tmp3Reg, tmp4Reg, /* vector256 */ true); + vptest(tmp2Reg, tmp1Reg); // check for Unicode chars in vector + jccb(Assembler::notZero, L_copy_32_chars_exit); + vpackuswb(tmp3Reg, tmp3Reg, tmp4Reg, /* vector256 */ true); + vpermq(tmp4Reg, tmp3Reg, 0xD8, /* vector256 */ true); + vmovdqu(Address(dst, len, Address::times_1, -32), tmp4Reg); + + bind(L_chars_32_check); + addptr(len, 32); + jccb(Assembler::lessEqual, L_copy_32_chars); + + bind(L_copy_32_chars_exit); + subptr(len, 16); + jccb(Assembler::greater, L_copy_16_chars_exit); + + } else if (UseSSE42Intrinsics) { + movl(tmp5, 0xff00ff00); // create mask to test for Unicode chars in vector + movdl(tmp1Reg, tmp5); + pshufd(tmp1Reg, tmp1Reg, 0); + jmpb(L_chars_16_check); + } + + bind(L_copy_16_chars); + if (UseAVX >= 2) { + vmovdqu(tmp2Reg, Address(src, len, Address::times_2, -32)); + vptest(tmp2Reg, tmp1Reg); + jccb(Assembler::notZero, L_copy_16_chars_exit); + vpackuswb(tmp2Reg, tmp2Reg, tmp1Reg, /* vector256 */ true); + vpermq(tmp3Reg, tmp2Reg, 0xD8, /* vector256 */ true); + } else { + if (UseAVX > 0) { + movdqu(tmp3Reg, Address(src, len, Address::times_2, -32)); + movdqu(tmp4Reg, Address(src, len, Address::times_2, -16)); + vpor(tmp2Reg, tmp3Reg, tmp4Reg, /* vector256 */ false); + } else { + movdqu(tmp3Reg, Address(src, len, Address::times_2, -32)); + por(tmp2Reg, tmp3Reg); + movdqu(tmp4Reg, Address(src, len, Address::times_2, -16)); + por(tmp2Reg, tmp4Reg); + } + ptest(tmp2Reg, tmp1Reg); // check for Unicode chars in vector + jccb(Assembler::notZero, L_copy_16_chars_exit); + packuswb(tmp3Reg, tmp4Reg); + } + movdqu(Address(dst, len, Address::times_1, -16), tmp3Reg); + + bind(L_chars_16_check); + addptr(len, 16); + jccb(Assembler::lessEqual, L_copy_16_chars); + + bind(L_copy_16_chars_exit); + subptr(len, 8); + jccb(Assembler::greater, L_copy_8_chars_exit); + + bind(L_copy_8_chars); + movdqu(tmp3Reg, Address(src, len, Address::times_2, -16)); + ptest(tmp3Reg, tmp1Reg); + jccb(Assembler::notZero, L_copy_8_chars_exit); + packuswb(tmp3Reg, tmp1Reg); + movq(Address(dst, len, Address::times_1, -8), tmp3Reg); + addptr(len, 8); + jccb(Assembler::lessEqual, L_copy_8_chars); + + bind(L_copy_8_chars_exit); + subptr(len, 8); + jccb(Assembler::zero, L_done); + } + + bind(L_copy_1_char); + load_unsigned_short(tmp5, Address(src, len, Address::times_2, 0)); + testl(tmp5, 0xff00); // check if Unicode char + jccb(Assembler::notZero, L_copy_1_char_exit); + movb(Address(dst, len, Address::times_1, 0), tmp5); + addptr(len, 1); + jccb(Assembler::less, L_copy_1_char); + + bind(L_copy_1_char_exit); + addptr(result, len); // len is negative count of not processed elements + bind(L_done); +} + #undef BIND #undef BLOCK_COMMENT diff --git a/hotspot/src/cpu/x86/vm/macroAssembler_x86.hpp b/hotspot/src/cpu/x86/vm/macroAssembler_x86.hpp index 3afcf23ebae..e4b89322b9d 100644 --- a/hotspot/src/cpu/x86/vm/macroAssembler_x86.hpp +++ b/hotspot/src/cpu/x86/vm/macroAssembler_x86.hpp @@ -1135,6 +1135,10 @@ public: Register to, Register value, Register count, Register rtmp, XMMRegister xtmp); + void encode_iso_array(Register src, Register dst, Register len, + XMMRegister tmp1, XMMRegister tmp2, XMMRegister tmp3, + XMMRegister tmp4, Register tmp5, Register result); + #undef VIRTUAL }; diff --git a/hotspot/src/cpu/x86/vm/x86_32.ad b/hotspot/src/cpu/x86/vm/x86_32.ad index 462060de119..6ceb50cee84 100644 --- a/hotspot/src/cpu/x86/vm/x86_32.ad +++ b/hotspot/src/cpu/x86/vm/x86_32.ad @@ -11687,6 +11687,23 @@ instruct array_equals(eDIRegP ary1, eSIRegP ary2, eAXRegI result, ins_pipe( pipe_slow ); %} +// encode char[] to byte[] in ISO_8859_1 +instruct encode_iso_array(eSIRegP src, eDIRegP dst, eDXRegI len, + regD tmp1, regD tmp2, regD tmp3, regD tmp4, + eCXRegI tmp5, eAXRegI result, eFlagsReg cr) %{ + match(Set result (EncodeISOArray src (Binary dst len))); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL tmp5, KILL cr); + + format %{ "Encode array $src,$dst,$len -> $result // KILL ECX, EDX, $tmp1, $tmp2, $tmp3, $tmp4, ESI, EDI " %} + ins_encode %{ + __ encode_iso_array($src$$Register, $dst$$Register, $len$$Register, + $tmp1$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister, + $tmp4$$XMMRegister, $tmp5$$Register, $result$$Register); + %} + ins_pipe( pipe_slow ); +%} + + //----------Control Flow Instructions------------------------------------------ // Signed compare Instructions instruct compI_eReg(eFlagsReg cr, rRegI op1, rRegI op2) %{ diff --git a/hotspot/src/cpu/x86/vm/x86_64.ad b/hotspot/src/cpu/x86/vm/x86_64.ad index b526a6ce27b..7c902c4e31c 100644 --- a/hotspot/src/cpu/x86/vm/x86_64.ad +++ b/hotspot/src/cpu/x86/vm/x86_64.ad @@ -10495,6 +10495,23 @@ instruct array_equals(rdi_RegP ary1, rsi_RegP ary2, rax_RegI result, ins_pipe( pipe_slow ); %} +// encode char[] to byte[] in ISO_8859_1 +instruct encode_iso_array(rsi_RegP src, rdi_RegP dst, rdx_RegI len, + regD tmp1, regD tmp2, regD tmp3, regD tmp4, + rcx_RegI tmp5, rax_RegI result, rFlagsReg cr) %{ + match(Set result (EncodeISOArray src (Binary dst len))); + effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL tmp5, KILL cr); + + format %{ "Encode array $src,$dst,$len -> $result // KILL RCX, RDX, $tmp1, $tmp2, $tmp3, $tmp4, RSI, RDI " %} + ins_encode %{ + __ encode_iso_array($src$$Register, $dst$$Register, $len$$Register, + $tmp1$$XMMRegister, $tmp2$$XMMRegister, $tmp3$$XMMRegister, + $tmp4$$XMMRegister, $tmp5$$Register, $result$$Register); + %} + ins_pipe( pipe_slow ); +%} + + //----------Control Flow Instructions------------------------------------------ // Signed compare Instructions diff --git a/hotspot/src/share/vm/adlc/formssel.cpp b/hotspot/src/share/vm/adlc/formssel.cpp index f7f48b4a2dc..b9170ec379e 100644 --- a/hotspot/src/share/vm/adlc/formssel.cpp +++ b/hotspot/src/share/vm/adlc/formssel.cpp @@ -862,8 +862,10 @@ uint InstructForm::oper_input_base(FormDict &globals) { ( strcmp(_matrule->_rChild->_opType,"AryEq" )==0 || strcmp(_matrule->_rChild->_opType,"StrComp" )==0 || strcmp(_matrule->_rChild->_opType,"StrEquals" )==0 || - strcmp(_matrule->_rChild->_opType,"StrIndexOf")==0 )) { + strcmp(_matrule->_rChild->_opType,"StrIndexOf")==0 || + strcmp(_matrule->_rChild->_opType,"EncodeISOArray")==0)) { // String.(compareTo/equals/indexOf) and Arrays.equals + // and sun.nio.cs.iso8859_1$Encoder.EncodeISOArray // take 1 control and 1 memory edges. return 2; } diff --git a/hotspot/src/share/vm/classfile/vmSymbols.hpp b/hotspot/src/share/vm/classfile/vmSymbols.hpp index 02abb3afb7b..ad0d7d9fc44 100644 --- a/hotspot/src/share/vm/classfile/vmSymbols.hpp +++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp @@ -735,6 +735,11 @@ do_intrinsic(_checkIndex, java_nio_Buffer, checkIndex_name, int_int_signature, F_R) \ do_name( checkIndex_name, "checkIndex") \ \ + do_class(sun_nio_cs_iso8859_1_Encoder, "sun/nio/cs/ISO_8859_1$Encoder") \ + do_intrinsic(_encodeISOArray, sun_nio_cs_iso8859_1_Encoder, encodeISOArray_name, encodeISOArray_signature, F_S) \ + do_name( encodeISOArray_name, "encodeISOArray") \ + do_signature(encodeISOArray_signature, "([CI[BII)I") \ + \ /* java/lang/ref/Reference */ \ do_intrinsic(_Reference_get, java_lang_ref_Reference, get_name, void_object_signature, F_R) \ \ diff --git a/hotspot/src/share/vm/opto/c2_globals.hpp b/hotspot/src/share/vm/opto/c2_globals.hpp index fa481caae22..e1891226189 100644 --- a/hotspot/src/share/vm/opto/c2_globals.hpp +++ b/hotspot/src/share/vm/opto/c2_globals.hpp @@ -516,6 +516,9 @@ develop(bool, SpecialArraysEquals, true, \ "special version of Arrays.equals(char[],char[])") \ \ + product(bool, SpecialEncodeISOArray, true, \ + "special version of ISO_8859_1$Encoder.encodeISOArray") \ + \ develop(bool, BailoutToInterpreterForThrows, false, \ "Compiled methods which throws/catches exceptions will be " \ "deopt and intp.") \ diff --git a/hotspot/src/share/vm/opto/classes.hpp b/hotspot/src/share/vm/opto/classes.hpp index 3f726991dd0..f97b385f422 100644 --- a/hotspot/src/share/vm/opto/classes.hpp +++ b/hotspot/src/share/vm/opto/classes.hpp @@ -127,6 +127,7 @@ macro(DivL) macro(DivMod) macro(DivModI) macro(DivModL) +macro(EncodeISOArray) macro(EncodeP) macro(EncodePKlass) macro(ExpD) diff --git a/hotspot/src/share/vm/opto/escape.cpp b/hotspot/src/share/vm/opto/escape.cpp index 659828e509f..c5a93dfa68f 100644 --- a/hotspot/src/share/vm/opto/escape.cpp +++ b/hotspot/src/share/vm/opto/escape.cpp @@ -523,7 +523,8 @@ void ConnectionGraph::add_node_to_connection_graph(Node *n, Unique_Node_List *de case Op_AryEq: case Op_StrComp: case Op_StrEquals: - case Op_StrIndexOf: { + case Op_StrIndexOf: + case Op_EncodeISOArray: { add_local_var(n, PointsToNode::ArgEscape); delayed_worklist->push(n); // Process it later. break; @@ -701,7 +702,8 @@ void ConnectionGraph::add_final_edges(Node *n) { case Op_AryEq: case Op_StrComp: case Op_StrEquals: - case Op_StrIndexOf: { + case Op_StrIndexOf: + case Op_EncodeISOArray: { // char[] arrays passed to string intrinsic do not escape but // they are not scalar replaceable. Adjust escape state for them. // Start from in(2) edge since in(1) is memory edge. @@ -2581,15 +2583,22 @@ Node* ConnectionGraph::find_inst_mem(Node *orig_mem, int alias_idx, GrowableArra } // Otherwise skip it (the call updated 'result' value). } else if (result->Opcode() == Op_SCMemProj) { - assert(result->in(0)->is_LoadStore(), "sanity"); - const Type *at = igvn->type(result->in(0)->in(MemNode::Address)); + Node* mem = result->in(0); + Node* adr = NULL; + if (mem->is_LoadStore()) { + adr = mem->in(MemNode::Address); + } else { + assert(mem->Opcode() == Op_EncodeISOArray, "sanity"); + adr = mem->in(3); // Memory edge corresponds to destination array + } + const Type *at = igvn->type(adr); if (at != Type::TOP) { assert (at->isa_ptr() != NULL, "pointer type required."); int idx = C->get_alias_index(at->is_ptr()); assert(idx != alias_idx, "Object is not scalar replaceable if a LoadStore node access its field"); break; } - result = result->in(0)->in(MemNode::Memory); + result = mem->in(MemNode::Memory); } } if (result->is_Phi()) { @@ -2927,6 +2936,11 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) if (m->is_MergeMem()) { assert(_mergemem_worklist.contains(m->as_MergeMem()), "EA: missing MergeMem node in the worklist"); } + } else if (use->Opcode() == Op_EncodeISOArray) { + if (use->in(MemNode::Memory) == n || use->in(3) == n) { + // EncodeISOArray overwrites destination array + memnode_worklist.append_if_missing(use); + } } else { uint op = use->Opcode(); if (!(op == Op_CmpP || op == Op_Conv2B || @@ -2962,6 +2976,16 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) n = n->as_MemBar()->proj_out(TypeFunc::Memory); if (n == NULL) continue; + } else if (n->Opcode() == Op_EncodeISOArray) { + // get the memory projection + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + Node *use = n->fast_out(i); + if (use->Opcode() == Op_SCMemProj) { + n = use; + break; + } + } + assert(n->Opcode() == Op_SCMemProj, "memory projection required"); } else { assert(n->is_Mem(), "memory node required."); Node *addr = n->in(MemNode::Address); @@ -2999,7 +3023,7 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) Node *use = n->fast_out(i); if (use->is_Phi() || use->is_ClearArray()) { memnode_worklist.append_if_missing(use); - } else if(use->is_Mem() && use->in(MemNode::Memory) == n) { + } else if (use->is_Mem() && use->in(MemNode::Memory) == n) { if (use->Opcode() == Op_StoreCM) // Ignore cardmark stores continue; memnode_worklist.append_if_missing(use); @@ -3010,6 +3034,11 @@ void ConnectionGraph::split_unique_types(GrowableArray &alloc_worklist) assert(use->in(MemNode::Memory) != n, "EA: missing memory path"); } else if (use->is_MergeMem()) { assert(_mergemem_worklist.contains(use->as_MergeMem()), "EA: missing MergeMem node in the worklist"); + } else if (use->Opcode() == Op_EncodeISOArray) { + if (use->in(MemNode::Memory) == n || use->in(3) == n) { + // EncodeISOArray overwrites destination array + memnode_worklist.append_if_missing(use); + } } else { uint op = use->Opcode(); if (!(op == Op_StoreCM || diff --git a/hotspot/src/share/vm/opto/lcm.cpp b/hotspot/src/share/vm/opto/lcm.cpp index 5ca4bae13d7..35006ff23b4 100644 --- a/hotspot/src/share/vm/opto/lcm.cpp +++ b/hotspot/src/share/vm/opto/lcm.cpp @@ -175,6 +175,7 @@ void Block::implicit_null_check(PhaseCFG *cfg, Node *proj, Node *val, int allowe case Op_StrEquals: case Op_StrIndexOf: case Op_AryEq: + case Op_EncodeISOArray: // Not a legit memory op for implicit null check regardless of // embedded loads continue; diff --git a/hotspot/src/share/vm/opto/library_call.cpp b/hotspot/src/share/vm/opto/library_call.cpp index 406f605853d..e3aee707e4a 100644 --- a/hotspot/src/share/vm/opto/library_call.cpp +++ b/hotspot/src/share/vm/opto/library_call.cpp @@ -290,6 +290,7 @@ class LibraryCallKit : public GraphKit { bool inline_cipherBlockChaining_AESCrypt(vmIntrinsics::ID id); Node* inline_cipherBlockChaining_AESCrypt_predicate(bool decrypting); Node* get_key_start_from_aescrypt_object(Node* aescrypt_object); + bool inline_encodeISOArray(); }; @@ -381,6 +382,10 @@ CallGenerator* Compile::make_vm_intrinsic(ciMethod* m, bool is_virtual) { // These also use the arraycopy intrinsic mechanism: if (!InlineArrayCopy) return NULL; break; + case vmIntrinsics::_encodeISOArray: + if (!SpecialEncodeISOArray) return NULL; + if (!Matcher::match_rule_supported(Op_EncodeISOArray)) return NULL; + break; case vmIntrinsics::_checkIndex: // We do not intrinsify this. The optimizer does fine with it. return NULL; @@ -799,6 +804,9 @@ bool LibraryCallKit::try_to_inline() { case vmIntrinsics::_cipherBlockChaining_decryptAESCrypt: return inline_cipherBlockChaining_AESCrypt(intrinsic_id()); + case vmIntrinsics::_encodeISOArray: + return inline_encodeISOArray(); + default: // If you get here, it may be that someone has added a new intrinsic // to the list in vmSymbols.hpp without implementing it here. @@ -5368,6 +5376,47 @@ LibraryCallKit::generate_unchecked_arraycopy(const TypePtr* adr_type, src_start, dest_start, copy_length XTOP); } +//-------------inline_encodeISOArray----------------------------------- +// encode char[] to byte[] in ISO_8859_1 +bool LibraryCallKit::inline_encodeISOArray() { + assert(callee()->signature()->size() == 5, "encodeISOArray has 5 parameters"); + // no receiver since it is static method + Node *src = argument(0); + Node *src_offset = argument(1); + Node *dst = argument(2); + Node *dst_offset = argument(3); + Node *length = argument(4); + + const Type* src_type = src->Value(&_gvn); + const Type* dst_type = dst->Value(&_gvn); + const TypeAryPtr* top_src = src_type->isa_aryptr(); + const TypeAryPtr* top_dest = dst_type->isa_aryptr(); + if (top_src == NULL || top_src->klass() == NULL || + top_dest == NULL || top_dest->klass() == NULL) { + // failed array check + return false; + } + + // Figure out the size and type of the elements we will be copying. + BasicType src_elem = src_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + BasicType dst_elem = dst_type->isa_aryptr()->klass()->as_array_klass()->element_type()->basic_type(); + if (src_elem != T_CHAR || dst_elem != T_BYTE) { + return false; + } + Node* src_start = array_element_address(src, src_offset, src_elem); + Node* dst_start = array_element_address(dst, dst_offset, dst_elem); + // 'src_start' points to src array + scaled offset + // 'dst_start' points to dst array + scaled offset + + const TypeAryPtr* mtype = TypeAryPtr::BYTES; + Node* enc = new (C) EncodeISOArrayNode(control(), memory(mtype), src_start, dst_start, length); + enc = _gvn.transform(enc); + Node* res_mem = _gvn.transform(new (C) SCMemProjNode(enc)); + set_memory(res_mem, mtype); + set_result(enc); + return true; +} + //----------------------------inline_reference_get---------------------------- // public T java.lang.ref.Reference.get(); bool LibraryCallKit::inline_reference_get() { diff --git a/hotspot/src/share/vm/opto/loopTransform.cpp b/hotspot/src/share/vm/opto/loopTransform.cpp index 259e10a56df..b405bbcf6ba 100644 --- a/hotspot/src/share/vm/opto/loopTransform.cpp +++ b/hotspot/src/share/vm/opto/loopTransform.cpp @@ -613,6 +613,7 @@ bool IdealLoopTree::policy_maximally_unroll( PhaseIdealLoop *phase ) const { case Op_StrComp: case Op_StrEquals: case Op_StrIndexOf: + case Op_EncodeISOArray: case Op_AryEq: { return false; } @@ -717,6 +718,7 @@ bool IdealLoopTree::policy_unroll( PhaseIdealLoop *phase ) const { case Op_StrComp: case Op_StrEquals: case Op_StrIndexOf: + case Op_EncodeISOArray: case Op_AryEq: { // Do not unroll a loop with String intrinsics code. // String intrinsics are large and have loops. diff --git a/hotspot/src/share/vm/opto/macro.cpp b/hotspot/src/share/vm/opto/macro.cpp index 2c45140816e..2dde491380c 100644 --- a/hotspot/src/share/vm/opto/macro.cpp +++ b/hotspot/src/share/vm/opto/macro.cpp @@ -361,14 +361,21 @@ static Node *scan_mem_chain(Node *mem, int alias_idx, int offset, Node *start_me } // Otherwise skip it (the call updated 'mem' value). } else if (mem->Opcode() == Op_SCMemProj) { - assert(mem->in(0)->is_LoadStore(), "sanity"); - const TypePtr* atype = mem->in(0)->in(MemNode::Address)->bottom_type()->is_ptr(); + mem = mem->in(0); + Node* adr = NULL; + if (mem->is_LoadStore()) { + adr = mem->in(MemNode::Address); + } else { + assert(mem->Opcode() == Op_EncodeISOArray, "sanity"); + adr = mem->in(3); // Destination array + } + const TypePtr* atype = adr->bottom_type()->is_ptr(); int adr_idx = Compile::current()->get_alias_index(atype); if (adr_idx == alias_idx) { assert(false, "Object is not scalar replaceable if a LoadStore node access its field"); return NULL; } - mem = mem->in(0)->in(MemNode::Memory); + mem = mem->in(MemNode::Memory); } else { return mem; } @@ -445,7 +452,7 @@ Node *PhaseMacroExpand::value_from_mem_phi(Node *mem, BasicType ft, const Type * } values.at_put(j, val); } else if (val->Opcode() == Op_SCMemProj) { - assert(val->in(0)->is_LoadStore(), "sanity"); + assert(val->in(0)->is_LoadStore() || val->in(0)->Opcode() == Op_EncodeISOArray, "sanity"); assert(false, "Object is not scalar replaceable if a LoadStore node access its field"); return NULL; } else { diff --git a/hotspot/src/share/vm/opto/matcher.cpp b/hotspot/src/share/vm/opto/matcher.cpp index 0c6f5ea8ec2..ffd3cc28346 100644 --- a/hotspot/src/share/vm/opto/matcher.cpp +++ b/hotspot/src/share/vm/opto/matcher.cpp @@ -919,6 +919,7 @@ static void match_alias_type(Compile* C, Node* n, Node* m) { case Op_AryEq: case Op_MemBarVolatile: case Op_MemBarCPUOrder: // %%% these ideals should have narrower adr_type? + case Op_EncodeISOArray: nidx = Compile::AliasIdxTop; nat = NULL; break; @@ -1982,6 +1983,7 @@ void Matcher::find_shared( Node *n ) { case Op_StrEquals: case Op_StrIndexOf: case Op_AryEq: + case Op_EncodeISOArray: set_shared(n); // Force result into register (it will be anyways) break; case Op_ConP: { // Convert pointers above the centerline to NUL @@ -2183,6 +2185,13 @@ void Matcher::find_shared( Node *n ) { n->del_req(4); break; } + case Op_EncodeISOArray: { + // Restructure into a binary tree for Matching. + Node* pair = new (C) BinaryNode(n->in(3), n->in(4)); + n->set_req(3, pair); + n->del_req(4); + break; + } default: break; } diff --git a/hotspot/src/share/vm/opto/memnode.cpp b/hotspot/src/share/vm/opto/memnode.cpp index bea81f210da..35fc4b7fc7f 100644 --- a/hotspot/src/share/vm/opto/memnode.cpp +++ b/hotspot/src/share/vm/opto/memnode.cpp @@ -2796,6 +2796,26 @@ const Type *StrIntrinsicNode::Value( PhaseTransform *phase ) const { return bottom_type(); } +//============================================================================= +//------------------------------match_edge------------------------------------- +// Do not match memory edge +uint EncodeISOArrayNode::match_edge(uint idx) const { + return idx == 2 || idx == 3; // EncodeISOArray src (Binary dst len) +} + +//------------------------------Ideal------------------------------------------ +// Return a node which is more "ideal" than the current node. Strip out +// control copies +Node *EncodeISOArrayNode::Ideal(PhaseGVN *phase, bool can_reshape) { + return remove_dead_region(phase, can_reshape) ? this : NULL; +} + +//------------------------------Value------------------------------------------ +const Type *EncodeISOArrayNode::Value(PhaseTransform *phase) const { + if (in(0) && phase->type(in(0)) == Type::TOP) return Type::TOP; + return bottom_type(); +} + //============================================================================= MemBarNode::MemBarNode(Compile* C, int alias_idx, Node* precedent) : MultiNode(TypeFunc::Parms + (precedent == NULL? 0: 1)), diff --git a/hotspot/src/share/vm/opto/memnode.hpp b/hotspot/src/share/vm/opto/memnode.hpp index 92a3d12feba..640eacb116a 100644 --- a/hotspot/src/share/vm/opto/memnode.hpp +++ b/hotspot/src/share/vm/opto/memnode.hpp @@ -888,6 +888,22 @@ public: virtual const Type* bottom_type() const { return TypeInt::BOOL; } }; + +//------------------------------EncodeISOArray-------------------------------- +// encode char[] to byte[] in ISO_8859_1 +class EncodeISOArrayNode: public Node { +public: + EncodeISOArrayNode(Node *control, Node* arymem, Node* s1, Node* s2, Node* c): Node(control, arymem, s1, s2, c) {}; + virtual int Opcode() const; + virtual bool depends_only_on_test() const { return false; } + virtual const Type* bottom_type() const { return TypeInt::INT; } + virtual const TypePtr* adr_type() const { return TypePtr::BOTTOM; } + virtual uint match_edge(uint idx) const; + virtual uint ideal_reg() const { return Op_RegI; } + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *Value(PhaseTransform *phase) const; +}; + //------------------------------MemBar----------------------------------------- // There are different flavors of Memory Barriers to match the Java Memory // Model. Monitor-enter and volatile-load act as Aquires: no following ref diff --git a/hotspot/test/compiler/6896617/Test6896617.java b/hotspot/test/compiler/6896617/Test6896617.java new file mode 100644 index 00000000000..e28d3d7d57b --- /dev/null +++ b/hotspot/test/compiler/6896617/Test6896617.java @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6896617 + * @summary Optimize sun.nio.cs.ISO_8859_1$Encode.encodeArrayLoop() with SSE instructions on x86 + * @run main/othervm/timeout=1200 -Xbatch -Xmx256m Test6896617 + * + */ + +import java.util.*; +import java.nio.*; +import java.nio.charset.*; + +public class Test6896617 { + final static int SIZE = 256; + + public static void main(String[] args) { + String csn = "ISO-8859-1"; + Charset cs = Charset.forName(csn); + CharsetEncoder enc = cs.newEncoder(); + enc.onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE); + CharsetDecoder dec = cs.newDecoder(); + dec.onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE); + + byte repl = (byte)'?'; + enc.replaceWith(new byte[] { repl }); + + // Use internal API for tests. + sun.nio.cs.ArrayEncoder arrenc = (sun.nio.cs.ArrayEncoder)enc; + sun.nio.cs.ArrayDecoder arrdec = (sun.nio.cs.ArrayDecoder)dec; + + // Populate char[] with chars which can be encoded by ISO_8859_1 (<= 0xFF) + Random rnd = new Random(0); + int maxchar = 0xFF; + char[] a = new char[SIZE]; + byte[] b = new byte[SIZE]; + char[] at = new char[SIZE]; + byte[] bt = new byte[SIZE]; + for (int i = 0; i < SIZE; i++) { + char c = (char) rnd.nextInt(maxchar); + if (!enc.canEncode(c)) { + System.out.printf("Something wrong: can't encode c=%03x\n", (int)c); + System.exit(97); + } + a[i] = c; + b[i] = (byte)c; + at[i] = (char)-1; + bt[i] = (byte)-1; + } + if (arrenc.encode(a, 0, SIZE, bt) != SIZE || !Arrays.equals(b, bt)) { + System.out.println("Something wrong: ArrayEncoder.encode failed"); + System.exit(97); + } + if (arrdec.decode(b, 0, SIZE, at) != SIZE || !Arrays.equals(a, at)) { + System.out.println("Something wrong: ArrayDecoder.decode failed"); + System.exit(97); + } + for (int i = 0; i < SIZE; i++) { + at[i] = (char)-1; + bt[i] = (byte)-1; + } + + ByteBuffer bb = ByteBuffer.wrap(b); + CharBuffer ba = CharBuffer.wrap(a); + ByteBuffer bbt = ByteBuffer.wrap(bt); + CharBuffer bat = CharBuffer.wrap(at); + if (!enc.encode(ba, bbt, true).isUnderflow() || !Arrays.equals(b, bt)) { + System.out.println("Something wrong: Encoder.encode failed"); + System.exit(97); + } + if (!dec.decode(bb, bat, true).isUnderflow() || !Arrays.equals(a, at)) { + System.out.println("Something wrong: Decoder.decode failed"); + System.exit(97); + } + for (int i = 0; i < SIZE; i++) { + at[i] = (char)-1; + bt[i] = (byte)-1; + } + + // Warm up + boolean failed = false; + int result = 0; + for (int i = 0; i < 10000; i++) { + result += arrenc.encode(a, 0, SIZE, bt); + result -= arrdec.decode(b, 0, SIZE, at); + } + for (int i = 0; i < 10000; i++) { + result += arrenc.encode(a, 0, SIZE, bt); + result -= arrdec.decode(b, 0, SIZE, at); + } + for (int i = 0; i < 10000; i++) { + result += arrenc.encode(a, 0, SIZE, bt); + result -= arrdec.decode(b, 0, SIZE, at); + } + if (result != 0 || !Arrays.equals(b, bt) || !Arrays.equals(a, at)) { + failed = true; + System.out.println("Failed: ArrayEncoder.encode char[" + SIZE + "] and ArrayDecoder.decode byte[" + SIZE + "]"); + } + for (int i = 0; i < SIZE; i++) { + at[i] = (char)-1; + bt[i] = (byte)-1; + } + + boolean is_underflow = true; + for (int i = 0; i < 10000; i++) { + ba.clear(); bb.clear(); bat.clear(); bbt.clear(); + boolean enc_res = enc.encode(ba, bbt, true).isUnderflow(); + boolean dec_res = dec.decode(bb, bat, true).isUnderflow(); + is_underflow = is_underflow && enc_res && dec_res; + } + for (int i = 0; i < SIZE; i++) { + at[i] = (char)-1; + bt[i] = (byte)-1; + } + for (int i = 0; i < 10000; i++) { + ba.clear(); bb.clear(); bat.clear(); bbt.clear(); + boolean enc_res = enc.encode(ba, bbt, true).isUnderflow(); + boolean dec_res = dec.decode(bb, bat, true).isUnderflow(); + is_underflow = is_underflow && enc_res && dec_res; + } + for (int i = 0; i < SIZE; i++) { + at[i] = (char)-1; + bt[i] = (byte)-1; + } + for (int i = 0; i < 10000; i++) { + ba.clear(); bb.clear(); bat.clear(); bbt.clear(); + boolean enc_res = enc.encode(ba, bbt, true).isUnderflow(); + boolean dec_res = dec.decode(bb, bat, true).isUnderflow(); + is_underflow = is_underflow && enc_res && dec_res; + } + if (!is_underflow || !Arrays.equals(b, bt) || !Arrays.equals(a, at)) { + failed = true; + System.out.println("Failed: Encoder.encode char[" + SIZE + "] and Decoder.decode byte[" + SIZE + "]"); + } + + // Test encoder with different source and destination sizes + System.out.println("Testing different source and destination sizes"); + for (int i = 1; i <= SIZE; i++) { + for (int j = 1; j <= SIZE; j++) { + bt = new byte[j]; + // very source's SIZE + result = arrenc.encode(a, 0, i, bt); + int l = Math.min(i, j); + if (result != l) { + failed = true; + System.out.println("Failed: encode char[" + i + "] to byte[" + j + "]: result = " + result + ", expected " + l); + } + for (int k = 0; k < l; k++) { + if (bt[k] != b[k]) { + failed = true; + System.out.println("Failed: encoded byte[" + k + "] (" + bt[k] + ") != " + b[k]); + } + } + // very source's offset + int sz = SIZE - i + 1; + result = arrenc.encode(a, i-1, sz, bt); + l = Math.min(sz, j); + if (result != l) { + failed = true; + System.out.println("Failed: encode char[" + sz + "] to byte[" + j + "]: result = " + result + ", expected " + l); + } + for (int k = 0; k < l; k++) { + if (bt[k] != b[i+k-1]) { + failed = true; + System.out.println("Failed: encoded byte[" + k + "] (" + bt[k] + ") != " + b[i+k-1]); + } + } + } + } + + // Test encoder with char > 0xFF + System.out.println("Testing big char"); + + byte orig = (byte)'A'; + bt = new byte[SIZE]; + for (int i = 1; i <= SIZE; i++) { + for (int j = 0; j < i; j++) { + a[j] += 0x100; + // make sure to replace a different byte + bt[j] = orig; + result = arrenc.encode(a, 0, i, bt); + if (result != i) { + failed = true; + System.out.println("Failed: encode char[" + i + "] to byte[" + i + "]: result = " + result + ", expected " + i); + } + if (bt[j] != repl) { + failed = true; + System.out.println("Failed: encoded replace byte[" + j + "] (" + bt[j] + ") != " + repl); + } + bt[j] = b[j]; // Restore to compare whole array + for (int k = 0; k < i; k++) { + if (bt[k] != b[k]) { + failed = true; + System.out.println("Failed: encoded byte[" + k + "] (" + bt[k] + ") != " + b[k]); + } + } + a[j] -= 0x100; // Restore + } + } + + // Test sun.nio.cs.ISO_8859_1$Encode.encodeArrayLoop() performance. + + int itrs = Integer.getInteger("iterations", 1000000); + int size = Integer.getInteger("size", 256); + a = new char[size]; + b = new byte[size]; + bt = new byte[size]; + for (int i = 0; i < size; i++) { + char c = (char) rnd.nextInt(maxchar); + if (!enc.canEncode(c)) { + System.out.printf("Something wrong: can't encode c=%03x\n", (int)c); + System.exit(97); + } + a[i] = c; + b[i] = (byte)-1; + bt[i] = (byte)c; + } + ba = CharBuffer.wrap(a); + bb = ByteBuffer.wrap(b); + boolean enc_res = enc.encode(ba, bb, true).isUnderflow(); + if (!enc_res || !Arrays.equals(b, bt)) { + failed = true; + System.out.println("Failed 1: Encoder.encode char[" + size + "]"); + } + for (int i = 0; i < size; i++) { + b[i] = (byte)-1; + } + + // Make sure to recompile method if needed before performance run. + for (int i = 0; i < 10000; i++) { + ba.clear(); bb.clear(); + enc_res = enc_res && enc.encode(ba, bb, true).isUnderflow(); + } + for (int i = 0; i < size; i++) { + b[i] = (byte)-1; + } + for (int i = 0; i < 10000; i++) { + ba.clear(); bb.clear(); + enc_res = enc_res && enc.encode(ba, bb, true).isUnderflow(); + } + if (!enc_res || !Arrays.equals(b, bt)) { + failed = true; + System.out.println("Failed 2: Encoder.encode char[" + size + "]"); + } + for (int i = 0; i < size; i++) { + b[i] = (byte)-1; + } + + System.out.println("Testing ISO_8859_1$Encode.encodeArrayLoop() performance"); + long start = System.currentTimeMillis(); + for (int i = 0; i < itrs; i++) { + ba.clear(); bb.clear(); + enc_res = enc_res && enc.encode(ba, bb, true).isUnderflow(); + } + long end = System.currentTimeMillis(); + if (!enc_res || !Arrays.equals(b, bt)) { + failed = true; + System.out.println("Failed 3: Encoder.encode char[" + size + "]"); + } else { + System.out.println("size: " + size + " time: " + (end - start)); + } + + // Test sun.nio.cs.ISO_8859_1$Encode.encode() performance. + + // Make sure to recompile method if needed before performance run. + result = 0; + for (int i = 0; i < size; i++) { + b[i] = (byte)-1; + } + for (int i = 0; i < 10000; i++) { + result += arrenc.encode(a, 0, size, b); + } + for (int i = 0; i < size; i++) { + b[i] = (byte)-1; + } + for (int i = 0; i < 10000; i++) { + result += arrenc.encode(a, 0, size, b); + } + if (result != size*20000 || !Arrays.equals(b, bt)) { + failed = true; + System.out.println("Failed 1: ArrayEncoder.encode char[" + SIZE + "]"); + } + for (int i = 0; i < size; i++) { + b[i] = (byte)-1; + } + + System.out.println("Testing ISO_8859_1$Encode.encode() performance"); + result = 0; + start = System.currentTimeMillis(); + for (int i = 0; i < itrs; i++) { + result += arrenc.encode(a, 0, size, b); + } + end = System.currentTimeMillis(); + if (!Arrays.equals(b, bt)) { + failed = true; + System.out.println("Failed 2: ArrayEncoder.encode char[" + size + "]"); + } else { + System.out.println("size: " + size + " time: " + (end - start)); + } + + if (failed) { + System.out.println("FAILED"); + System.exit(97); + } + System.out.println("PASSED"); + } +} From 4b926cfd51ce31c59f2db5a46e77d357c7898ba8 Mon Sep 17 00:00:00 2001 From: Stephen Colebourne Date: Tue, 22 Jan 2013 21:02:06 -0800 Subject: [PATCH 117/138] 8003680: JSR 310 Date/Time API Integration of JSR310 Date/Time API for M6 Co-authored-by: Roger Riggs Co-authored-by: Richard Warburton Co-authored-by: Michael Nascimento Reviewed-by: alanb, naoto, dholmes --- common/makefiles/javadoc/CORE_PKGS.gmk | 5 +++++ make/jprt.properties | 1 + test/Makefile | 3 ++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/common/makefiles/javadoc/CORE_PKGS.gmk b/common/makefiles/javadoc/CORE_PKGS.gmk index dcfda026ded..b91aae1b650 100644 --- a/common/makefiles/javadoc/CORE_PKGS.gmk +++ b/common/makefiles/javadoc/CORE_PKGS.gmk @@ -127,6 +127,11 @@ CORE_PKGS = \ java.sql \ java.text \ java.text.spi \ + java.time \ + java.time.temporal \ + java.time.calendar \ + java.time.format \ + java.time.zone \ java.util \ java.util.concurrent \ java.util.concurrent.atomic \ diff --git a/make/jprt.properties b/make/jprt.properties index 990c442a164..b7bad856278 100644 --- a/make/jprt.properties +++ b/make/jprt.properties @@ -92,6 +92,7 @@ jprt.make.rule.core.test.targets= \ ${jprt.my.test.target.set:TESTNAME=jdk_text}, \ ${jprt.my.test.target.set:TESTNAME=jdk_tools}, \ ${jprt.my.test.target.set:TESTNAME=jdk_jfr}, \ + ${jprt.my.test.target.set:TESTNAME=jdk_time}, \ ${jprt.my.test.target.set:TESTNAME=jdk_other} # All vm test targets (testset=all) diff --git a/test/Makefile b/test/Makefile index 9e4d509b550..701d39c4bfb 100644 --- a/test/Makefile +++ b/test/Makefile @@ -63,7 +63,8 @@ JDK_DEFAULT_TEST_LIST = \ jdk_nio \ jdk_security1 \ jdk_text \ - jdk_util + jdk_util \ + jdk_time # These tests are not part of the default testing list JDK_NONDEFAULT_TEST_LIST = \ From 036eecdd790928ca149b8f3d2536fcdea6e6b33a Mon Sep 17 00:00:00 2001 From: Erik Joelsson Date: Wed, 23 Jan 2013 11:37:36 +0100 Subject: [PATCH 118/138] 8005855: build-infra: Remove -R flag when cross compiling Reviewed-by: dholmes, tbell --- common/autoconf/generated-configure.sh | 8 +++++++- common/autoconf/libraries.m4 | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/common/autoconf/generated-configure.sh b/common/autoconf/generated-configure.sh index 2ab9711e3d3..694623e1e95 100644 --- a/common/autoconf/generated-configure.sh +++ b/common/autoconf/generated-configure.sh @@ -3723,7 +3723,7 @@ fi #CUSTOM_AUTOCONF_INCLUDE # Do not change or remove the following line, it is needed for consistency checks: -DATE_WHEN_GENERATED=1358499442 +DATE_WHEN_GENERATED=1358937404 ############################################################################### # @@ -29247,6 +29247,12 @@ fi fi +# AC_PATH_XTRA creates X_LIBS and sometimes adds -R flags. When cross compiling +# this doesn't make sense so we remove it. +if test "x$COMPILE_TYPE" = xcross; then + X_LIBS=`$ECHO $X_LIBS | $SED 's/-R \{0,1\}[^ ]*//g'` +fi + if test "x$no_x" = xyes && test "x$X11_NOT_NEEDED" != xyes; then # Print a helpful message on how to acquire the necessary build dependency. diff --git a/common/autoconf/libraries.m4 b/common/autoconf/libraries.m4 index e1fea0726d6..91b0a846e5f 100644 --- a/common/autoconf/libraries.m4 +++ b/common/autoconf/libraries.m4 @@ -136,6 +136,12 @@ fi AC_PATH_X AC_PATH_XTRA +# AC_PATH_XTRA creates X_LIBS and sometimes adds -R flags. When cross compiling +# this doesn't make sense so we remove it. +if test "x$COMPILE_TYPE" = xcross; then + X_LIBS=`$ECHO $X_LIBS | $SED 's/-R \{0,1\}[[^ ]]*//g'` +fi + if test "x$no_x" = xyes && test "x$X11_NOT_NEEDED" != xyes; then HELP_MSG_MISSING_DEPENDENCY([x11]) AC_MSG_ERROR([Could not find X11 libraries. $HELP_MSG]) From 0a44d7bfec580aca502b9319ff71a3720217cf15 Mon Sep 17 00:00:00 2001 From: Erik Joelsson Date: Wed, 23 Jan 2013 11:41:06 +0100 Subject: [PATCH 119/138] 8006663: build-infra: Compare two arbitrary zip/jar files with compare.sh Reviewed-by: tbell --- common/bin/compare.sh | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/common/bin/compare.sh b/common/bin/compare.sh index 1e1ceb08ae6..0ba46727acc 100644 --- a/common/bin/compare.sh +++ b/common/bin/compare.sh @@ -350,9 +350,15 @@ compare_zip_file() { OTHER_DIR=$2 WORK_DIR=$3 ZIP_FILE=$4 + # Optionally provide different name for other zipfile + OTHER_ZIP_FILE=$5 THIS_ZIP=$THIS_DIR/$ZIP_FILE - OTHER_ZIP=$OTHER_DIR/$ZIP_FILE + if [ -n "$OTHER_ZIP_FILE" ]; then + OTHER_ZIP=$OTHER_DIR/$OTHER_ZIP_FILE + else + OTHER_ZIP=$OTHER_DIR/$ZIP_FILE + fi THIS_SUFFIX="${THIS_ZIP##*.}" OTHER_SUFFIX="${OTHER_ZIP##*.}" @@ -962,6 +968,9 @@ if [ -z "$1" ] || [ "$1" = "-h" ] || [ "$1" = "-?" ] || [ "$1" = "/h" ] || [ "$1 echo "[FILTER] List filenames in the image to compare, works for jars, zips, libs and execs" echo "Example:" echo "bash ./common/bin/compareimages.sh CodePointIM.jar" + echo "" + echo "-2zips Compare two zip files only" + echo "" exit 10 fi @@ -1023,6 +1032,13 @@ while [ -n "$1" ]; do -execs) CMP_EXECS=true ;; + -2zips) + CMP_2_ZIPS=true + THIS_FILE=$2 + OTHER_FILE=$3 + shift + shift + ;; *) CMP_NAMES=false CMP_PERMS=false @@ -1041,6 +1057,18 @@ while [ -n "$1" ]; do shift done +if [ "$CMP_2_ZIPS" = "true" ]; then + THIS_DIR="$(dirname $THIS_FILE)" + THIS_DIR="$(cd "$THIS_DIR" && pwd )" + OTHER_DIR="$(dirname $OTHER_FILE)" + OTHER_DIR="$(cd "$OTHER_DIR" && pwd )" + THIS_FILE_NAME="$(basename $THIS_FILE)" + OTHER_FILE_NAME="$(basename $OTHER_FILE)" + echo Comparing $THIS_DIR/$THIS_FILE_NAME and $OTHER_DIR/$OTHER_FILE_NAME + compare_zip_file $THIS_DIR $OTHER_DIR $COMPARE_ROOT/2zips $THIS_FILE_NAME $OTHER_FILE_NAME + exit +fi + if [ "$CMP_NAMES" = "false" ] && [ "$CMP_TYPES" = "false" ] && [ "$CMP_PERMS" = "false" ] && [ "$CMP_GENERAL" = "false" ] && [ "$CMP_ZIPS" = "false" ] && [ "$CMP_JARS" = "false" ] && [ "$CMP_LIBS" = "false" ] && [ "$CMP_EXECS" = "false" ]; then CMP_NAMES=true CMP_PERMS=true From af4998b10e3a28ff98d5967aa4b3af79efb5adb1 Mon Sep 17 00:00:00 2001 From: Erik Joelsson Date: Wed, 23 Jan 2013 11:42:29 +0100 Subject: [PATCH 120/138] 8006658: build-infra: Make MILESTONE behave the same as JDK_BUILD_NUMBER Reviewed-by: ohrstrom, dholmes, tbell --- common/autoconf/generated-configure.sh | 5 +++-- common/autoconf/jdk-options.m4 | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/common/autoconf/generated-configure.sh b/common/autoconf/generated-configure.sh index 694623e1e95..3bc6c12118b 100644 --- a/common/autoconf/generated-configure.sh +++ b/common/autoconf/generated-configure.sh @@ -3723,7 +3723,7 @@ fi #CUSTOM_AUTOCONF_INCLUDE # Do not change or remove the following line, it is needed for consistency checks: -DATE_WHEN_GENERATED=1358937404 +DATE_WHEN_GENERATED=1358937713 ############################################################################### # @@ -10778,7 +10778,8 @@ if test "x$with_milestone" = xyes; then as_fn_error $? "Milestone must have a value" "$LINENO" 5 elif test "x$with_milestone" != x; then MILESTONE="$with_milestone" -else +fi +if test "x$MILESTONE" = x; then MILESTONE=internal fi diff --git a/common/autoconf/jdk-options.m4 b/common/autoconf/jdk-options.m4 index d9f188d65d8..48eb3f85c0e 100644 --- a/common/autoconf/jdk-options.m4 +++ b/common/autoconf/jdk-options.m4 @@ -389,7 +389,8 @@ if test "x$with_milestone" = xyes; then AC_MSG_ERROR([Milestone must have a value]) elif test "x$with_milestone" != x; then MILESTONE="$with_milestone" -else +fi +if test "x$MILESTONE" = x; then MILESTONE=internal fi From f1cf6ff58828d56d56513c127d84e99ac35ef2f9 Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Wed, 23 Jan 2013 11:47:07 -0800 Subject: [PATCH 121/138] 8006799: Optimize sun.nio.cs.ISO_8859_1$Encode.encodeArrayLoop() (jdk part of 6896617) Move hot loop in ISO_8859_1$Encode.encodeArrayLoop() into separate method encodeISOArray() to be replaced by JVM JIT compiler with optimized intrinsic code. Reviewed-by: alanb, sherman --- .../share/classes/sun/nio/cs/ISO_8859_1.java | 79 ++++++++++++------- 1 file changed, 49 insertions(+), 30 deletions(-) diff --git a/jdk/src/share/classes/sun/nio/cs/ISO_8859_1.java b/jdk/src/share/classes/sun/nio/cs/ISO_8859_1.java index 7da5c12e70c..559f7e1cea8 100644 --- a/jdk/src/share/classes/sun/nio/cs/ISO_8859_1.java +++ b/jdk/src/share/classes/sun/nio/cs/ISO_8859_1.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2004, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -147,37 +147,53 @@ class ISO_8859_1 private final Surrogate.Parser sgp = new Surrogate.Parser(); + // JVM may replace this method with intrinsic code. + private static int encodeISOArray(char[] sa, int sp, + byte[] da, int dp, int len) + { + int i = 0; + for (; i < len; i++) { + char c = sa[sp++]; + if (c > '\u00FF') + break; + da[dp++] = (byte)c; + } + return i; + } + private CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) { char[] sa = src.array(); - int sp = src.arrayOffset() + src.position(); - int sl = src.arrayOffset() + src.limit(); + int soff = src.arrayOffset(); + int sp = soff + src.position(); + int sl = soff + src.limit(); assert (sp <= sl); sp = (sp <= sl ? sp : sl); byte[] da = dst.array(); - int dp = dst.arrayOffset() + dst.position(); - int dl = dst.arrayOffset() + dst.limit(); + int doff = dst.arrayOffset(); + int dp = doff + dst.position(); + int dl = doff + dst.limit(); assert (dp <= dl); dp = (dp <= dl ? dp : dl); + int dlen = dl - dp; + int slen = sl - sp; + int len = (dlen < slen) ? dlen : slen; try { - while (sp < sl) { - char c = sa[sp]; - if (c <= '\u00FF') { - if (dp >= dl) - return CoderResult.OVERFLOW; - da[dp++] = (byte)c; - sp++; - continue; - } - if (sgp.parse(c, sa, sp, sl) < 0) + int ret = encodeISOArray(sa, sp, da, dp, len); + sp = sp + ret; + dp = dp + ret; + if (ret != len) { + if (sgp.parse(sa[sp], sa, sp, sl) < 0) return sgp.error(); return sgp.unmappableResult(); } + if (len < slen) + return CoderResult.OVERFLOW; return CoderResult.UNDERFLOW; } finally { - src.position(sp - src.arrayOffset()); - dst.position(dp - dst.arrayOffset()); + src.position(sp - soff); + dst.position(dp - doff); } } @@ -221,22 +237,25 @@ class ISO_8859_1 public int encode(char[] src, int sp, int len, byte[] dst) { int dp = 0; - int sl = sp + Math.min(len, dst.length); + int slen = Math.min(len, dst.length); + int sl = sp + slen; while (sp < sl) { - char c = src[sp++]; - if (c <= '\u00FF') { - dst[dp++] = (byte)c; - continue; - } - if (Character.isHighSurrogate(c) && sp < sl && - Character.isLowSurrogate(src[sp])) { - if (len > dst.length) { - sl++; - len--; + int ret = encodeISOArray(src, sp, dst, dp, slen); + sp = sp + ret; + dp = dp + ret; + if (ret != slen) { + char c = src[sp++]; + if (Character.isHighSurrogate(c) && sp < sl && + Character.isLowSurrogate(src[sp])) { + if (len > dst.length) { + sl++; + len--; + } + sp++; } - sp++; + dst[dp++] = repl; + slen = Math.min((sl - sp), (dst.length - dp)); } - dst[dp++] = repl; } return dp; } From 89152955605ad92d71d5ed48e89f2571417ea40b Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Wed, 23 Jan 2013 15:11:03 -0800 Subject: [PATCH 122/138] 8003878: compiler/7196199 test failed on OS X since 8b54, jdk7u12b01 Limit vectors size to 16 bytes on BSD until the problem is fixed Reviewed-by: twisti --- hotspot/src/cpu/x86/vm/vm_version_x86.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp index 01d99c37219..90066c1041a 100644 --- a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp +++ b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp @@ -661,6 +661,14 @@ void VM_Version::get_processor_features() { } } } +#if defined(COMPILER2) && defined(_ALLBSD_SOURCE) + if (MaxVectorSize > 16) { + // Limit vectors size to 16 bytes on BSD until it fixes + // restoring upper 128bit of YMM registers on return + // from signal handler. + FLAG_SET_DEFAULT(MaxVectorSize, 16); + } +#endif // COMPILER2 // Use population count instruction if available. if (supports_popcnt()) { From ae1a2f5e3c8f92b39804dcba66b984bde4292880 Mon Sep 17 00:00:00 2001 From: Krystal Mo Date: Thu, 24 Jan 2013 02:03:38 -0800 Subject: [PATCH 123/138] 8006758: LinkResolver assertion (caused by @Contended changes) Treat anonymous classes as privileged code to restore the special handling for @Compiled during class file parsing Reviewed-by: jrose, coleenp, kvn, dholmes --- hotspot/src/share/vm/classfile/classFileParser.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/hotspot/src/share/vm/classfile/classFileParser.cpp b/hotspot/src/share/vm/classfile/classFileParser.cpp index e0558af122b..557707ba432 100644 --- a/hotspot/src/share/vm/classfile/classFileParser.cpp +++ b/hotspot/src/share/vm/classfile/classFileParser.cpp @@ -1802,11 +1802,9 @@ ClassFileParser::AnnotationCollector::ID ClassFileParser::AnnotationCollector::annotation_index(ClassLoaderData* loader_data, Symbol* name) { vmSymbols::SID sid = vmSymbols::find_sid(name); - bool privileged = false; - if (loader_data->is_the_null_class_loader_data()) { - // Privileged code can use all annotations. Other code silently drops some. - privileged = true; - } + // Privileged code can use all annotations. Other code silently drops some. + bool privileged = loader_data->is_the_null_class_loader_data() || + loader_data->is_anonymous(); switch (sid) { case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_ForceInline_signature): if (_location != _in_method) break; // only allow for methods From 7782e252f16cbd8a426ad736ac43a197b4d93d8e Mon Sep 17 00:00:00 2001 From: Alexander Potochkin Date: Thu, 24 Jan 2013 15:26:40 +0400 Subject: [PATCH 124/138] 7147078: [macosx] Echo char set in TextField doesn't prevent word jumping Reviewed-by: art --- .../com/apple/laf/AquaKeyBindings.java | 15 ++++++++++++ .../com/apple/laf/AquaLookAndFeel.java | 2 +- .../classes/sun/lwawt/LWTextFieldPeer.java | 23 +++++++++++++------ 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/jdk/src/macosx/classes/com/apple/laf/AquaKeyBindings.java b/jdk/src/macosx/classes/com/apple/laf/AquaKeyBindings.java index bbb804ae3f1..88db59f0b9f 100644 --- a/jdk/src/macosx/classes/com/apple/laf/AquaKeyBindings.java +++ b/jdk/src/macosx/classes/com/apple/laf/AquaKeyBindings.java @@ -142,6 +142,21 @@ public class AquaKeyBindings { })); } + LateBoundInputMap getPasswordFieldInputMap() { + return new LateBoundInputMap(new SimpleBinding(getTextFieldInputMap().getBindings()), + // nullify all the bindings that may discover space characters in the text + new SimpleBinding(new String[] { + "alt LEFT", null, + "alt KP_LEFT", null, + "alt RIGHT", null, + "alt KP_RIGHT", null, + "shift alt LEFT", null, + "shift alt KP_LEFT", null, + "shift alt RIGHT", null, + "shift alt KP_RIGHT", null, + })); + } + LateBoundInputMap getMultiLineTextInputMap() { return new LateBoundInputMap(new SimpleBinding(commonTextEditorBindings), new SimpleBinding(new String[] { "ENTER", DefaultEditorKit.insertBreakAction, diff --git a/jdk/src/macosx/classes/com/apple/laf/AquaLookAndFeel.java b/jdk/src/macosx/classes/com/apple/laf/AquaLookAndFeel.java index 4f108da2df0..457b82d36a3 100644 --- a/jdk/src/macosx/classes/com/apple/laf/AquaLookAndFeel.java +++ b/jdk/src/macosx/classes/com/apple/laf/AquaLookAndFeel.java @@ -697,7 +697,7 @@ public class AquaLookAndFeel extends BasicLookAndFeel { "Panel.foreground", black, "Panel.opaque", useOpaqueComponents, - "PasswordField.focusInputMap", aquaKeyBindings.getTextFieldInputMap(), + "PasswordField.focusInputMap", aquaKeyBindings.getPasswordFieldInputMap(), "PasswordField.font", controlFont, "PasswordField.background", textBackground, "PasswordField.foreground", textForeground, diff --git a/jdk/src/macosx/classes/sun/lwawt/LWTextFieldPeer.java b/jdk/src/macosx/classes/sun/lwawt/LWTextFieldPeer.java index 7532f620149..ef2b4d087e2 100644 --- a/jdk/src/macosx/classes/sun/lwawt/LWTextFieldPeer.java +++ b/jdk/src/macosx/classes/sun/lwawt/LWTextFieldPeer.java @@ -34,7 +34,7 @@ import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.peer.TextFieldPeer; -import javax.swing.JPasswordField; +import javax.swing.*; import javax.swing.text.JTextComponent; final class LWTextFieldPeer @@ -48,7 +48,7 @@ final class LWTextFieldPeer @Override protected JPasswordField createDelegate() { - return new JTextAreaDelegate(); + return new JPasswordFieldDelegate(); } @Override @@ -69,9 +69,18 @@ final class LWTextFieldPeer public void setEchoChar(final char echoChar) { synchronized (getDelegateLock()) { getDelegate().setEchoChar(echoChar); - getDelegate().putClientProperty("JPasswordField.cutCopyAllowed", - getDelegate().echoCharIsSet() - ? Boolean.FALSE : Boolean.TRUE); + final boolean cutCopyAllowed; + final String focusInputMapKey; + if (echoChar != 0) { + cutCopyAllowed = false; + focusInputMapKey = "PasswordField.focusInputMap"; + } else { + cutCopyAllowed = true; + focusInputMapKey = "TextField.focusInputMap"; + } + getDelegate().putClientProperty("JPasswordField.cutCopyAllowed", cutCopyAllowed); + InputMap inputMap = (InputMap) UIManager.get(focusInputMapKey); + SwingUtilities.replaceUIInputMap(getDelegate(), JComponent.WHEN_FOCUSED, inputMap); } } @@ -106,11 +115,11 @@ final class LWTextFieldPeer super.handleJavaFocusEvent(e); } - private final class JTextAreaDelegate extends JPasswordField { + private final class JPasswordFieldDelegate extends JPasswordField { // Empty non private constructor was added because access to this // class shouldn't be emulated by a synthetic accessor method. - JTextAreaDelegate() { + JPasswordFieldDelegate() { super(); } From 6eb458d36453af70db7612168e05592078b935e1 Mon Sep 17 00:00:00 2001 From: Alexander Potochkin Date: Thu, 24 Jan 2013 15:52:25 +0400 Subject: [PATCH 125/138] 7132793: [macosx] setWheelScrollEnabled action reversed Reviewed-by: serb, art --- .../classes/sun/lwawt/LWComponentPeer.java | 2 +- .../classes/sun/lwawt/LWScrollPanePeer.java | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/jdk/src/macosx/classes/sun/lwawt/LWComponentPeer.java b/jdk/src/macosx/classes/sun/lwawt/LWComponentPeer.java index 55b4665a752..aa9b798c617 100644 --- a/jdk/src/macosx/classes/sun/lwawt/LWComponentPeer.java +++ b/jdk/src/macosx/classes/sun/lwawt/LWComponentPeer.java @@ -1226,7 +1226,7 @@ public abstract class LWComponentPeer sendEventToDelegate(e); } - private void sendEventToDelegate(final AWTEvent e) { + protected void sendEventToDelegate(final AWTEvent e) { synchronized (getDelegateLock()) { if (getDelegate() == null || !isShowing() || !isEnabled()) { return; diff --git a/jdk/src/macosx/classes/sun/lwawt/LWScrollPanePeer.java b/jdk/src/macosx/classes/sun/lwawt/LWScrollPanePeer.java index 1e386f25725..723a9dd8da7 100644 --- a/jdk/src/macosx/classes/sun/lwawt/LWScrollPanePeer.java +++ b/jdk/src/macosx/classes/sun/lwawt/LWScrollPanePeer.java @@ -29,6 +29,7 @@ import javax.swing.*; import javax.swing.event.ChangeListener; import javax.swing.event.ChangeEvent; import java.awt.*; +import java.awt.event.MouseWheelEvent; import java.awt.peer.ScrollPanePeer; import java.util.List; @@ -51,6 +52,21 @@ final class LWScrollPanePeer extends LWContainerPeer return sp; } + @Override + public void handleEvent(AWTEvent e) { + if (e instanceof MouseWheelEvent) { + MouseWheelEvent wheelEvent = (MouseWheelEvent) e; + //java.awt.ScrollPane consumes the event + // in case isWheelScrollingEnabled() is true, + // forcibly send the consumed event to the delegate + if (getTarget().isWheelScrollingEnabled() && wheelEvent.isConsumed()) { + sendEventToDelegate(wheelEvent); + } + } else { + super.handleEvent(e); + } + } + @Override public void stateChanged(final ChangeEvent e) { SwingUtilities.invokeLater(new Runnable() { From 3f2ea7f894d876792aff7c243d700229331cabf1 Mon Sep 17 00:00:00 2001 From: Petr Pchelko Date: Thu, 24 Jan 2013 15:55:04 +0400 Subject: [PATCH 126/138] 8005997: [macosx] Printer Dialog opens an additional title bar Reviewed-by: anthony, art --- jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java b/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java index c20f0a97ec8..54f15f66c99 100644 --- a/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java +++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java @@ -923,6 +923,10 @@ public final class CPlatformWindow extends CFRetainedResource implements Platfor return false; } + if (blocker instanceof CPrinterDialogPeer) { + return true; + } + CPlatformWindow pWindow = (CPlatformWindow)blocker.getPlatformWindow(); pWindow.orderAboveSiblings(); From c41878d46d98fa25a364cad74bb5359dfacc44ce Mon Sep 17 00:00:00 2001 From: Alexander Zuev Date: Thu, 24 Jan 2013 16:09:48 +0400 Subject: [PATCH 127/138] 7143768: [macosx] Unexpected NullPointerException and java.io.IOException during DnD Reviewed-by: alexp --- .../sun/lwawt/macosx/CDataTransferer.java | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/jdk/src/macosx/classes/sun/lwawt/macosx/CDataTransferer.java b/jdk/src/macosx/classes/sun/lwawt/macosx/CDataTransferer.java index 459895523e2..5b56b19ac1c 100644 --- a/jdk/src/macosx/classes/sun/lwawt/macosx/CDataTransferer.java +++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CDataTransferer.java @@ -273,8 +273,31 @@ public class CDataTransferer extends DataTransferer { @Override protected ByteArrayOutputStream convertFileListToBytes(ArrayList fileList) throws IOException { - // TODO Auto-generated method stub - return null; + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + for (int i = 0; i < fileList.size(); i++) + { + byte[] bytes = fileList.get(i).getBytes(); + bos.write(bytes, 0, bytes.length); + bos.write(0); + } + return bos; + } + + @Override + protected boolean isURIListFormat(long format) { + String nat = getNativeForFormat(format); + if (nat == null) { + return false; + } + try { + DataFlavor df = new DataFlavor(nat); + if (df.getPrimaryType().equals("text") && df.getSubType().equals("uri-list")) { + return true; + } + } catch (Exception e) { + // Not a MIME format. + } + return false; } } From d22b9b71497108e67d65b5bd7b2ddb462bdc3eef Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Thu, 24 Jan 2013 17:26:32 +0400 Subject: [PATCH 128/138] 6817933: Setting the background of an HTML Widget changes the native Windows JFileChooser Reviewed-by: alexsch --- .../classes/sun/swing/WindowsPlacesBar.java | 5 +- .../JFileChooser/6817933/Test6817933.java | 121 ++++++++++++++++++ 2 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 jdk/test/javax/swing/JFileChooser/6817933/Test6817933.java diff --git a/jdk/src/share/classes/sun/swing/WindowsPlacesBar.java b/jdk/src/share/classes/sun/swing/WindowsPlacesBar.java index a05259e5978..8b033ca3e7f 100644 --- a/jdk/src/share/classes/sun/swing/WindowsPlacesBar.java +++ b/jdk/src/share/classes/sun/swing/WindowsPlacesBar.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -116,9 +116,6 @@ public class WindowsPlacesBar extends JToolBar icon = fsv.getSystemIcon(files[i]); } buttons[i] = new JToggleButton(folderName, icon); - if (isXPPlatform) { - buttons[i].setText("

      "+folderName+"
      "); - } if (isXPStyle) { buttons[i].putClientProperty("XPStyle.subAppName", "placesbar"); } else { diff --git a/jdk/test/javax/swing/JFileChooser/6817933/Test6817933.java b/jdk/test/javax/swing/JFileChooser/6817933/Test6817933.java new file mode 100644 index 00000000000..2b65f173a18 --- /dev/null +++ b/jdk/test/javax/swing/JFileChooser/6817933/Test6817933.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6817933 + * @summary Tests that HTMLEditorKit does not affect JFileChooser + * @author Sergey Malenkov + */ + +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Point; +import java.awt.Robot; +import java.awt.Toolkit; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JToggleButton; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.text.html.HTMLEditorKit; +import javax.swing.text.html.StyleSheet; + +import sun.awt.SunToolkit; +import sun.swing.WindowsPlacesBar; + +public class Test6817933 { + + private static final String STYLE = "BODY {background:red}"; + private static final Color COLOR = Color.RED; + private static JFileChooser chooser; + + public static void main(String[] args) throws Exception { + try { + UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); + } + catch (Exception exception) { + exception.printStackTrace(); + return; // ignore test on non-Windows machines + } + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + StyleSheet css = new StyleSheet(); + css.addRule(STYLE); + + HTMLEditorKit kit = new HTMLEditorKit(); + kit.setStyleSheet(css); + + JFrame frame = new JFrame(STYLE); + frame.add(chooser = new JFileChooser()); + frame.setSize(640, 480); + frame.setVisible(true); + } + }); + + SunToolkit toolkit = (SunToolkit) Toolkit.getDefaultToolkit(); + toolkit.realSync(500); + + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + try { + JToggleButton button = get(JToggleButton.class, + get(WindowsPlacesBar.class, chooser)); + + int width = button.getWidth(); + int height = button.getHeight() / 3; + Point point = new Point(0, height * 2); + SwingUtilities.convertPointToScreen(point, button); + width += point.x; + height += point.y; + + int count = 0; + Robot robot = new Robot(); + for (int x = point.x; x < width; x++) { + for (int y = point.y; y < height; y++) { + count += COLOR.equals(robot.getPixelColor(x, y)) ? -2 : 1; + } + } + if (count < 0) { + throw new Exception("TEST ERROR: a lot of red pixels"); + } + } + catch (Exception exception) { + throw new Error(exception); + } + finally { + SwingUtilities.getWindowAncestor(chooser).dispose(); + } + } + }); + } + + private static T get(Class type, Container container) { + Component component = container.getComponent(0); + if (!type.isAssignableFrom(component.getClass())) { + throw new IllegalStateException("expected " + type + "; expected " + component.getClass()); + } + return (T) component; + } +} From 2c1808e68da8f2372180005d14ce74af2f123f7c Mon Sep 17 00:00:00 2001 From: Sergey Bylokhov Date: Thu, 24 Jan 2013 17:50:03 +0400 Subject: [PATCH 129/138] 8003173: [macosx] Fullscreen on Mac leaves an empty rectangle Reviewed-by: anthony, alexsch --- .../classes/sun/awt/CGraphicsDevice.java | 20 ++- .../classes/sun/lwawt/LWWindowPeer.java | 40 ++--- .../sun/lwawt/macosx/CPlatformView.java | 24 +-- .../sun/lwawt/macosx/CPlatformWindow.java | 55 +++--- .../FullScreenInsets/FullScreenInsets.java | 156 ++++++++++++++++++ 5 files changed, 219 insertions(+), 76 deletions(-) create mode 100644 jdk/test/java/awt/FullScreen/FullScreenInsets/FullScreenInsets.java diff --git a/jdk/src/macosx/classes/sun/awt/CGraphicsDevice.java b/jdk/src/macosx/classes/sun/awt/CGraphicsDevice.java index 2b884538839..bcb24ea46f7 100644 --- a/jdk/src/macosx/classes/sun/awt/CGraphicsDevice.java +++ b/jdk/src/macosx/classes/sun/awt/CGraphicsDevice.java @@ -30,6 +30,7 @@ import java.awt.GraphicsDevice; import java.awt.Window; import java.awt.AWTPermission; import java.awt.DisplayMode; +import java.util.Objects; import sun.java2d.opengl.CGLGraphicsConfig; @@ -122,12 +123,12 @@ public final class CGraphicsDevice extends GraphicsDevice { boolean fsSupported = isFullScreenSupported(); if (fsSupported && old != null) { - // enter windowed mode (and restore original display mode) - exitFullScreenExclusive(old); + // restore original display mode and enter windowed mode. if (originalMode != null) { setDisplayMode(originalMode); originalMode = null; } + exitFullScreenExclusive(old); } super.setFullScreenWindow(w); @@ -186,13 +187,20 @@ public final class CGraphicsDevice extends GraphicsDevice { } @Override - public void setDisplayMode(DisplayMode dm) { + public void setDisplayMode(final DisplayMode dm) { if (dm == null) { throw new IllegalArgumentException("Invalid display mode"); } - nativeSetDisplayMode(displayID, dm.getWidth(), dm.getHeight(), dm.getBitDepth(), dm.getRefreshRate()); - if (isFullScreenSupported() && getFullScreenWindow() != null) { - getFullScreenWindow().setSize(dm.getWidth(), dm.getHeight()); + if (!Objects.equals(dm, getDisplayMode())) { + final Window w = getFullScreenWindow(); + if (w != null) { + exitFullScreenExclusive(w); + } + nativeSetDisplayMode(displayID, dm.getWidth(), dm.getHeight(), + dm.getBitDepth(), dm.getRefreshRate()); + if (isFullScreenSupported() && w != null) { + enterFullScreenExclusive(w); + } } } diff --git a/jdk/src/macosx/classes/sun/lwawt/LWWindowPeer.java b/jdk/src/macosx/classes/sun/lwawt/LWWindowPeer.java index c11b626a764..5b19788f244 100644 --- a/jdk/src/macosx/classes/sun/lwawt/LWWindowPeer.java +++ b/jdk/src/macosx/classes/sun/lwawt/LWWindowPeer.java @@ -53,7 +53,7 @@ public class LWWindowPeer private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.lwawt.focus.LWWindowPeer"); - private PlatformWindow platformWindow; + private final PlatformWindow platformWindow; // Window bounds reported by the native system (as opposed to // regular bounds inherited from LWComponentPeer which are @@ -554,12 +554,14 @@ public class LWWindowPeer /** * Called by the {@code PlatformWindow} when this window is moved/resized by - * user. There's no notifyReshape() in LWComponentPeer as the only - * components which could be resized by user are top-level windows. + * user or window insets are changed. There's no notifyReshape() in + * LWComponentPeer as the only components which could be resized by user are + * top-level windows. */ public final void notifyReshape(int x, int y, int w, int h) { final boolean moved; final boolean resized; + final boolean invalid = updateInsets(platformWindow.getInsets()); synchronized (getStateLock()) { moved = (x != sysX) || (y != sysY); resized = (w != sysW) || (h != sysH); @@ -570,7 +572,7 @@ public class LWWindowPeer } // Check if anything changed - if (!moved && !resized) { + if (!moved && !resized && !invalid) { return; } // First, update peer's bounds @@ -584,10 +586,10 @@ public class LWWindowPeer } // Third, COMPONENT_MOVED/COMPONENT_RESIZED/PAINT events - if (moved) { + if (moved || invalid) { handleMove(x, y, true); } - if (resized) { + if (resized || invalid) { handleResize(w, h, true); repaintPeer(); } @@ -999,27 +1001,21 @@ public class LWWindowPeer } } - /* - * Request the window insets from the delegate and compares it - * with the current one. This method is mostly called by the - * delegate, e.g. when the window state is changed and insets - * should be recalculated. - * + /** + * Request the window insets from the delegate and compares it with the + * current one. This method is mostly called by the delegate, e.g. when the + * window state is changed and insets should be recalculated. + *

      * This method may be called on the toolkit thread. */ - public boolean updateInsets(Insets newInsets) { - boolean changed = false; + public final boolean updateInsets(final Insets newInsets) { synchronized (getStateLock()) { - changed = (insets.equals(newInsets)); + if (insets.equals(newInsets)) { + return false; + } insets = newInsets; } - - if (changed) { - replaceSurfaceData(); - repaintPeer(); - } - - return changed; + return true; } public static LWWindowPeer getWindowUnderCursor() { diff --git a/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformView.java b/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformView.java index 4972301e780..495657963bd 100644 --- a/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformView.java +++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformView.java @@ -27,7 +27,6 @@ package sun.lwawt.macosx; import java.awt.*; import java.awt.geom.Rectangle2D; -import java.awt.image.VolatileImage; import sun.awt.CGraphicsConfig; import sun.awt.CGraphicsEnvironment; @@ -89,29 +88,8 @@ public class CPlatformView extends CFRetainedResource { return peer; } - public void enterFullScreenMode(final long nsWindowPtr) { + public void enterFullScreenMode() { CWrapper.NSView.enterFullScreenMode(ptr); - - // REMIND: CGLSurfaceData expects top-level's size - // and therefore we need to account insets before - // recreating the surface data - Insets insets = peer.getInsets(); - - Rectangle screenBounds; - final long screenPtr = CWrapper.NSWindow.screen(nsWindowPtr); - try { - screenBounds = CWrapper.NSScreen.frame(screenPtr).getBounds(); - } finally { - CWrapper.NSObject.release(screenPtr); - } - - // the move/size notification from the underlying system comes - // but it contains a bounds smaller than the whole screen - // and therefore we need to create the synthetic notifications - peer.notifyReshape(screenBounds.x - insets.left, - screenBounds.y - insets.bottom, - screenBounds.width + insets.left + insets.right, - screenBounds.height + insets.top + insets.bottom); } public void exitFullScreenMode() { diff --git a/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java b/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java index 54f15f66c99..8f83702a209 100644 --- a/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java +++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java @@ -38,7 +38,6 @@ import sun.awt.*; import sun.java2d.SurfaceData; import sun.java2d.opengl.CGLSurfaceData; import sun.lwawt.*; -import sun.lwawt.LWWindowPeer.PeerType; import sun.util.logging.PlatformLogger; import com.apple.laf.*; @@ -196,7 +195,8 @@ public final class CPlatformWindow extends CFRetainedResource implements Platfor // 1) setting native bounds via nativeSetBounds() call // 2) getting notification from the native level via deliverMoveResizeEvent() private Rectangle nativeBounds = new Rectangle(0, 0, 0, 0); - private volatile boolean isFullScreenMode = false; + private volatile boolean isFullScreenMode; + private boolean isFullScreenAnimationOn; private Window target; private LWWindowPeer peer; @@ -414,8 +414,10 @@ public final class CPlatformWindow extends CFRetainedResource implements Platfor @Override // PlatformWindow public Insets getInsets() { - final Insets insets = nativeGetNSWindowInsets(getNSWindowPtr()); - return insets; + if (!isFullScreenMode) { + return nativeGetNSWindowInsets(getNSWindowPtr()); + } + return new Insets(0, 0, 0, 0); } @Override // PlatformWindow @@ -727,7 +729,19 @@ public final class CPlatformWindow extends CFRetainedResource implements Platfor @Override public void enterFullScreenMode() { isFullScreenMode = true; - contentView.enterFullScreenMode(getNSWindowPtr()); + contentView.enterFullScreenMode(); + // the move/size notification from the underlying system comes + // but it contains a bounds smaller than the whole screen + // and therefore we need to create the synthetic notifications + Rectangle screenBounds; + final long screenPtr = CWrapper.NSWindow.screen(getNSWindowPtr()); + try { + screenBounds = CWrapper.NSScreen.frame(screenPtr).getBounds(); + } finally { + CWrapper.NSObject.release(screenPtr); + } + peer.notifyReshape(screenBounds.x, screenBounds.y, screenBounds.width, + screenBounds.height); } @Override @@ -874,11 +888,10 @@ public final class CPlatformWindow extends CFRetainedResource implements Platfor final Rectangle oldB = nativeBounds; nativeBounds = new Rectangle(x, y, width, height); peer.notifyReshape(x, y, width, height); - if (byUser && !oldB.getSize().equals(nativeBounds.getSize())) { + if ((byUser && !oldB.getSize().equals(nativeBounds.getSize())) + || isFullScreenAnimationOn) { flushBuffers(); } - //TODO validateSurface already called from notifyReshape - validateSurface(); } private void deliverWindowClosingEvent() { @@ -978,27 +991,19 @@ public final class CPlatformWindow extends CFRetainedResource implements Platfor orderAboveSiblings(); } - private void updateDisplay() { - EventQueue.invokeLater(new Runnable() { - public void run() { - validateSurface(); - } - }); - } - - private void updateWindowContent() { - ComponentEvent resizeEvent = new ComponentEvent(target, ComponentEvent.COMPONENT_RESIZED); - SunToolkit.postEvent(SunToolkit.targetToAppContext(target), resizeEvent); - } - private void windowWillEnterFullScreen() { - updateWindowContent(); + isFullScreenAnimationOn = true; } + private void windowDidEnterFullScreen() { - updateDisplay(); + isFullScreenAnimationOn = false; } + private void windowWillExitFullScreen() { - updateWindowContent(); + isFullScreenAnimationOn = true; + } + + private void windowDidExitFullScreen() { + isFullScreenAnimationOn = false; } - private void windowDidExitFullScreen() {} } diff --git a/jdk/test/java/awt/FullScreen/FullScreenInsets/FullScreenInsets.java b/jdk/test/java/awt/FullScreen/FullScreenInsets/FullScreenInsets.java new file mode 100644 index 00000000000..d42137af303 --- /dev/null +++ b/jdk/test/java/awt/FullScreen/FullScreenInsets/FullScreenInsets.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.AWTException; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.DisplayMode; +import java.awt.Frame; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Insets; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.Window; +import java.awt.image.BufferedImage; + +import sun.awt.SunToolkit; + +/** + * @test + * @bug 8003173 7019055 + * @summary Full-screen windows should have the proper insets. + * @author Sergey Bylokhov + */ +public final class FullScreenInsets { + + private static boolean passed = true; + + public static void main(final String[] args) { + final GraphicsEnvironment ge = GraphicsEnvironment + .getLocalGraphicsEnvironment(); + final GraphicsDevice[] devices = ge.getScreenDevices(); + + final Window wGreen = new Frame(); + wGreen.setBackground(Color.GREEN); + wGreen.setSize(300, 300); + wGreen.setVisible(true); + sleep(); + final Insets iGreen = wGreen.getInsets(); + final Dimension sGreen = wGreen.getSize(); + + final Window wRed = new Frame(); + wRed.setBackground(Color.RED); + wRed.setSize(300, 300); + wRed.setVisible(true); + sleep(); + final Insets iRed = wGreen.getInsets(); + final Dimension sRed = wGreen.getSize(); + + for (final GraphicsDevice device : devices) { + if (!device.isFullScreenSupported()) { + continue; + } + device.setFullScreenWindow(wGreen); + sleep(); + testWindowBounds(device.getDisplayMode(), wGreen); + testColor(wGreen, Color.GREEN); + + device.setFullScreenWindow(wRed); + sleep(); + testWindowBounds(device.getDisplayMode(), wRed); + testColor(wRed, Color.RED); + + device.setFullScreenWindow(null); + sleep(); + testInsets(wGreen.getInsets(), iGreen); + testInsets(wRed.getInsets(), iRed); + testSize(wGreen.getSize(), sGreen); + testSize(wRed.getSize(), sRed); + } + wGreen.dispose(); + wRed.dispose(); + if (!passed) { + throw new RuntimeException("Test failed"); + } + } + + private static void testSize(final Dimension actual, final Dimension exp) { + if (!exp.equals(actual)) { + System.err.println(" Wrong window size:" + + " Expected: " + exp + " Actual: " + actual); + passed = false; + } + } + + private static void testInsets(final Insets actual, final Insets exp) { + if (!actual.equals(exp)) { + System.err.println(" Wrong window insets:" + + " Expected: " + exp + " Actual: " + actual); + passed = false; + } + } + + private static void testWindowBounds(final DisplayMode dm, final Window w) { + if (w.getWidth() != dm.getWidth() || w.getHeight() != dm.getHeight()) { + System.err.println(" Wrong window bounds:" + + " Expected: width = " + dm.getWidth() + + ", height = " + dm.getHeight() + " Actual: " + + w.getSize()); + passed = false; + } + } + + private static void testColor(final Window w, final Color color) { + final Robot r; + try { + r = new Robot(w.getGraphicsConfiguration().getDevice()); + } catch (AWTException e) { + e.printStackTrace(); + passed = false; + return; + } + final BufferedImage bi = r.createScreenCapture(w.getBounds()); + for (int y = 0; y < bi.getHeight(); y++) { + for (int x = 0; x < bi.getWidth(); x++) { + if (bi.getRGB(x, y) != color.getRGB()) { + System.err.println( + "Incorrect pixel at " + x + "x" + y + " : " + + Integer.toHexString(bi.getRGB(x, y)) + + " ,expected : " + Integer.toHexString( + color.getRGB())); + passed = false; + return; + } + } + } + } + + private static void sleep() { + ((SunToolkit) Toolkit.getDefaultToolkit()).realSync(); + try { + Thread.sleep(2000); + } catch (InterruptedException ignored) { + } + } +} From 6d5f0df029a95b5e39c278d7603a5e2467e8ba18 Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Thu, 24 Jan 2013 17:57:02 +0400 Subject: [PATCH 130/138] 8005138: test/java/beans/Introspector/TestTypeResolver.java fails Reviewed-by: alexsch --- .../java/beans/Introspector/TestTypeResolver.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/jdk/test/java/beans/Introspector/TestTypeResolver.java b/jdk/test/java/beans/Introspector/TestTypeResolver.java index 6704d8f7e16..e6915192776 100644 --- a/jdk/test/java/beans/Introspector/TestTypeResolver.java +++ b/jdk/test/java/beans/Introspector/TestTypeResolver.java @@ -180,10 +180,22 @@ public class TestTypeResolver { return null; // not used } + public T[] getAnnotations(Class annotationClass) { + return null; // not used + } + public Annotation[] getAnnotations() { return null; // not used } + public T getDeclaredAnnotation(Class annotationClass) { + return null; // not used + } + + public T[] getDeclaredAnnotations(Class annotationClass) { + return null; // not used + } + public Annotation[] getDeclaredAnnotations() { return null; // not used } From 601fc96c2768b371b1d8975fa79475eef7f8a338 Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Thu, 24 Jan 2013 18:06:24 +0400 Subject: [PATCH 131/138] 8003400: JTree scrolling problem when using large model in WindowsLookAndFeel Reviewed-by: alexsch --- .../javax/swing/plaf/basic/BasicTreeUI.java | 25 +++- .../swing/JTree/8003400/Test8003400.java | 109 ++++++++++++++++++ 2 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 jdk/test/javax/swing/JTree/8003400/Test8003400.java diff --git a/jdk/src/share/classes/javax/swing/plaf/basic/BasicTreeUI.java b/jdk/src/share/classes/javax/swing/plaf/basic/BasicTreeUI.java index 497c0d6cac0..3d39cf6a876 100644 --- a/jdk/src/share/classes/javax/swing/plaf/basic/BasicTreeUI.java +++ b/jdk/src/share/classes/javax/swing/plaf/basic/BasicTreeUI.java @@ -1879,6 +1879,20 @@ public class BasicTreeUI extends TreeUI visRect.x -= i.left; visRect.y -= i.top; } + // we should consider a non-visible area above + Component component = SwingUtilities.getUnwrappedParent(tree); + if (component instanceof JViewport) { + component = component.getParent(); + if (component instanceof JScrollPane) { + JScrollPane pane = (JScrollPane) component; + JScrollBar bar = pane.getHorizontalScrollBar(); + if ((bar != null) && bar.isVisible()) { + int height = bar.getHeight(); + visRect.y -= height; + visRect.height += height; + } + } + } preferredSize.width = treeState.getPreferredWidth(visRect); } else { @@ -4504,7 +4518,7 @@ public class BasicTreeUI extends TreeUI } } - private void home(JTree tree, BasicTreeUI ui, int direction, + private void home(JTree tree, final BasicTreeUI ui, int direction, boolean addToSelection, boolean changeSelection) { // disable moving of lead unless in discontiguous mode @@ -4514,7 +4528,7 @@ public class BasicTreeUI extends TreeUI changeSelection = true; } - int rowCount = ui.getRowCount(tree); + final int rowCount = ui.getRowCount(tree); if (rowCount > 0) { if(direction == -1) { @@ -4566,6 +4580,13 @@ public class BasicTreeUI extends TreeUI ui.setLeadSelectionPath(ui.getPathForRow(tree, rowCount - 1), true); } + if (ui.isLargeModel()){ + SwingUtilities.invokeLater(new Runnable() { + public void run() { + ui.ensureRowsAreVisible(rowCount - 1, rowCount - 1); + } + }); + } } } } diff --git a/jdk/test/javax/swing/JTree/8003400/Test8003400.java b/jdk/test/javax/swing/JTree/8003400/Test8003400.java new file mode 100644 index 00000000000..f2102f8b1e4 --- /dev/null +++ b/jdk/test/javax/swing/JTree/8003400/Test8003400.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8003400 + * @summary Tests that JTree shows the last row + * @author Sergey Malenkov + * @run main/othervm Test8003400 + * @run main/othervm Test8003400 reverse + * @run main/othervm Test8003400 system + * @run main/othervm Test8003400 system reverse + */ + +import sun.awt.SunToolkit; + +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.event.KeyEvent; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTree; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.tree.DefaultMutableTreeNode; + +public class Test8003400 { + + private static final String TITLE = "Test JTree with a large model"; + private static List OBJECTS = Arrays.asList(TITLE, "x", "y", "z"); + private static JScrollPane pane; + + public static void main(String[] args) throws Exception { + for (String arg : args) { + if (arg.equals("reverse")) { + Collections.reverse(OBJECTS); + } + if (arg.equals("system")) { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } + } + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + DefaultMutableTreeNode root = new DefaultMutableTreeNode(); + + JTree tree = new JTree(root); + tree.setLargeModel(true); + tree.setRowHeight(16); + + JFrame frame = new JFrame(TITLE); + frame.add(pane = new JScrollPane(tree)); + frame.setSize(200, 100); + frame.setLocationRelativeTo(null); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + frame.setVisible(true); + + for (String object : OBJECTS) { + root.add(new DefaultMutableTreeNode(object)); + } + tree.expandRow(0); + } + }); + + SunToolkit toolkit = (SunToolkit) Toolkit.getDefaultToolkit(); + toolkit.realSync(500); + new Robot().keyPress(KeyEvent.VK_END); + toolkit.realSync(500); + + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + JTree tree = (JTree) pane.getViewport().getView(); + Rectangle inner = tree.getRowBounds(tree.getRowCount() - 1); + Rectangle outer = SwingUtilities.convertRectangle(tree, inner, pane); + outer.y += tree.getRowHeight() - 1 - pane.getVerticalScrollBar().getHeight(); + // error reporting only for automatic testing + if (null != System.getProperty("test.src", null)) { + SwingUtilities.getWindowAncestor(pane).dispose(); + if (outer.y != 0) { + throw new Error("TEST FAILED: " + outer.y); + } + } + } + }); + } +} From 5288b84f0aeceba081a97e25314c92ece5322abd Mon Sep 17 00:00:00 2001 From: David Katleman Date: Thu, 24 Jan 2013 16:48:33 -0800 Subject: [PATCH 132/138] Added tag jdk8-b74 for changeset dde885cc8685 --- .hgtags-top-repo | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags-top-repo b/.hgtags-top-repo index 8555b3366ec..53096d3923b 100644 --- a/.hgtags-top-repo +++ b/.hgtags-top-repo @@ -195,3 +195,4 @@ cdb401a60cea6ad5ef3f498725ed1decf8dda1ea jdk8-b68 51ad2a34342055333eb5f36e2fb514b027895708 jdk8-b71 c1be681d80a1f1c848dc671d664fccb19e046a12 jdk8-b72 93b9664f97eeb6f89397a8842318ebacaac9feb9 jdk8-b73 +b43aa5bd8ca5c8121336495382d35ecfa7a71536 jdk8-b74 From ad11d38f92225e708f8bbecd30706a5c7587e87c Mon Sep 17 00:00:00 2001 From: David Katleman Date: Thu, 24 Jan 2013 16:48:39 -0800 Subject: [PATCH 133/138] Added tag jdk8-b74 for changeset 5a4f1fb4c6ef --- corba/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/corba/.hgtags b/corba/.hgtags index 1a3ee17d1bb..c01bb26f197 100644 --- a/corba/.hgtags +++ b/corba/.hgtags @@ -195,3 +195,4 @@ d54dc53e223ed9ce7d5f4d2cd02ad9d5def3c2db jdk8-b59 8171d23e914d758836527b80b06debcfdb718f2d jdk8-b71 cb40427f47145b01b7e53c3e02b38ff7625efbda jdk8-b72 191afde59e7be0e1a1d76d06f2a32ff17444f0ec jdk8-b73 +2132845cf5f717ff5c240a2431c0c0e03e66e3a5 jdk8-b74 From 86f89a0768cf6004a1a812b563f73aa7b14e24eb Mon Sep 17 00:00:00 2001 From: David Katleman Date: Thu, 24 Jan 2013 16:48:45 -0800 Subject: [PATCH 134/138] Added tag jdk8-b74 for changeset 8d54b69d4504 --- hotspot/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/hotspot/.hgtags b/hotspot/.hgtags index 617e00e138a..ad0530d1939 100644 --- a/hotspot/.hgtags +++ b/hotspot/.hgtags @@ -308,3 +308,4 @@ e94068d4ff52849c8aa0786a53a59b63d1312a39 jdk8-b70 d5cb5830f570d1304ea4b196dde672a291b55f29 jdk8-b72 1e129851479e4f5df439109fca2c7be1f1613522 hs25-b15 11619f33cd683c2f1d6ef72f1c6ff3dacf5a9f1c jdk8-b73 +1a3e54283c54aaa8b3437813e8507fbdc966e5b6 jdk8-b74 From 686adf319b544c41debb75dc7fe27c2229323d38 Mon Sep 17 00:00:00 2001 From: David Katleman Date: Thu, 24 Jan 2013 16:49:20 -0800 Subject: [PATCH 135/138] Added tag jdk8-b74 for changeset 64054e252871 --- jdk/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/jdk/.hgtags b/jdk/.hgtags index e774b097c5e..dbc8d128f1f 100644 --- a/jdk/.hgtags +++ b/jdk/.hgtags @@ -195,3 +195,4 @@ a996b57e554198f4592a5f3c30f2f9f4075e545d jdk8-b70 2a5af0f766d0acd68a81fb08fe11fd66795f86af jdk8-b71 32a57e645e012a1f0665c075969ca598e0dbb948 jdk8-b72 733885f57e14cc27f5a5ff0dffe641d2fa3c704a jdk8-b73 +57d5d954462831ac353a1f40d3bb05ddb4620952 jdk8-b74 From 6e9aeb35208ec7e95bafc1d75e2807c91024f6ef Mon Sep 17 00:00:00 2001 From: Alejandro Murillo Date: Fri, 25 Jan 2013 02:36:28 -0800 Subject: [PATCH 136/138] Added tag hs25-b17 for changeset f767fc368725 --- hotspot/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/hotspot/.hgtags b/hotspot/.hgtags index 54ddc0b1e90..d46eb646def 100644 --- a/hotspot/.hgtags +++ b/hotspot/.hgtags @@ -310,3 +310,4 @@ d5cb5830f570d1304ea4b196dde672a291b55f29 jdk8-b72 11619f33cd683c2f1d6ef72f1c6ff3dacf5a9f1c jdk8-b73 70c89bd6b895a10d25ca70e08093c09ff2005fda hs25-b16 1a3e54283c54aaa8b3437813e8507fbdc966e5b6 jdk8-b74 +b4391649e91ea8d37f66317a03d6d2573a93d10d hs25-b17 From c2419823c8b4762e4e41a83d8dd9e170d3cd0a71 Mon Sep 17 00:00:00 2001 From: Eric Mccorkle Date: Sat, 26 Jan 2013 16:57:02 +0000 Subject: [PATCH 137/138] 8006503: JVM_PrintStackTrace is not used in JDK Reviewed-by: alanb, darcy --- jdk/src/share/javavm/export/jvm.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/jdk/src/share/javavm/export/jvm.h b/jdk/src/share/javavm/export/jvm.h index f25a34e8f2b..28885cfb14d 100644 --- a/jdk/src/share/javavm/export/jvm.h +++ b/jdk/src/share/javavm/export/jvm.h @@ -188,9 +188,6 @@ JVM_IsNaN(jdouble d); JNIEXPORT void JNICALL JVM_FillInStackTrace(JNIEnv *env, jobject throwable); -JNIEXPORT void JNICALL -JVM_PrintStackTrace(JNIEnv *env, jobject throwable, jobject printable); - JNIEXPORT jint JNICALL JVM_GetStackTraceDepth(JNIEnv *env, jobject throwable); From a1d7653c193af77712492373eb05615afd084c30 Mon Sep 17 00:00:00 2001 From: Erik Joelsson Date: Tue, 29 Jan 2013 16:35:24 +0100 Subject: [PATCH 138/138] 8006873: SWAT-b74 msvcr100.dll does not have the permission for all Reviewed-by: alanb, tbell --- jdk/makefiles/CopyFiles.gmk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jdk/makefiles/CopyFiles.gmk b/jdk/makefiles/CopyFiles.gmk index 8f99a113d3d..7de946c88e3 100644 --- a/jdk/makefiles/CopyFiles.gmk +++ b/jdk/makefiles/CopyFiles.gmk @@ -267,10 +267,12 @@ endif ifeq ($(OPENJDK_TARGET_OS),windows) MSVCR_TARGET := $(JDK_OUTPUTDIR)/bin/$(notdir $(MSVCR_DLL)) + # Chmod to avoid permission issues if bundles are unpacked on unix platforms. $(MSVCR_TARGET): $(MSVCR_DLL) $(MKDIR) -p $(@D) $(RM) $@ $(CP) $< $@ + $(CHMOD) a+rx $@ COPY_FILES += $(MSVCR_TARGET) endif