1 #include "image_io/xml/xml_element_rules.h"
2
3 #include <utility>
4
5 #include "image_io/xml/xml_attribute_rule.h"
6 #include "image_io/xml/xml_cdata_and_comment_rules.h"
7 #include "image_io/xml/xml_handler.h"
8 #include "image_io/xml/xml_pi_rule.h"
9 #include "image_io/xml/xml_token_context.h"
10
11 namespace photos_editing_formats {
12 namespace image_io {
13
14 namespace {
15
16 /// Some names of terminals used by these rules.
17 const char kWhitespace[] = "Whitespace";
18 const char kEmptyElementEnd[] = "EmptyElementEnd";
19 const char kElementEnd[] = "ElementEnd";
20 const char kElementSentinalDescription[] =
21 "The start of an attribute name or the end of the element ('>' or '/>')";
22
23 /// A shortcut for referring to all XmlPortion bits.
24 const XmlPortion kAllPortions =
25 XmlPortion::kBegin | XmlPortion::kMiddle | XmlPortion::kEnd;
26
27 /// @param context The action context passed to an action handler.
28 /// @param token_range The token range to use when building the token context.
29 /// @param portion The token portion to use when building the token context.
30 /// @param A token context for use in calling an XmlHandler function.
GetTokenContext(const XmlActionContext & context,const DataRange & token_range,XmlPortion portion)31 XmlTokenContext GetTokenContext(const XmlActionContext& context,
32 const DataRange& token_range,
33 XmlPortion portion) {
34 return XmlTokenContext(context.GetLocation(), context.GetRange(),
35 context.GetSegment(), context.GetDataLineMap(),
36 context.GetResult(), token_range, portion);
37 }
38
39 } // namespace
40
XmlElementRule()41 XmlElementRule::XmlElementRule() : XmlElementRule(kFirstStartPoint) {}
42
XmlElementRule(XmlRule::StartPoint start_point)43 XmlElementRule::XmlElementRule(XmlRule::StartPoint start_point)
44 : XmlRule("Element") {
45 AddLiteralTerminal("<");
46 AddNameTerminal().WithAction(
47 [&](const XmlActionContext& context) { return HandleName(context); });
48 AddOptionalWhitespaceTerminal().WithName(kWhitespace);
49 AddSentinelTerminal("~/>")
50 .WithDescription(kElementSentinalDescription)
51 .WithAction([&](const XmlActionContext& context) {
52 return HandlePostWhitespaceChar(context);
53 });
54 AddLiteralTerminal("/>")
55 .WithName(kEmptyElementEnd)
56 .WithAction([&](const XmlActionContext& context) {
57 return HandleEmptyElemTagEnd(context);
58 });
59 AddLiteralTerminal(">")
60 .WithName(kElementEnd)
61 .WithAction([&](const XmlActionContext& context) {
62 return HandleSTagEnd(context);
63 });
64 if (start_point == kSecondStartPoint) {
65 SetTerminalIndex(1);
66 }
67 }
68
HandleName(const XmlActionContext & context)69 DataMatchResult XmlElementRule::HandleName(const XmlActionContext& context) {
70 XmlTokenContext token_context(context);
71 return context.GetHandler()->StartElement(token_context);
72 }
73
HandlePostWhitespaceChar(const XmlActionContext & context)74 DataMatchResult XmlElementRule::HandlePostWhitespaceChar(
75 const XmlActionContext& context) {
76 DataMatchResult result = context.GetResultWithBytesConsumed(0);
77 char sentinel = context.GetTerminal()->GetScanner()->GetSentinel();
78 if (sentinel == '/') {
79 size_t index = GetTerminalIndexFromName(kEmptyElementEnd);
80 SetTerminalIndex(index);
81 } else if (sentinel == '>') {
82 size_t index = GetTerminalIndexFromName(kElementEnd);
83 SetTerminalIndex(index);
84 } else if (sentinel == '~') {
85 std::unique_ptr<XmlRule> rule(new XmlAttributeRule);
86 SetNextRule(std::move(rule));
87 ResetTerminalScanners();
88 size_t index = GetTerminalIndexFromName(kWhitespace);
89 SetTerminalIndex(index);
90 result.SetType(DataMatchResult::kPartial);
91 }
92 return result;
93 }
94
HandleEmptyElemTagEnd(const XmlActionContext & context)95 DataMatchResult XmlElementRule::HandleEmptyElemTagEnd(
96 const XmlActionContext& context) {
97 SetTerminalIndex(GetTerminalCount());
98 return context.GetHandler()->FinishElement(
99 GetTokenContext(context, DataRange(), XmlPortion::kNone));
100 }
101
HandleSTagEnd(const XmlActionContext & context)102 DataMatchResult XmlElementRule::HandleSTagEnd(const XmlActionContext& context) {
103 DataMatchResult result = context.GetResult();
104 std::unique_ptr<XmlRule> rule(new XmlElementContentRule);
105 SetNextRule(std::move(rule));
106 return result;
107 }
108
XmlElementContentRule()109 XmlElementContentRule::XmlElementContentRule() : XmlRule("ElementContent") {
110 // ElementContent until
111 // <N... Element
112 // <?N ... ?> PI
113 // <!-- ... --> Comment
114 // <![CDATA[ ... ]]> CDATA
115 // </Nws> Element Etag
116 // &...; EntityRef or CharRef (Don't care about this)
117 AddThroughLiteralTerminal("<").WithAction(
118 [&](const XmlActionContext& context) { return HandleContent(context); });
119 AddSentinelTerminal("~?!/").WithAction([&](const XmlActionContext& context) {
120 return HandlePostOpenChar(context);
121 });
122 AddNameTerminal().WithAction(
123 [&](const XmlActionContext& context) { return HandleEndTag(context); });
124 AddLiteralTerminal(">");
125 }
126
HandleContent(const XmlActionContext & context)127 DataMatchResult XmlElementContentRule::HandleContent(
128 const XmlActionContext& context) {
129 const auto& range = context.GetTerminal()->GetScanner()->GetTokenRange();
130 if (range.IsValid()) {
131 size_t end = context.GetResult().GetType() == DataMatchResult::kFull
132 ? range.GetEnd() - 1
133 : range.GetEnd();
134 DataRange token_range(range.GetBegin(), end);
135 if (token_range.GetLength() > 0) {
136 XmlTokenContext token_context =
137 GetTokenContext(context, token_range, kAllPortions);
138 DataMatchResult result =
139 context.GetHandler()->ElementContent(token_context);
140 context.GetTerminal()->GetScanner()->ResetTokenRange();
141 return result;
142 }
143 }
144 context.GetTerminal()->GetScanner()->ResetTokenRange();
145 return context.GetResult();
146 }
147
HandlePostOpenChar(const XmlActionContext & context)148 DataMatchResult XmlElementContentRule::HandlePostOpenChar(
149 const XmlActionContext& context) {
150 DataMatchResult result = context.GetResult();
151 char sentinel = context.GetTerminal()->GetScanner()->GetSentinel();
152 if (sentinel == '~') {
153 result.SetBytesConsumed(0);
154 result.SetType(DataMatchResult::kPartial);
155 std::unique_ptr<XmlRule> rule(new XmlElementRule(kSecondStartPoint));
156 SetNextRule(std::move(rule));
157 } else if (sentinel == '?') {
158 result.SetType(DataMatchResult::kPartial);
159 std::unique_ptr<XmlRule> rule(new XmlPiRule(kSecondStartPoint));
160 SetNextRule(std::move(rule));
161 } else if (sentinel == '!') {
162 result.SetType(DataMatchResult::kPartial);
163 std::unique_ptr<XmlRule> rule(new XmlCdataOrCommentRule(kSecondStartPoint));
164 SetNextRule(std::move(rule));
165 } else if (sentinel == '/') {
166 // Do nothing so that the next terminals (the 'name>' part of '</name>')
167 // will be activated and scanned.
168 return context.GetResult();
169 }
170 ResetTerminalScanners();
171 SetTerminalIndex(0);
172 return result;
173 }
174
HandleEndTag(const XmlActionContext & context)175 DataMatchResult XmlElementContentRule::HandleEndTag(
176 const XmlActionContext& context) {
177 XmlTokenContext token_context(context);
178 return context.GetHandler()->FinishElement(token_context);
179 }
180
181 } // namespace image_io
182 } // namespace photos_editing_formats
183