1 /*
2  * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 package com.sun.beans.decoder;
26 
27 /**
28  * The base class for element handlers.
29  *
30  * @since 1.7
31  *
32  * @author Sergey A. Malenkov
33  *
34  * @see DocumentHandler
35  */
36 public abstract class ElementHandler {
37     private DocumentHandler owner;
38     private ElementHandler parent;
39 
40     private String id;
41 
42     /**
43      * Returns the document handler that creates this element handler.
44      *
45      * @return the owner document handler
46      */
getOwner()47     public final DocumentHandler getOwner() {
48         return this.owner;
49     }
50 
51     /**
52      * Sets the document handler that creates this element handler.
53      * The owner document handler should be set after instantiation.
54      * Such approach is used to simplify the extensibility.
55      *
56      * @param owner  the owner document handler
57      * @see DocumentHandler#startElement
58      */
setOwner(DocumentHandler owner)59     final void setOwner(DocumentHandler owner) {
60         if (owner == null) {
61             throw new IllegalArgumentException("Every element should have owner");
62         }
63         this.owner = owner;
64     }
65 
66     /**
67      * Returns the element handler that contains this one.
68      *
69      * @return the parent element handler
70      */
getParent()71     public final ElementHandler getParent() {
72         return this.parent;
73     }
74 
75     /**
76      * Sets the element handler that contains this one.
77      * The parent element handler should be set after instantiation.
78      * Such approach is used to simplify the extensibility.
79      *
80      * @param parent  the parent element handler
81      * @see DocumentHandler#startElement
82      */
setParent(ElementHandler parent)83     final void setParent(ElementHandler parent) {
84         this.parent = parent;
85     }
86 
87     /**
88      * Returns the value of the variable with specified identifier.
89      *
90      * @param id  the identifier
91      * @return the value of the variable
92      */
getVariable(String id)93     protected final Object getVariable(String id) {
94         if (id.equals(this.id)) {
95             ValueObject value = getValueObject();
96             if (value.isVoid()) {
97                 throw new IllegalStateException("The element does not return value");
98             }
99             return value.getValue();
100         }
101         return (this.parent != null)
102                 ? this.parent.getVariable(id)
103                 : this.owner.getVariable(id);
104     }
105 
106     /**
107      * Returns the value of the parent element.
108      *
109      * @return the value of the parent element
110      */
getContextBean()111     protected Object getContextBean() {
112         if (this.parent != null) {
113             ValueObject value = this.parent.getValueObject();
114             if (!value.isVoid()) {
115                 return value.getValue();
116             }
117             throw new IllegalStateException("The outer element does not return value");
118         } else {
119             Object value = this.owner.getOwner();
120             if (value != null) {
121                 return value;
122             }
123             throw new IllegalStateException("The topmost element does not have context");
124         }
125     }
126 
127     /**
128      * Parses attributes of the element.
129      * By default, the following attribute is supported:
130      * <dl>
131      * <dt>id
132      * <dd>the identifier of the variable that is intended to store the result
133      * </dl>
134      *
135      * @param name   the attribute name
136      * @param value  the attribute value
137      */
addAttribute(String name, String value)138     public void addAttribute(String name, String value) {
139         if (name.equals("id")) { // NON-NLS: the attribute name
140             this.id = value;
141         } else {
142             throw new IllegalArgumentException("Unsupported attribute: " + name);
143         }
144     }
145 
146     /**
147      * This method is called before parsing of the element's body.
148      * All attributes are parsed at this point.
149      * By default, do nothing.
150      */
startElement()151     public void startElement() {
152     }
153 
154     /**
155      * This method is called after parsing of the element's body.
156      * By default, it calculates the value of this element.
157      * The following tasks are executing for any non-void value:
158      * <ol>
159      * <li>If the {@code id} attribute is set
160      * the value of the variable with the specified identifier
161      * is set to the value of this element.</li>
162      * <li>This element is used as an argument of parent element if it is possible.</li>
163      * </ol>
164      *
165      * @see #isArgument
166      */
endElement()167     public void endElement() {
168         // do nothing if no value returned
169         ValueObject value = getValueObject();
170         if (!value.isVoid()) {
171             if (this.id != null) {
172                 this.owner.setVariable(this.id, value.getValue());
173             }
174             if (isArgument()) {
175                 if (this.parent != null) {
176                     this.parent.addArgument(value.getValue());
177                 } else {
178                     this.owner.addObject(value.getValue());
179                 }
180             }
181         }
182     }
183 
184     /**
185      * Adds the character that contained in this element.
186      * By default, only whitespaces are acceptable.
187      *
188      * @param ch  the character
189      */
addCharacter(char ch)190     public void addCharacter(char ch) {
191         if ((ch != ' ') && (ch != '\n') && (ch != '\t') && (ch != '\r')) {
192             throw new IllegalStateException("Illegal character with code " + (int) ch);
193         }
194     }
195 
196     /**
197      * Adds the argument that is used to calculate the value of this element.
198      * By default, no arguments are acceptable.
199      *
200      * @param argument  the value of the element that contained in this one
201      */
addArgument(Object argument)202     protected void addArgument(Object argument) {
203         throw new IllegalStateException("Could not add argument to simple element");
204     }
205 
206     /**
207      * Tests whether the value of this element can be used
208      * as an argument of the element that contained in this one.
209      *
210      * @return {@code true} if the value of this element can be used
211      *         as an argument of the element that contained in this one,
212      *         {@code false} otherwise
213      */
isArgument()214     protected boolean isArgument() {
215         return this.id == null;
216     }
217 
218     /**
219      * Returns the value of this element.
220      *
221      * @return the value of this element
222      */
getValueObject()223     protected abstract ValueObject getValueObject();
224 }
225