xref: /aosp_15_r20/external/image_io/src/xml/xml_element_rules.cc (revision ca0779eb572efbbfda2e47f806647c3c7eeea8c3)
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