// Copyright 2011 the V8 project authors. 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 Google Inc. 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. #include "src/extensions/experimental/break-iterator.h" #include #include "unicode/brkiter.h" #include "unicode/locid.h" #include "unicode/rbbi.h" namespace v8 { namespace internal { v8::Persistent BreakIterator::break_iterator_template_; icu::BreakIterator* BreakIterator::UnpackBreakIterator( v8::Handle obj) { if (break_iterator_template_->HasInstance(obj)) { return static_cast( obj->GetPointerFromInternalField(0)); } return NULL; } icu::UnicodeString* BreakIterator::ResetAdoptedText( v8::Handle obj, v8::Handle value) { // Get the previous value from the internal field. icu::UnicodeString* text = static_cast( obj->GetPointerFromInternalField(1)); delete text; // Assign new value to the internal pointer. v8::String::Value text_value(value); text = new icu::UnicodeString( reinterpret_cast(*text_value), text_value.length()); obj->SetPointerInInternalField(1, text); // Return new unicode string pointer. return text; } void BreakIterator::DeleteBreakIterator(v8::Persistent object, void* param) { v8::Persistent persistent_object = v8::Persistent::Cast(object); // First delete the hidden C++ object. // Unpacking should never return NULL here. That would only happen if // this method is used as the weak callback for persistent handles not // pointing to a break iterator. delete UnpackBreakIterator(persistent_object); delete static_cast( persistent_object->GetPointerFromInternalField(1)); // Then dispose of the persistent handle to JS object. persistent_object.Dispose(); } // Throws a JavaScript exception. static v8::Handle ThrowUnexpectedObjectError() { // Returns undefined, and schedules an exception to be thrown. return v8::ThrowException(v8::Exception::Error( v8::String::New("BreakIterator method called on an object " "that is not a BreakIterator."))); } v8::Handle BreakIterator::BreakIteratorAdoptText( const v8::Arguments& args) { if (args.Length() != 1 || !args[0]->IsString()) { return v8::ThrowException(v8::Exception::SyntaxError( v8::String::New("Text input is required."))); } icu::BreakIterator* break_iterator = UnpackBreakIterator(args.Holder()); if (!break_iterator) { return ThrowUnexpectedObjectError(); } break_iterator->setText(*ResetAdoptedText(args.Holder(), args[0])); return v8::Undefined(); } v8::Handle BreakIterator::BreakIteratorFirst( const v8::Arguments& args) { icu::BreakIterator* break_iterator = UnpackBreakIterator(args.Holder()); if (!break_iterator) { return ThrowUnexpectedObjectError(); } return v8::Int32::New(break_iterator->first()); } v8::Handle BreakIterator::BreakIteratorNext( const v8::Arguments& args) { icu::BreakIterator* break_iterator = UnpackBreakIterator(args.Holder()); if (!break_iterator) { return ThrowUnexpectedObjectError(); } return v8::Int32::New(break_iterator->next()); } v8::Handle BreakIterator::BreakIteratorCurrent( const v8::Arguments& args) { icu::BreakIterator* break_iterator = UnpackBreakIterator(args.Holder()); if (!break_iterator) { return ThrowUnexpectedObjectError(); } return v8::Int32::New(break_iterator->current()); } v8::Handle BreakIterator::BreakIteratorBreakType( const v8::Arguments& args) { icu::BreakIterator* break_iterator = UnpackBreakIterator(args.Holder()); if (!break_iterator) { return ThrowUnexpectedObjectError(); } // TODO(cira): Remove cast once ICU fixes base BreakIterator class. icu::RuleBasedBreakIterator* rule_based_iterator = static_cast(break_iterator); int32_t status = rule_based_iterator->getRuleStatus(); // Keep return values in sync with JavaScript BreakType enum. if (status >= UBRK_WORD_NONE && status < UBRK_WORD_NONE_LIMIT) { return v8::Int32::New(UBRK_WORD_NONE); } else if (status >= UBRK_WORD_NUMBER && status < UBRK_WORD_NUMBER_LIMIT) { return v8::Int32::New(UBRK_WORD_NUMBER); } else if (status >= UBRK_WORD_LETTER && status < UBRK_WORD_LETTER_LIMIT) { return v8::Int32::New(UBRK_WORD_LETTER); } else if (status >= UBRK_WORD_KANA && status < UBRK_WORD_KANA_LIMIT) { return v8::Int32::New(UBRK_WORD_KANA); } else if (status >= UBRK_WORD_IDEO && status < UBRK_WORD_IDEO_LIMIT) { return v8::Int32::New(UBRK_WORD_IDEO); } else { return v8::Int32::New(-1); } } v8::Handle BreakIterator::JSBreakIterator( const v8::Arguments& args) { v8::HandleScope handle_scope; if (args.Length() != 2 || !args[0]->IsString() || !args[1]->IsString()) { return v8::ThrowException(v8::Exception::SyntaxError( v8::String::New("Locale and iterator type are required."))); } v8::String::Utf8Value locale(args[0]); icu::Locale icu_locale(*locale); UErrorCode status = U_ZERO_ERROR; icu::BreakIterator* break_iterator = NULL; v8::String::Utf8Value type(args[1]); if (!strcmp(*type, "character")) { break_iterator = icu::BreakIterator::createCharacterInstance(icu_locale, status); } else if (!strcmp(*type, "word")) { break_iterator = icu::BreakIterator::createWordInstance(icu_locale, status); } else if (!strcmp(*type, "sentence")) { break_iterator = icu::BreakIterator::createSentenceInstance(icu_locale, status); } else if (!strcmp(*type, "line")) { break_iterator = icu::BreakIterator::createLineInstance(icu_locale, status); } else { return v8::ThrowException(v8::Exception::SyntaxError( v8::String::New("Invalid iterator type."))); } if (U_FAILURE(status)) { delete break_iterator; return v8::ThrowException(v8::Exception::Error( v8::String::New("Failed to create break iterator."))); } if (break_iterator_template_.IsEmpty()) { v8::Local raw_template(v8::FunctionTemplate::New()); raw_template->SetClassName(v8::String::New("v8Locale.v8BreakIterator")); // Define internal field count on instance template. v8::Local object_template = raw_template->InstanceTemplate(); // Set aside internal fields for icu break iterator and adopted text. object_template->SetInternalFieldCount(2); // Define all of the prototype methods on prototype template. v8::Local proto = raw_template->PrototypeTemplate(); proto->Set(v8::String::New("adoptText"), v8::FunctionTemplate::New(BreakIteratorAdoptText)); proto->Set(v8::String::New("first"), v8::FunctionTemplate::New(BreakIteratorFirst)); proto->Set(v8::String::New("next"), v8::FunctionTemplate::New(BreakIteratorNext)); proto->Set(v8::String::New("current"), v8::FunctionTemplate::New(BreakIteratorCurrent)); proto->Set(v8::String::New("breakType"), v8::FunctionTemplate::New(BreakIteratorBreakType)); break_iterator_template_ = v8::Persistent::New(raw_template); } // Create an empty object wrapper. v8::Local local_object = break_iterator_template_->GetFunction()->NewInstance(); v8::Persistent wrapper = v8::Persistent::New(local_object); // Set break iterator as internal field of the resulting JS object. wrapper->SetPointerInInternalField(0, break_iterator); // Make sure that the pointer to adopted text is NULL. wrapper->SetPointerInInternalField(1, NULL); // Make object handle weak so we can delete iterator once GC kicks in. wrapper.MakeWeak(NULL, DeleteBreakIterator); return wrapper; } } } // namespace v8::internal