View Javadoc
1   /*
2    * Copyright 2010 Philipp Erlacher
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  package org.exolab.castor.xml;
15  
16  import java.text.MessageFormat;
17  import java.util.Locale;
18  import java.util.ResourceBundle;
19  
20  import org.apache.commons.lang3.ArrayUtils;
21  import org.apache.commons.lang3.StringUtils;
22  import org.apache.commons.logging.Log;
23  import org.apache.commons.logging.LogFactory;
24  import org.xml.sax.ContentHandler;
25  import org.xml.sax.SAXException;
26  
27  /**
28   * A processor that assists {@link UnmarshalHandler} in dealing with the SAX 2
29   * {@link ContentHandler#characters(char[], int, int)} callback method.
30   * 
31   * @author <a href=" mailto:philipp.erlacher AT gmail DOT com">Philipp Erlacher</a>
32   */
33  public class CharactersProcessor {
34  
35    /**
36     * Standard logger to use.
37     */
38    private static final Log LOG = LogFactory.getLog(CharactersProcessor.class);
39  
40    /**
41     * resource bundle
42     */
43    protected static ResourceBundle resourceBundle;
44  
45    /**
46     * Callback {@link UnmarshalHandler} reference to set the actual state on this instance.
47     */
48    private final UnmarshalHandler _unmarshalHandler;
49  
50    static {
51      resourceBundle = ResourceBundle.getBundle("UnmarshalHandlerMessages", Locale.getDefault());
52    }
53  
54    /**
55     * Creates an instance of this class, with a reference to the actual {@link UnmarshalHandler} for
56     * which this processor deals with the SAX 2 characters() callback method.
57     * 
58     * @param unmarshalHandler The {@link UnmarshalHandler} instance on which the results of
59     *        processing the characters method will be 'persisted'/set.
60     */
61    public CharactersProcessor(final UnmarshalHandler unmarshalHandler) {
62      _unmarshalHandler = unmarshalHandler;
63    }
64  
65    public void compute(char[] ch, int start, int length) throws SAXException {
66      String string = new String(ch, start, length);
67      if (LOG.isTraceEnabled()) {
68        String trace = MessageFormat.format(
69            resourceBundle.getString("unmarshalHandler.log.trace.characters"), new Object[] {string});
70        LOG.trace(trace);
71      }
72  
73      // -- If we are skipping elements that have appeared in the XML but for
74      // -- which we have no mapping, skip the text and return
75      if (_unmarshalHandler.getStrictElementHandler().skipElement()) {
76        return;
77      }
78  
79      if (_unmarshalHandler.getStateStack().isEmpty()) {
80        return;
81      }
82  
83      if (_unmarshalHandler.getAnyNodeHandler().hasAnyUnmarshaller()) {
84        _unmarshalHandler.getAnyNodeHandler().characters(ch, start, length);
85        return;
86      }
87  
88      UnmarshalState state = _unmarshalHandler.getStateStack().getLastState();
89      // -- handle whitespace
90      boolean removedTrailingWhitespace = false;
91      boolean removedLeadingWhitespace = false;
92      if (!state.isWhitespacePreserving() && !ArrayUtils.isEmpty(ch)) {
93        removedTrailingWhitespace = Character.isWhitespace(ch[start + length - 1]);
94        removedLeadingWhitespace = Character.isWhitespace(ch[start]);
95        string = string.trim();
96      }
97  
98      if (state.getBuffer() == null) {
99        state.setBuffer(new StringBuffer());
100     } else {
101       if (state.isWhitespacePreserving()) {
102         state.setTrailingWhitespaceRemoved(false);
103         state.getBuffer().append(string);
104         return;
105       } else if (StringUtils.isEmpty(string)) {
106         state.setTrailingWhitespaceRemoved(removedTrailingWhitespace);
107         return;
108       } else if (state.isTrailingWhitespaceRemoved() || removedLeadingWhitespace) {
109         state.getBuffer().append(' ');
110       }
111     }
112     state.setTrailingWhitespaceRemoved(removedTrailingWhitespace);
113     state.getBuffer().append(string);
114   }
115 }