1/*
2 * Copyright (C) 2004, 2005, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006 Rob Buis <buis@kde.org>
4 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB.  If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22#include "config.h"
23#include "core/svg/SVGAngle.h"
24
25#include "bindings/v8/ExceptionState.h"
26#include "core/dom/ExceptionCode.h"
27#include "core/svg/SVGParserUtilities.h"
28#include "wtf/MathExtras.h"
29#include "wtf/text/WTFString.h"
30
31namespace WebCore {
32
33SVGAngle::SVGAngle()
34    : m_unitType(SVG_ANGLETYPE_UNSPECIFIED)
35    , m_valueInSpecifiedUnits(0)
36{
37}
38
39float SVGAngle::value() const
40{
41    switch (m_unitType) {
42    case SVG_ANGLETYPE_GRAD:
43        return grad2deg(m_valueInSpecifiedUnits);
44    case SVG_ANGLETYPE_RAD:
45        return rad2deg(m_valueInSpecifiedUnits);
46    case SVG_ANGLETYPE_UNSPECIFIED:
47    case SVG_ANGLETYPE_UNKNOWN:
48    case SVG_ANGLETYPE_DEG:
49        return m_valueInSpecifiedUnits;
50    }
51
52    ASSERT_NOT_REACHED();
53    return 0;
54}
55
56void SVGAngle::setValue(float value)
57{
58    switch (m_unitType) {
59    case SVG_ANGLETYPE_GRAD:
60        m_valueInSpecifiedUnits = deg2grad(value);
61        break;
62    case SVG_ANGLETYPE_RAD:
63        m_valueInSpecifiedUnits = deg2rad(value);
64        break;
65    case SVG_ANGLETYPE_UNSPECIFIED:
66    case SVG_ANGLETYPE_UNKNOWN:
67    case SVG_ANGLETYPE_DEG:
68        m_valueInSpecifiedUnits = value;
69        break;
70    }
71}
72
73template<typename CharType>
74static SVGAngle::SVGAngleType stringToAngleType(const CharType*& ptr, const CharType* end)
75{
76    // If there's no unit given, the angle type is unspecified.
77    if (ptr == end)
78        return SVGAngle::SVG_ANGLETYPE_UNSPECIFIED;
79
80    const CharType firstChar = *ptr;
81
82    // If the unit contains only one character, the angle type is unknown.
83    ++ptr;
84    if (ptr == end)
85        return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
86
87    const CharType secondChar = *ptr;
88
89    // If the unit contains only two characters, the angle type is unknown.
90    ++ptr;
91    if (ptr == end)
92        return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
93
94    const CharType thirdChar = *ptr;
95    if (firstChar == 'd' && secondChar == 'e' && thirdChar == 'g')
96        return SVGAngle::SVG_ANGLETYPE_DEG;
97    if (firstChar == 'r' && secondChar == 'a' && thirdChar == 'd')
98        return SVGAngle::SVG_ANGLETYPE_RAD;
99
100    // If the unit contains three characters, but is not deg or rad, then it's unknown.
101    ++ptr;
102    if (ptr == end)
103        return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
104
105    const CharType fourthChar = *ptr;
106
107    if (firstChar == 'g' && secondChar == 'r' && thirdChar == 'a' && fourthChar == 'd')
108        return SVGAngle::SVG_ANGLETYPE_GRAD;
109
110    return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
111}
112
113String SVGAngle::valueAsString() const
114{
115    switch (m_unitType) {
116    case SVG_ANGLETYPE_DEG: {
117        DEFINE_STATIC_LOCAL(String, degString, ("deg"));
118        return String::number(m_valueInSpecifiedUnits) + degString;
119    }
120    case SVG_ANGLETYPE_RAD: {
121        DEFINE_STATIC_LOCAL(String, radString, ("rad"));
122        return String::number(m_valueInSpecifiedUnits) + radString;
123    }
124    case SVG_ANGLETYPE_GRAD: {
125        DEFINE_STATIC_LOCAL(String, gradString, ("grad"));
126        return String::number(m_valueInSpecifiedUnits) + gradString;
127    }
128    case SVG_ANGLETYPE_UNSPECIFIED:
129    case SVG_ANGLETYPE_UNKNOWN:
130        return String::number(m_valueInSpecifiedUnits);
131    }
132
133    ASSERT_NOT_REACHED();
134    return String();
135}
136
137template<typename CharType>
138static bool parseValue(const String& value, float& valueInSpecifiedUnits, SVGAngle::SVGAngleType& unitType)
139{
140    const CharType* ptr = value.getCharacters<CharType>();
141    const CharType* end = ptr + value.length();
142
143    if (!parseNumber(ptr, end, valueInSpecifiedUnits, false))
144        return false;
145
146    unitType = stringToAngleType(ptr, end);
147    if (unitType == SVGAngle::SVG_ANGLETYPE_UNKNOWN)
148        return false;
149
150    return true;
151}
152
153void SVGAngle::setValueAsString(const String& value, ExceptionState& exceptionState)
154{
155    if (value.isEmpty()) {
156        m_unitType = SVG_ANGLETYPE_UNSPECIFIED;
157        return;
158    }
159
160    float valueInSpecifiedUnits = 0;
161    SVGAngleType unitType = SVG_ANGLETYPE_UNKNOWN;
162
163    bool success = value.is8Bit() ? parseValue<LChar>(value, valueInSpecifiedUnits, unitType)
164                                  : parseValue<UChar>(value, valueInSpecifiedUnits, unitType);
165    if (!success) {
166        exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
167        return;
168    }
169
170    m_unitType = unitType;
171    m_valueInSpecifiedUnits = valueInSpecifiedUnits;
172}
173
174void SVGAngle::newValueSpecifiedUnits(unsigned short unitType, float valueInSpecifiedUnits, ExceptionState& exceptionState)
175{
176    if (unitType == SVG_ANGLETYPE_UNKNOWN || unitType > SVG_ANGLETYPE_GRAD) {
177        exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError);
178        return;
179    }
180
181    if (unitType != m_unitType)
182        m_unitType = static_cast<SVGAngleType>(unitType);
183
184    m_valueInSpecifiedUnits = valueInSpecifiedUnits;
185}
186
187void SVGAngle::convertToSpecifiedUnits(unsigned short unitType, ExceptionState& exceptionState)
188{
189    if (unitType == SVG_ANGLETYPE_UNKNOWN || m_unitType == SVG_ANGLETYPE_UNKNOWN || unitType > SVG_ANGLETYPE_GRAD) {
190        exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError);
191        return;
192    }
193
194    if (unitType == m_unitType)
195        return;
196
197    switch (m_unitType) {
198    case SVG_ANGLETYPE_RAD:
199        switch (unitType) {
200        case SVG_ANGLETYPE_GRAD:
201            m_valueInSpecifiedUnits = rad2grad(m_valueInSpecifiedUnits);
202            break;
203        case SVG_ANGLETYPE_UNSPECIFIED:
204        case SVG_ANGLETYPE_DEG:
205            m_valueInSpecifiedUnits = rad2deg(m_valueInSpecifiedUnits);
206            break;
207        case SVG_ANGLETYPE_RAD:
208        case SVG_ANGLETYPE_UNKNOWN:
209            ASSERT_NOT_REACHED();
210            break;
211        }
212        break;
213    case SVG_ANGLETYPE_GRAD:
214        switch (unitType) {
215        case SVG_ANGLETYPE_RAD:
216            m_valueInSpecifiedUnits = grad2rad(m_valueInSpecifiedUnits);
217            break;
218        case SVG_ANGLETYPE_UNSPECIFIED:
219        case SVG_ANGLETYPE_DEG:
220            m_valueInSpecifiedUnits = grad2deg(m_valueInSpecifiedUnits);
221            break;
222        case SVG_ANGLETYPE_GRAD:
223        case SVG_ANGLETYPE_UNKNOWN:
224            ASSERT_NOT_REACHED();
225            break;
226        }
227        break;
228    case SVG_ANGLETYPE_UNSPECIFIED:
229        // Spec: For angles, a unitless value is treated the same as if degrees were specified.
230    case SVG_ANGLETYPE_DEG:
231        switch (unitType) {
232        case SVG_ANGLETYPE_RAD:
233            m_valueInSpecifiedUnits = deg2rad(m_valueInSpecifiedUnits);
234            break;
235        case SVG_ANGLETYPE_GRAD:
236            m_valueInSpecifiedUnits = deg2grad(m_valueInSpecifiedUnits);
237            break;
238        case SVG_ANGLETYPE_UNSPECIFIED:
239            break;
240        case SVG_ANGLETYPE_DEG:
241        case SVG_ANGLETYPE_UNKNOWN:
242            ASSERT_NOT_REACHED();
243            break;
244        }
245        break;
246    case SVG_ANGLETYPE_UNKNOWN:
247        ASSERT_NOT_REACHED();
248        break;
249    }
250
251    m_unitType = static_cast<SVGAngleType>(unitType);
252}
253
254}
255