OW_SessionLanguage.cpp

Go to the documentation of this file.
00001 /*******************************************************************************
00002 * Copyright (C) 2001-2004 Novell, Inc. All rights reserved.
00003 *
00004 * Redistribution and use in source and binary forms, with or without
00005 * modification, are permitted provided that the following conditions are met:
00006 *
00007 *  - Redistributions of source code must retain the above copyright notice,
00008 *    this list of conditions and the following disclaimer.
00009 *
00010 *  - Redistributions in binary form must reproduce the above copyright notice,
00011 *    this list of conditions and the following disclaimer in the documentation
00012 *    and/or other materials provided with the distribution.
00013 *
00014 *  - Neither the name of Novell, Inc. nor the names of its
00015 *    contributors may be used to endorse or promote products derived from this
00016 *    software without specific prior written permission.
00017 *
00018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
00019 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00020 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00021 * ARE DISCLAIMED. IN NO EVENT SHALL Novell, Inc. OR THE CONTRIBUTORS
00022 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00023 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00024 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00025 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00026 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00027 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00028 * POSSIBILITY OF SUCH DAMAGE.
00029 *******************************************************************************/
00030 
00034 #include "OW_config.h"
00035 #include "OW_SessionLanguage.hpp"
00036 
00037 #include <cstdlib>
00038 #include <cstring>
00039 #include <cctype>
00040 #include <cerrno>
00041 #include <algorithm> // for sort
00042 
00043 namespace OW_NAMESPACE
00044 {
00045 
00046 namespace
00047 {
00048 
00049 inline const char* 
00050 skipWhite(const char* arg)
00051 {
00052    while (*arg && isspace(*arg)) { arg++; }
00053    return arg;
00054 }
00055 
00056 bool
00057 subtagsMatch(const char* arg1, const char* arg2)
00058 {
00059    // If either arg is a wildcard, return match
00060    if (*arg1 == '*' || *arg2 == '*')
00061    {
00062       return true;
00063    }
00064 
00065    // Assume lower case in both args
00066    return (::strcmp(arg1, arg2) == 0);
00067 }
00068 
00069 }  // End of unnamed namespace
00070 
00072 LanguageTag::LanguageTag()
00073 {
00074    m_subtag1[0] = 0;
00075    m_subtag2[0] = 0;
00076    m_subtag3[0] = 0;
00077    m_weight = 0;
00078    m_explicitQualityValue = false;
00079 }
00080 
00082 LanguageTag::LanguageTag(const char* languageTag)
00083 {
00084    assign(languageTag);
00085 }
00086 
00088 LanguageTag::LanguageTag(const LanguageTag& arg)
00089 {
00090    copy(arg);
00091 }
00092 
00094 LanguageTag& 
00095 LanguageTag::operator= (const LanguageTag& arg)
00096 {
00097    return copy(arg);
00098 }
00099 
00101 LanguageTag&
00102 LanguageTag::operator= (const char* arg)
00103 {
00104    assign(arg);
00105    return *this;
00106 }
00107 
00109 const char*
00110 LanguageTag::assign(const char* arg)
00111 {
00112    m_weight = 0;
00113    m_explicitQualityValue = false;
00114     return setSubTags(arg);
00115 }
00116 
00118 LanguageTag& 
00119 LanguageTag::copy(const LanguageTag& arg)
00120 {
00121    ::memcpy(m_subtag1, arg.m_subtag1, sizeof(m_subtag1));
00122    ::memcpy(m_subtag2, arg.m_subtag2, sizeof(m_subtag2));
00123    ::memcpy(m_subtag3, arg.m_subtag3, sizeof(m_subtag3));
00124    m_weight = arg.m_weight;
00125    m_explicitQualityValue = arg.m_explicitQualityValue;
00126    return *this;
00127 }
00128 
00130 int 
00131 LanguageTag::compareWeight(const LanguageTag& arg) const
00132 {
00133    int v = m_weight - arg.m_weight;
00134    if (v == 0)
00135    {
00136       if (m_explicitQualityValue != arg.m_explicitQualityValue)
00137       {
00138          v = (arg.m_explicitQualityValue) ? -1 : 1;
00139       }
00140    }
00141 
00142    return v;
00143 }
00144 
00146 String 
00147 LanguageTag::toString() const
00148 {
00149    char tmpBuf[sizeof(m_subtag1)+sizeof(m_subtag2)+sizeof(m_subtag3)+10];
00150    tmpBuf[0] = 0;
00151    if (!invalid())
00152    {
00153       ::strcpy(tmpBuf, m_subtag1);
00154       if (m_subtag2[0])
00155       {
00156          ::strcat(tmpBuf, "-");
00157          ::strcat(tmpBuf, m_subtag2);
00158          if (m_subtag3[0])
00159          {
00160             ::strcat(tmpBuf, "-");
00161             ::strcat(tmpBuf, m_subtag3);
00162          }
00163       }
00164    }
00165 
00166    return String(tmpBuf);
00167 }
00168 
00170 const char*
00171 LanguageTag::setSubTags(const char* languageTag)
00172 {
00173    // Parse section of string of the format: "en-GB ; q = 0.8 "
00174 
00175    m_subtag1[0] = 0;
00176    m_subtag2[0] = 0;
00177    m_subtag3[0] = 0;
00178    m_weight = 100;
00179    m_explicitQualityValue = false;
00180 
00181    const char* p = skipWhite(languageTag); // point to start of language tag
00182 
00183    if (!(p = parseSubTag(p, m_subtag1)))  // Get subtag1
00184       return 0;   // Invalid
00185 
00186    p = skipWhite(p);
00187    if (*p == ';') // Start of quality value for language tag?
00188       return setWeight(p); // End of this language tag
00189 
00190    if (*p != '-') // Start of subtag2?
00191       return p;   // Must be the end of the entire language tag
00192 
00193    // Must be starting subtag2
00194    ++p;  // Skip the '-' character
00195    if (!(p = parseSubTag(p, m_subtag2)))  // Get subtag2
00196       return p;   // Invalid
00197 
00198    p = skipWhite(p);
00199    if (*p == ';') // Start of quality value for language tag?
00200       return setWeight(p); // End of this language tag
00201 
00202    if (*p != '-') // Start of subtag3?
00203       return p;   // Must be the end of the entire language tag
00204 
00205    ++p;  // Skip the '-' character
00206    return parseSubTag(p, m_subtag3);   // Get subtag3
00207 }
00208 
00210 const char*
00211 LanguageTag::parseSubTag(const char* arg, char* tagField)
00212 {
00213    int i = 0;
00214    while (true)
00215    {
00216       if (!isalpha(*arg) && *arg != '*')
00217       {
00218          if (*arg != '-'         // Start of next subtag?
00219             && *arg != ';'    // Start of quality value?
00220             && *arg != ','    // End of language tag
00221             && !isspace(*arg) // End of tag?
00222             && *arg != 0)     // End of string?
00223          {
00224             // Invalid char in subtag
00225             m_subtag1[0] = 0;
00226             arg = 0;
00227          }
00228          break;
00229       }
00230 
00231       if (i == 8)
00232       {
00233          // subtag to long
00234          m_subtag1[0] = 0;
00235          arg = 0;
00236          break;
00237       }
00238 
00239       tagField[i++] = tolower(*arg);
00240       tagField[i] = 0;
00241       arg++;
00242    }
00243 
00244    return arg;
00245 }
00246 
00248 const char*
00249 LanguageTag::setWeight(const char* arg)
00250 {
00251    // Parse section of string of the format: " ; q = 0.8 "
00252 
00253    m_weight = 0;
00254    // Skip white space and the ';' character
00255    while (*arg && (*arg == ';' || isspace(*arg)))
00256    {
00257       ++arg;
00258    }
00259 
00260    // If we didn't encounter a 'q' or 'Q' then its invalid
00261    if (*arg != 'q' && *arg != 'Q')
00262    {
00263       m_subtag1[0] = 0;
00264       return 0;
00265    }
00266    arg++;      // Skip the 'q'/'Q'
00267    arg = skipWhite(arg);   // Eat the white space up to the '='
00268    if (*arg != '=')
00269    {
00270       m_subtag1[0] = 0;
00271       return 0;
00272    }
00273    arg++;               // Skip the '='
00274    arg = skipWhite(arg);   // Eat any white space after '='
00275    const char* p = arg; // Save start of numeric value
00276 
00277    if (!isdigit(*arg) && *arg != '.')
00278    {
00279       m_subtag1[0] = 0;
00280       return 0;
00281    }
00282 
00283    while (isdigit(*arg))   // Look for decimal point or end of number
00284       ++arg;
00285 
00286    if (*arg == '.')     // If we hit the decimal then keep going
00287    {
00288       do
00289       {
00290          ++arg;
00291       } while (isdigit(*arg));
00292    }
00293 
00294    errno = 0;
00295    double fv = strtod(p, 0);
00296    if (errno == ERANGE)
00297    {
00298       m_subtag1[0] = 0;
00299       arg = 0;
00300    }
00301    else
00302    {
00303       fv *= 100.0;
00304       m_weight = static_cast<Int32>(fv);
00305    }
00306    m_explicitQualityValue = true;
00307    return arg;
00308 }
00309 
00311 SessionLanguage::SessionLanguage()
00312    : OperationContext::Data()
00313    , m_langTags()
00314    , m_contentLanguage()
00315    , m_acceptLanguageString()
00316 {
00317 }
00318 
00320 SessionLanguage::SessionLanguage(const char* acceptLangHdrValue)
00321    : OperationContext::Data()
00322    , m_langTags()
00323    , m_contentLanguage()
00324    , m_acceptLanguageString()
00325 {
00326     assign(acceptLangHdrValue);
00327 }
00328 
00330 SessionLanguage::SessionLanguage(const SessionLanguage& arg)
00331    : OperationContext::Data()
00332    , m_langTags(arg.m_langTags)
00333    , m_contentLanguage(arg.m_contentLanguage)
00334    , m_acceptLanguageString(arg.m_acceptLanguageString)
00335 {
00336 }
00337 
00339 SessionLanguage&
00340 SessionLanguage::operator=(const SessionLanguage& arg)
00341 {
00342    m_langTags = arg.m_langTags;
00343    m_contentLanguage = arg.m_contentLanguage;
00344    m_acceptLanguageString = arg.m_acceptLanguageString;
00345    return *this;
00346 }
00347 
00349 SessionLanguage&
00350 SessionLanguage::assign(const char* acceptLangHdrValue)
00351 {
00352    buildLangTags(acceptLangHdrValue);
00353    return *this;
00354 }
00355 
00357 void 
00358 SessionLanguage::buildLangTags(const char* acceptLangHdrValue)
00359 {
00360    m_acceptLanguageString = acceptLangHdrValue;
00361    m_langTags.clear();
00362    const char* p = skipWhite(acceptLangHdrValue);
00363    if (!(*p))
00364    {
00365       return;
00366    }
00367 
00368    LanguageTag ltag;
00369    while (*p)
00370    {
00371       p = ltag.assign(p);
00372       if (!p)
00373       {
00374          break;
00375       }
00376       m_langTags.append(ltag);
00377       if (!*p)
00378       {
00379          break;
00380       }
00381       ++p;
00382    }
00383 
00384    if (!p)
00385    {
00386       m_langTags.clear();
00387    }
00388    else
00389    {
00390       std::sort(m_langTags.begin(), m_langTags.end(), 
00391          std::greater<LanguageTag>());
00392    }
00393 }
00394 
00396 // STATIC
00397 bool 
00398 SessionLanguage::langsMatch(const LanguageTag& t1, const LanguageTag& t2,
00399    int level)
00400 {
00401    bool matches = subtagsMatch(t1.m_subtag1, t2.m_subtag1);
00402    if (level > 1 && matches)
00403    {
00404       matches = subtagsMatch(t1.m_subtag2, t2.m_subtag2);
00405    }
00406    if (level > 2 && matches)
00407    {
00408       matches = subtagsMatch(t1.m_subtag3, t2.m_subtag3);
00409    }
00410    return matches;
00411 }
00412 
00414 String 
00415 SessionLanguage::getBestLanguage(const StringArray& languages, const String& defaultLanguage) const
00416 {
00417    if (languages.size() == 0)
00418    {
00419       return defaultLanguage;
00420    }
00421 
00422    if (m_langTags.size() == 0)
00423    {
00424       return languages[0]; // Return default content language?
00425    }
00426 
00427    int bestIndex = 0;
00428    int bestWeight = -1;
00429 
00430    for (int level = 3; level > 0; level--)
00431    {
00432       // Try to find fully qualified match first (all subtags match)
00433       for (StringArray::size_type i = 0; i < languages.size(); i++)
00434       {
00435          LanguageTag lt(languages[i].c_str());
00436          for (LanguageTagArray::size_type j = 0; j < m_langTags.size(); j++)
00437          {
00438             if (langsMatch(m_langTags[j], lt, level))
00439             {
00440                if (m_langTags[j].getWeight() > 0)
00441                {
00442                   // If the quality value for this language is better
00443                   // than the one found previously, then save it for the
00444                   // return value
00445                   if (m_langTags[j].getWeight() > bestWeight)
00446                   {
00447                      bestWeight = m_langTags[j].getWeight();
00448                      bestIndex = i; // Index of languages parm
00449                   }
00450                   break;
00451                }
00452             }
00453          }
00454       }
00455    }  
00456 
00457    return (bestWeight > -1) ? languages[bestIndex] : defaultLanguage;
00458 }
00459 
00461 void
00462 SessionLanguage::addContentLanguage(const String& contentLanguage)
00463 {
00464    if (!m_contentLanguage.empty())
00465    {
00466       m_contentLanguage += ", ";
00467    }
00468 
00469    StringArray currentLangs = m_contentLanguage.tokenize(", ");
00470    if (std::find(currentLangs.begin(), currentLangs.end(), contentLanguage) == currentLangs.end())
00471    {
00472       m_contentLanguage += contentLanguage;
00473    }
00474 }
00475 
00477 String 
00478 SessionLanguage::getContentLanguage() const
00479 {
00480    return m_contentLanguage;
00481 }
00482 
00483 }  // end namespace OW_NAMESPACE
00484 

Generated on Thu Feb 9 08:48:10 2006 for openwbem by  doxygen 1.4.6