| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "Properties.h" |
| |
| #include <map> |
| #include <sstream> |
| #include <decaf/util/Date.h> |
| #include <decaf/util/Map.h> |
| #include <decaf/util/StlMap.h> |
| #include <decaf/io/BufferedInputStream.h> |
| #include <decaf/lang/Character.h> |
| #include <decaf/lang/Integer.h> |
| #include <decaf/lang/exceptions/UnsupportedOperationException.h> |
| |
| using namespace decaf; |
| using namespace decaf::util; |
| using namespace decaf::lang; |
| using namespace decaf::lang::exceptions; |
| using namespace decaf::io; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace decaf{ |
| namespace util{ |
| |
| class PropertiesInternal{ |
| public: |
| |
| decaf::util::StlMap< std::string, std::string > properties; |
| |
| PropertiesInternal() : properties() {} |
| }; |
| |
| }} |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| namespace { |
| |
| /** |
| * internal namespace for Properties utility methods, these might change often |
| * so we hide them in here to preserve ABI rules on the header. |
| */ |
| |
| enum ParsingMode { |
| PARSE_MODE_NONE = 0, |
| PARSE_MODE_SLASH = 1, |
| PARSE_MODE_CONTINUE = 2, |
| PARSE_MODE_KEY_DONE = 3, |
| PARSE_MODE_IGNORE = 4 |
| }; |
| |
| void dumpString(std::ostringstream& buffer, const std::string& string, bool key) { |
| |
| std::size_t i = 0; |
| if (!key && i < string.length() && string.at(i) == ' ') { |
| buffer << "\\ "; |
| i++; |
| } |
| |
| for (; i < string.length(); i++) { |
| |
| char ch = string.at(i); |
| |
| switch(ch) { |
| case '\t': |
| buffer << "\\t"; |
| break; |
| case '\n': |
| buffer << "\\n"; |
| break; |
| case '\f': |
| buffer << "\\f"; |
| break; |
| case '\r': |
| buffer << "\\r"; |
| break; |
| default: |
| if (std::string("\\#!=:").find(ch) != std::string::npos || (key && ch == ' ')) { |
| buffer << '\\'; |
| } |
| |
| if (ch >= ' ' && ch <= '~') { |
| buffer << ch; |
| } else { |
| |
| std::string hex = Integer::toHexString(ch); |
| buffer << "\\u"; |
| for (std::size_t j = 0; j < 4 - hex.length(); j++) { |
| buffer << "0"; |
| } |
| buffer << hex; |
| } |
| break; |
| } |
| } |
| } |
| |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| Properties::Properties() : internal(new PropertiesInternal()), defaults() { |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| Properties::Properties(const Properties& src) : internal(new PropertiesInternal()), defaults() { |
| |
| this->internal->properties.copy(src.internal->properties); |
| |
| if (src.defaults.get() != NULL) { |
| this->defaults.reset(src.defaults->clone()); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| Properties::~Properties() { |
| try { |
| delete this->internal; |
| } |
| DECAF_CATCH_NOTHROW(Exception) |
| DECAF_CATCHALL_NOTHROW() |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| Properties& Properties::operator=(const Properties& source) { |
| |
| if (this == &source) { |
| return *this; |
| } |
| |
| this->copy(source); |
| |
| return *this; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| bool Properties::isEmpty() const { |
| synchronized( &( internal->properties ) ) { |
| return internal->properties.isEmpty(); |
| } |
| |
| return false; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| int Properties::size() const { |
| |
| synchronized( &( internal->properties ) ) { |
| return (int) internal->properties.size(); |
| } |
| |
| return 0; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| const char* Properties::getProperty(const std::string& name) const { |
| |
| synchronized( &( internal->properties ) ) { |
| if (this->internal->properties.containsKey(name)) { |
| return this->internal->properties.get(name).c_str(); |
| } else if (this->defaults != NULL && this->defaults->hasProperty(name)) { |
| return this->defaults->getProperty(name); |
| } |
| } |
| |
| return NULL; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| std::string Properties::getProperty(const std::string& name, const std::string& defaultValue) const { |
| |
| synchronized(&(internal->properties)) { |
| if (this->internal->properties.containsKey(name)) { |
| return this->internal->properties.get(name); |
| } else if (this->defaults != NULL && this->defaults->hasProperty(name)) { |
| return this->defaults->getProperty(name); |
| } |
| } |
| |
| return defaultValue; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| std::string Properties::setProperty(const std::string& name, const std::string& value) { |
| |
| std::string oldValue; |
| synchronized(&(internal->properties)) { |
| |
| if (internal->properties.containsKey(name)) { |
| oldValue = internal->properties.get(name); |
| } |
| |
| internal->properties.put(name, value); |
| } |
| |
| return oldValue; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| bool Properties::hasProperty(const std::string& name) const { |
| |
| synchronized( &( internal->properties ) ) { |
| return this->internal->properties.containsKey(name); |
| } |
| |
| return false; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| std::string Properties::remove(const std::string& name) { |
| |
| std::string oldValue; |
| |
| synchronized( &( internal->properties ) ) { |
| if (this->internal->properties.containsKey(name)) { |
| oldValue = this->internal->properties.get(name); |
| this->internal->properties.remove(name); |
| } |
| } |
| |
| return oldValue; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| std::vector<std::pair<std::string, std::string> > Properties::toArray() const { |
| |
| std::vector<std::pair<std::string, std::string> > result; |
| |
| synchronized( &( internal->properties ) ) { |
| Pointer<Iterator<MapEntry<std::string, std::string> > > entries( |
| this->internal->properties.entrySet().iterator()); |
| while (entries->hasNext()) { |
| MapEntry<std::string, std::string> entry = entries->next(); |
| result.push_back(std::make_pair(entry.getKey(), entry.getValue())); |
| } |
| } |
| |
| return result; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void Properties::copy( const Properties& source ){ |
| |
| if (&source == this) { |
| return; |
| } |
| |
| synchronized( &( this->internal->properties ) ) { |
| |
| synchronized( &( source.internal->properties ) ) { |
| this->internal->properties.copy(source.internal->properties); |
| |
| if (source.defaults.get() != NULL) { |
| this->defaults.reset(source.defaults->clone()); |
| } |
| } |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| Properties* Properties::clone() const { |
| |
| Properties* props = new Properties(); |
| props->internal->properties.copy(this->internal->properties); |
| return props; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void Properties::clear() { |
| |
| synchronized( &( internal->properties ) ) { |
| this->internal->properties.clear(); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| bool Properties::equals(const Properties& source) const { |
| return this->internal->properties.equals(source.internal->properties); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| std::string Properties::toString() const { |
| |
| std::ostringstream stream; |
| std::map<std::string, std::string>::const_iterator iter; |
| |
| stream << "Begin Class decaf::util::Properties:" << std::endl; |
| |
| synchronized(&(internal->properties)) { |
| Pointer<Iterator<MapEntry<std::string, std::string> > > entries( |
| this->internal->properties.entrySet().iterator()); |
| while (entries->hasNext()) { |
| MapEntry<std::string, std::string> entry = entries->next(); |
| stream << " property[" << entry.getKey() << "] = " << entry.getValue() << std::endl; |
| } |
| } |
| |
| stream << "End Class decaf::util::Properties:" << std::endl; |
| |
| return stream.str(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| std::vector<std::string> Properties::propertyNames() const { |
| StlMap<std::string, std::string> selectedProperties; |
| this->selectProperties(selectedProperties); |
| return selectedProperties.keySet().toArray(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void Properties::selectProperties(StlMap<std::string, std::string>& selectProperties) const { |
| |
| if (this->defaults != NULL) { |
| this->defaults->selectProperties(selectProperties); |
| } |
| |
| Pointer<Iterator<MapEntry<std::string, std::string> > > entries( |
| this->internal->properties.entrySet().iterator()); |
| while (entries->hasNext()) { |
| MapEntry<std::string, std::string> entry = entries->next(); |
| selectProperties.put(entry.getKey(), entry.getValue()); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void Properties::load(decaf::io::InputStream* stream) { |
| |
| try{ |
| |
| if (stream == NULL) { |
| throw NullPointerException( |
| __FILE__, __LINE__, "The Stream instance passed was Null"); |
| } |
| |
| int mode = PARSE_MODE_NONE; |
| char nextChar; |
| std::vector<char> buf; |
| int offset = 0; |
| int keyLength = -1; |
| int intVal; |
| bool firstChar = true; |
| BufferedInputStream bis(stream); |
| |
| while (true) { |
| |
| intVal = bis.read(); |
| |
| if (intVal == -1) { |
| break; |
| } |
| |
| nextChar = (char) (intVal & 0xFF); |
| |
| if (mode == PARSE_MODE_SLASH) { |
| |
| mode = PARSE_MODE_NONE; |
| switch( nextChar ) { |
| case '\r': |
| mode = PARSE_MODE_CONTINUE; // Look for a following \n |
| continue; |
| case '\n': |
| mode = PARSE_MODE_IGNORE; // Ignore whitespace on the next line |
| continue; |
| case 'b': |
| nextChar = '\b'; |
| break; |
| case 'f': |
| nextChar = '\f'; |
| break; |
| case 'n': |
| nextChar = '\n'; |
| break; |
| case 'r': |
| nextChar = '\r'; |
| break; |
| case 't': |
| nextChar = '\t'; |
| break; |
| } |
| |
| } else { |
| |
| switch( nextChar ) { |
| case '#': |
| case '!': |
| if (firstChar) { |
| while (true) { |
| intVal = bis.read(); |
| if (intVal == -1) { |
| break; |
| } |
| |
| nextChar = (char) (intVal & 0xFF); |
| |
| if (nextChar == '\r' || nextChar == '\n') { |
| break; |
| } |
| } |
| continue; |
| } |
| break; |
| case '\n': |
| if (mode == PARSE_MODE_CONTINUE) { // Part of a \r\n sequence |
| mode = PARSE_MODE_IGNORE; // Ignore whitespace on the next line |
| continue; |
| } |
| // Intentional fall into the next case |
| case '\r': |
| mode = PARSE_MODE_NONE; |
| firstChar = true; |
| if (offset > 0 || (offset == 0 && keyLength == 0)) { |
| |
| if (keyLength == -1) { |
| keyLength = offset; |
| } |
| std::string temp(buf.begin(), buf.begin() + offset); |
| |
| this->internal->properties.put( |
| temp.substr(0, (std::size_t) keyLength), temp.substr((std::size_t) keyLength)); |
| } |
| |
| keyLength = -1; |
| offset = 0; |
| buf.clear(); |
| continue; |
| case '\\': |
| if (mode == PARSE_MODE_KEY_DONE) { |
| keyLength = offset; |
| } |
| mode = PARSE_MODE_SLASH; |
| continue; |
| case ':': |
| case '=': |
| if (keyLength == -1) { // if parsing the key |
| mode = PARSE_MODE_NONE; |
| keyLength = offset; |
| continue; |
| } |
| break; |
| } |
| |
| if (Character::isWhitespace(nextChar)) { |
| if (mode == PARSE_MODE_CONTINUE) { |
| mode = PARSE_MODE_IGNORE; |
| } |
| // if key length == 0 or value length == 0 |
| if (offset == 0 || offset == keyLength || mode == PARSE_MODE_IGNORE) { |
| continue; |
| } |
| if (keyLength == -1) { // if parsing the key |
| mode = PARSE_MODE_KEY_DONE; |
| continue; |
| } |
| } |
| |
| if (mode == PARSE_MODE_IGNORE || mode == PARSE_MODE_CONTINUE) { |
| mode = PARSE_MODE_NONE; |
| } |
| } |
| |
| firstChar = false; |
| if (mode == PARSE_MODE_KEY_DONE) { |
| keyLength = offset; |
| mode = PARSE_MODE_NONE; |
| } |
| |
| offset += 1; |
| buf.push_back(nextChar); |
| } |
| |
| if (keyLength == -1 && offset > 0) { |
| keyLength = offset; |
| } |
| |
| if (keyLength >= 0) { |
| std::string temp(buf.begin(), buf.begin() + offset); |
| this->internal->properties.put( |
| temp.substr(0, (std::size_t) keyLength), temp.substr((std::size_t) keyLength)); |
| } |
| } |
| DECAF_CATCH_RETHROW(IOException) |
| DECAF_CATCH_RETHROW(IllegalArgumentException) |
| DECAF_CATCH_RETHROW(NullPointerException) |
| DECAF_CATCH_EXCEPTION_CONVERT(Exception, IOException) |
| DECAF_CATCHALL_THROW(IOException) |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void Properties::load(decaf::io::Reader* reader) { |
| |
| try { |
| |
| if (reader == NULL) { |
| throw NullPointerException( |
| __FILE__, __LINE__, "The Reader instance passed was Null"); |
| } |
| |
| throw UnsupportedOperationException( |
| __FILE__, __LINE__, "Not yet Implemented."); |
| } |
| DECAF_CATCH_RETHROW(IOException) |
| DECAF_CATCH_RETHROW(IllegalArgumentException) |
| DECAF_CATCH_RETHROW(NullPointerException) |
| DECAF_CATCH_EXCEPTION_CONVERT(Exception, IOException) |
| DECAF_CATCHALL_THROW(IOException) |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void Properties::store(decaf::io::OutputStream* out, const std::string& comments DECAF_UNUSED) { |
| |
| try { |
| |
| if (out == NULL) { |
| throw NullPointerException( |
| __FILE__, __LINE__, "The OutputStream instance passed was Null"); |
| } |
| |
| std::ostringstream buffer; |
| std::ostringstream writer; |
| |
| if (comments != "") { |
| writer << "#"; |
| writer << comments; |
| writer << std::endl; |
| } |
| |
| writer << "#"; |
| writer << Date().toString(); |
| writer << std::endl; |
| |
| Pointer<Iterator<MapEntry<std::string, std::string> > > entries( |
| this->internal->properties.entrySet().iterator()); |
| while (entries->hasNext()) { |
| MapEntry<std::string, std::string> entry = entries->next(); |
| |
| dumpString(buffer, entry.getKey(), true); |
| buffer << "="; |
| dumpString(buffer, entry.getValue(), false); |
| buffer << std::endl; |
| |
| writer << buffer.str(); |
| buffer.str(""); |
| } |
| |
| int length = (int) writer.str().length(); |
| |
| out->write((const unsigned char*) writer.str().c_str(), length, 0, length); |
| out->flush(); |
| } |
| DECAF_CATCH_RETHROW(IOException) |
| DECAF_CATCH_RETHROW(NullPointerException) |
| DECAF_CATCH_EXCEPTION_CONVERT(Exception, IOException) |
| DECAF_CATCHALL_THROW(IOException) |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| void Properties::store(decaf::io::Writer* writer, const std::string& comments DECAF_UNUSED) { |
| |
| try { |
| |
| if (writer == NULL) { |
| throw NullPointerException( |
| __FILE__, __LINE__, "The Writer instance passed was Null"); |
| } |
| |
| throw UnsupportedOperationException( |
| __FILE__, __LINE__, "Not yet Implemented."); |
| } |
| DECAF_CATCH_RETHROW(IOException) |
| DECAF_CATCH_RETHROW(NullPointerException) |
| DECAF_CATCH_EXCEPTION_CONVERT(Exception, IOException) |
| DECAF_CATCHALL_THROW(IOException) |
| } |
| |