001: /*
002: * Copyright 2006-2007 The Scriptella Project Team.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package scriptella.configuration;
018:
019: import org.w3c.dom.Element;
020: import org.w3c.dom.Node;
021: import scriptella.spi.DialectIdentifier;
022: import scriptella.spi.Resource;
023: import scriptella.util.StringUtils;
024:
025: import java.util.ArrayList;
026: import java.util.List;
027: import java.util.regex.Pattern;
028:
029: /**
030: * Represents dialect based content used inside query/script/onerror elements.
031: * <p>When the DOM is traversed the internal representation model is built.
032: * This model contains enough info to speed up selection based on a requested dialect identifier.
033: * The following example demonstrates the algorithm
034: * using pseudo markup:
035: * <pre>
036: * AAA[Dialect1 111]BBB[Dialect2 222]
037: * </pre>
038: * The returned text for default Dialect is: AAABBB
039: * <br> The returned text for Dialect1 is: AAA111BBB
040: *
041: * @author Fyodor Kupolov
042: * @version 1.0
043: */
044: public class DialectBasedContentEl extends XmlConfigurableBase {
045: private List<Dialect> dialects;
046:
047: public DialectBasedContentEl() {
048: }
049:
050: public DialectBasedContentEl(final XmlElement element) {
051: configure(element);
052: }
053:
054: public void configure(final XmlElement element) {
055: Dialect defaultDialect = null;
056: dialects = new ArrayList<Dialect>();
057: //iterate through the child nodes of this element
058: for (Node node = element.getElement().getFirstChild(); node != null; node = node
059: .getNextSibling()) {
060: if (isDialectElement(node)) {
061: if (defaultDialect != null) {
062: dialects.add(defaultDialect);
063: defaultDialect = null;
064: }
065: Dialect d = new Dialect();
066: d.configure(new XmlElement((Element) node, element));
067: dialects.add(d);
068: } else {
069: //Try to convert the node to resource if possible
070: Resource resource = ContentEl.asResource(element, node);
071: //If it's a text or include
072: if (resource != null) {
073: //check if we have default dialect instance
074: if (defaultDialect == null) {
075: //if no - create one
076: defaultDialect = new Dialect();
077: defaultDialect.configureDefault(element);
078: }
079: //append a resource to default dialect
080: defaultDialect.contentEl.append(resource);
081: }
082: }
083: }
084: if (defaultDialect != null) {
085: dialects.add(defaultDialect); //
086: }
087:
088: }
089:
090: private static boolean isDialectElement(Node node) {
091: return node instanceof Element
092: && "dialect".equals(node.getNodeName());
093: }
094:
095: /**
096: * This method returns content for specified dialect id or null - if script doesn't support this dialect.
097: *
098: * @param id dialect identifier. null if any dialect.
099: * @return content for specified dialect id or null - if script doesn't support this dialect.
100: */
101: public ContentEl getContent(final DialectIdentifier id) {
102: ContentEl result = null;
103: for (Dialect d : dialects) {
104: if (d.matches(id)) {
105: if (result == null) {
106: result = new ContentEl();
107: result.setLocation(getLocation());
108: }
109: result.merge(d.getContentEl());
110: }
111: }
112: return result;
113: }
114:
115: /**
116: * For testing purposes
117: *
118: * @return internal list of dialects.
119: */
120: List<Dialect> getDialects() {
121: return dialects;
122: }
123:
124: static class Dialect extends XmlConfigurableBase {
125: private Pattern name;
126: private Pattern version;
127: private ContentEl contentEl;
128:
129: public Pattern getName() {
130: return name;
131: }
132:
133: public void setName(final Pattern name) {
134: this .name = name;
135: }
136:
137: public Pattern getVersion() {
138: return version;
139: }
140:
141: public void setVersion(final Pattern version) {
142: this .version = version;
143: }
144:
145: public ContentEl getContentEl() {
146: return contentEl;
147: }
148:
149: public void configure(final XmlElement element) {
150: setPatternProperty(element, "name");
151: setPatternProperty(element, "version");
152: contentEl = new ContentEl(element);
153: setLocation(element);
154: }
155:
156: /**
157: * Configures default dialect.
158: *
159: * @param parent parent element.
160: */
161: public void configureDefault(final XmlElement parent) {
162: setLocation(parent);
163: contentEl = new ContentEl();
164: }
165:
166: boolean matches(final DialectIdentifier id) {
167: if (id == null) { //if db has no dialect identifier
168: //return true only if we have no specified restrictions
169: return name == null && version == null;
170: }
171:
172: String idName = StringUtils.nullsafeToString(id.getName());
173: String idVersion = StringUtils.nullsafeToString(id
174: .getVersion());
175: //Substring matching is used for names. Versions are matched entirely
176: return ((name == null) || name.matcher(idName).find())
177: && ((version == null) || version.matcher(idVersion)
178: .matches());
179:
180: }
181:
182: public String toString() {
183: return "Dialect{" + "name=" + name + ", version=" + version
184: + ", contentEl=" + contentEl + "}";
185: }
186: }
187:
188: }
|