1 // Copyright 2010 Google Inc. All Rights Reserved.
3 package com
.google
.apphosting
.utils
.config
;
5 import net
.sourceforge
.yamlbeans
.YamlException
;
6 import net
.sourceforge
.yamlbeans
.YamlReader
;
9 import java
.io
.StringReader
;
10 import java
.util
.LinkedList
;
11 import java
.util
.List
;
14 * Class to parse index.yaml into a IndexesXml object.
17 public class IndexYamlReader
{
19 public static final String INDEX_DEFINITIONS_TAG
=
20 "!!python/object:google.appengine.datastore.datastore_index.IndexDefinitions";
21 public static final String INDEX_TAG
=
22 "!!python/object:google.appengine.datastore.datastore_index.Index";
23 public static final String PROPERTY_TAG
=
24 "!!python/object:google.appengine.datastore.datastore_index.Property";
27 * Wrapper around IndexesXml to make the JavaBeans properties match the YAML
30 public static class IndexYaml
{
33 * JavaBean wrapper for Index entries in IndexesXml.
35 public static class Index
{
37 protected boolean ancestor
;
38 public List
<Property
> properties
;
40 public void setAncestor(String ancestor
) {
41 this.ancestor
= YamlUtils
.parseBoolean(ancestor
);
44 public String
getAncestor() {
50 * JavaBean wrapper for IndexesXml properties.
52 public static class Property
{
54 private String direction
= "asc";
56 public void setDirection(String direction
) {
57 if ("desc".equals(direction
) || "asc".equals(direction
)) {
58 this.direction
= direction
;
60 throw new AppEngineConfigException(
61 "Invalid direction '" + direction
+ "': expected 'asc' or 'desc'.");
65 public String
getDirection() {
69 public boolean isAscending() {
70 return "asc".equals(direction
);
74 private List
<Index
> indexes
;
76 public List
<Index
> getIndexes() {
80 public void setIndexes(List
<Index
> indexes
) {
81 this.indexes
= indexes
;
84 public IndexesXml
toXml(IndexesXml xml
) {
85 if (indexes
== null) {
86 throw new AppEngineConfigException("Empty index configuration.");
89 xml
= new IndexesXml();
91 for (Index yamlIndex
: indexes
) {
92 if (yamlIndex
.kind
== null) {
93 throw new AppEngineConfigException("Index missing required element 'kind'");
95 IndexesXml
.Index xmlIndex
= xml
.addNewIndex(yamlIndex
.kind
, yamlIndex
.ancestor
);
96 if (yamlIndex
.properties
!= null) {
97 for (Property property
: yamlIndex
.properties
) {
98 if (property
.name
== null) {
99 throw new AppEngineConfigException("Property is missing required element 'name'.");
101 xmlIndex
.addNewProperty(property
.name
, property
.isAscending());
109 public static IndexesXml
parse(Reader yaml
, IndexesXml xml
) {
110 List
<IndexesXml
> list
;
112 list
= parseMultiple(yaml
, xml
);
113 } catch (YamlException ex
) {
114 throw new AppEngineConfigException(ex
.getMessage(), ex
);
116 if (0 == list
.size()) {
117 throw new AppEngineConfigException("Empty index configuration.");
119 if (list
.size() > 1) {
120 throw new AppEngineConfigException(
121 "yaml unexepectedly contains more than one document: " + list
.size());
127 * Parses the Yaml from {@code yaml} into a Yaml document and deserializes
128 * the document into an instance of {@link IndexesXml}.
129 * @param yaml A {@link String} from which to read the Yaml. This String is allowed to
130 * be in the style generated by the admin server, including the Python-specific tags.
131 * This method will safely ignore those tags.
132 * @return An instance of {@link IndexesXml}.
134 public static IndexesXml
parse(String yaml
) {
135 return parse(new StringReader(clean(yaml
)), null);
139 * Parses the Yaml from {@code yaml} into one or more documents, and
140 * deserializes the documents into one or more instances of {@link IndexesXml}
141 * which are returned in a {@link List}.
143 * @param yaml A {@link String} from which to read the Yyaml. This String is
144 * allowed to be in the style generated by the admin server, including
145 * the Python-specific tags. This method will safely ignore those tags.
146 * @return A {@link List} of {@link IndexesXml} instances representing one or
147 * more parsed Yaml documents.
149 public static List
<IndexesXml
> parseMultiple(String yaml
) {
151 return parseMultiple(new StringReader(clean(yaml
)), null);
152 } catch (YamlException ex
) {
153 throw new AppEngineConfigException(ex
.getMessage(), ex
);
158 * Cleans a Yaml String by removing Pyton-specific tags.
159 * These tags are written by the admin server when it generates
160 * Yaml representing indexes.
161 * @param yaml A {@link String} containing Yaml
162 * @return The cleaned {@link String}
164 private static String
clean(String yaml
) {
166 .replaceAll(INDEX_DEFINITIONS_TAG
, "")
167 .replaceAll(INDEX_TAG
, "")
168 .replaceAll(PROPERTY_TAG
, "")
173 * Parses the Yaml from {@code yaml} into one or more documents, and
174 * deserializes the documents into one or more instances of {@link IndexesXml}
175 * which are returned in a {@link List}.
177 * @param yaml A {@link Reader} from which to read the yaml
178 * @param xml A possibly {@code null} {@link IndexesXml} instance. If this
179 * parameter is not {@code null} then each of the yaml documents will
180 * be deserialized into it. This parameter is intended to be
181 * used in cases where there is only one Yaml document expected and so
182 * there will only be one instance of {@link IndexesXml} in the
183 * returned {@link List}. If this parameter is not {@code null} and the
184 * returned list has length greater than 1, then the list will contain
185 * multiple copies of this parameter.
186 * @return A {@link List} of {@link IndexesXml} instances representing one or
187 * more parsed Yaml documents.
188 * @throws YamlException If the Yaml parser has trobule parsing.
190 private static List
<IndexesXml
> parseMultiple(Reader yaml
, IndexesXml xml
) throws YamlException
{
191 YamlReader reader
= new YamlReader(yaml
);
192 reader
.getConfig().setPropertyElementType(IndexYaml
.class, "indexes", IndexYaml
.Index
.class);
193 reader
.getConfig().setPropertyElementType(
194 IndexYaml
.Index
.class, "properties", IndexYaml
.Property
.class);
195 List
<IndexesXml
> list
= new LinkedList
<IndexesXml
>();
197 IndexYaml indexYaml
= reader
.read(IndexYaml
.class);
198 if (null == indexYaml
) {
201 list
.add(indexYaml
.toXml(xml
));