1// Copyright (C) 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3/********************************************************************
4 * COPYRIGHT:
5 * Copyright (c) 1997-2012, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 * Copyright (C) 2010 , Yahoo! Inc.
8 ********************************************************************
9 *
10 * File SELFMT.CPP
11 *
12 * Modification History:
13 *
14 *   Date        Name        Description
15 *   11/11/09    kirtig      Finished first cut of implementation.
16 *   11/16/09    kirtig      Improved version
17 ********************************************************************/
18
19#include "utypeinfo.h"  // for 'typeid' to work
20
21#include "unicode/messagepattern.h"
22#include "unicode/rbnf.h"
23#include "unicode/selfmt.h"
24#include "unicode/uchar.h"
25#include "unicode/ucnv_err.h"
26#include "unicode/umsg.h"
27#include "unicode/ustring.h"
28#include "unicode/utypes.h"
29#include "cmemory.h"
30#include "messageimpl.h"
31#include "patternprops.h"
32#include "selfmtimpl.h"
33#include "uassert.h"
34#include "ustrfmt.h"
35#include "util.h"
36#include "uvector.h"
37
38#if !UCONFIG_NO_FORMATTING
39
40U_NAMESPACE_BEGIN
41
42UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SelectFormat)
43
44static const UChar SELECT_KEYWORD_OTHER[] = {LOW_O, LOW_T, LOW_H, LOW_E, LOW_R, 0};
45
46SelectFormat::SelectFormat(const UnicodeString& pat,
47                           UErrorCode& status) : msgPattern(status) {
48   applyPattern(pat, status);
49}
50
51SelectFormat::SelectFormat(const SelectFormat& other) : Format(other),
52                                                        msgPattern(other.msgPattern) {
53}
54
55SelectFormat::~SelectFormat() {
56}
57
58void
59SelectFormat::applyPattern(const UnicodeString& newPattern, UErrorCode& status) {
60    if (U_FAILURE(status)) {
61      return;
62    }
63
64    msgPattern.parseSelectStyle(newPattern, NULL, status);
65    if (U_FAILURE(status)) {
66        msgPattern.clear();
67    }
68}
69
70UnicodeString&
71SelectFormat::format(const Formattable& obj,
72                   UnicodeString& appendTo,
73                   FieldPosition& pos,
74                   UErrorCode& status) const
75{
76    if (U_FAILURE(status)) {
77        return appendTo;
78    }
79    if (obj.getType() == Formattable::kString) {
80        return format(obj.getString(status), appendTo, pos, status);
81    } else {
82        status = U_ILLEGAL_ARGUMENT_ERROR;
83        return appendTo;
84    }
85}
86
87UnicodeString&
88SelectFormat::format(const UnicodeString& keyword,
89                     UnicodeString& appendTo,
90                     FieldPosition& /*pos */,
91                     UErrorCode& status) const {
92    if (U_FAILURE(status)) {
93        return appendTo;
94    }
95    // Check for the validity of the keyword
96    if (!PatternProps::isIdentifier(keyword.getBuffer(), keyword.length())) {
97        status = U_ILLEGAL_ARGUMENT_ERROR;  // Invalid formatting argument.
98    }
99    if (msgPattern.countParts() == 0) {
100        status = U_INVALID_STATE_ERROR;
101        return appendTo;
102    }
103    int32_t msgStart = findSubMessage(msgPattern, 0, keyword, status);
104    if (!MessageImpl::jdkAposMode(msgPattern)) {
105        int32_t patternStart = msgPattern.getPart(msgStart).getLimit();
106        int32_t msgLimit = msgPattern.getLimitPartIndex(msgStart);
107        appendTo.append(msgPattern.getPatternString(),
108                        patternStart,
109                        msgPattern.getPatternIndex(msgLimit) - patternStart);
110        return appendTo;
111    }
112    // JDK compatibility mode: Remove SKIP_SYNTAX.
113    return MessageImpl::appendSubMessageWithoutSkipSyntax(msgPattern, msgStart, appendTo);
114}
115
116UnicodeString&
117SelectFormat::toPattern(UnicodeString& appendTo) {
118    if (0 == msgPattern.countParts()) {
119        appendTo.setToBogus();
120    } else {
121        appendTo.append(msgPattern.getPatternString());
122    }
123    return appendTo;
124}
125
126
127int32_t SelectFormat::findSubMessage(const MessagePattern& pattern, int32_t partIndex,
128                                     const UnicodeString& keyword, UErrorCode& ec) {
129    if (U_FAILURE(ec)) {
130        return 0;
131    }
132    UnicodeString other(FALSE, SELECT_KEYWORD_OTHER, 5);
133    int32_t count = pattern.countParts();
134    int32_t msgStart=0;
135    // Iterate over (ARG_SELECTOR, message) pairs until ARG_LIMIT or end of select-only pattern.
136    do {
137        const MessagePattern::Part& part=pattern.getPart(partIndex++);
138        const UMessagePatternPartType type=part.getType();
139        if(type==UMSGPAT_PART_TYPE_ARG_LIMIT) {
140            break;
141        }
142        // part is an ARG_SELECTOR followed by a message
143        if(pattern.partSubstringMatches(part, keyword)) {
144            // keyword matches
145            return partIndex;
146        } else if(msgStart==0 && pattern.partSubstringMatches(part, other)) {
147            msgStart=partIndex;
148        }
149        partIndex=pattern.getLimitPartIndex(partIndex);
150    } while(++partIndex<count);
151    return msgStart;
152}
153
154Format* SelectFormat::clone() const
155{
156    return new SelectFormat(*this);
157}
158
159SelectFormat&
160SelectFormat::operator=(const SelectFormat& other) {
161    if (this != &other) {
162        msgPattern = other.msgPattern;
163    }
164    return *this;
165}
166
167UBool
168SelectFormat::operator==(const Format& other) const {
169    if (this == &other) {
170        return TRUE;
171    }
172    if (!Format::operator==(other)) {
173        return FALSE;
174    }
175    const SelectFormat& o = (const SelectFormat&)other;
176    return msgPattern == o.msgPattern;
177}
178
179UBool
180SelectFormat::operator!=(const Format& other) const {
181    return  !operator==(other);
182}
183
184void
185SelectFormat::parseObject(const UnicodeString& /*source*/,
186                        Formattable& /*result*/,
187                        ParsePosition& pos) const
188{
189    // Parsing not supported.
190    pos.setErrorIndex(pos.getIndex());
191}
192
193U_NAMESPACE_END
194
195#endif /* #if !UCONFIG_NO_FORMATTING */
196
197//eof
198