| /* Copyright 2004 The Apache Software Foundation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.xmlbeans.impl.store; |
| |
| import org.apache.xmlbeans.impl.common.ValidatorListener; |
| import javax.xml.stream.Location; |
| import org.apache.xmlbeans.XmlCursor; |
| import javax.xml.namespace.QName; |
| |
| final class Validate implements ValidatorListener.Event |
| { |
| Validate ( Cur c, ValidatorListener sink ) |
| { |
| if (!c.isUserNode()) |
| throw new IllegalStateException( "Inappropriate location to validate" ); |
| |
| _sink = sink; |
| _cur = c; |
| _textCur = c.tempCur(); |
| _hasText = false; |
| |
| _cur.push(); |
| |
| try |
| { |
| process(); |
| } |
| finally |
| { |
| _cur.pop(); |
| _cur = null; |
| |
| _sink = null; |
| |
| _textCur.release(); |
| } |
| } |
| |
| private void process ( ) |
| { |
| emitEvent( ValidatorListener.BEGIN ); |
| |
| if (_cur.isAttr()) |
| { |
| // If validating an attr, I'm really validating the contents of that attr. So, go to |
| // any text value and shove it thru the validator. |
| |
| _cur.next(); |
| |
| if (_cur.isText()) |
| emitText(); |
| } |
| else |
| { |
| assert _cur.isContainer(); |
| |
| // Do the attrs of the top container |
| |
| doAttrs(); |
| |
| for ( _cur.next() ; ! _cur.isAtEndOfLastPush() ; _cur.next() ) |
| { |
| switch ( _cur.kind() ) |
| { |
| case Cur.ELEM : |
| emitEvent( ValidatorListener.BEGIN ); |
| doAttrs(); |
| break; |
| |
| case - Cur.ELEM : |
| emitEvent( ValidatorListener.END ); |
| break; |
| |
| case Cur.TEXT : |
| emitText(); |
| break; |
| |
| case Cur.COMMENT : |
| case Cur.PROCINST : |
| _cur.toEnd(); |
| break; |
| |
| default : |
| throw new RuntimeException( "Unexpected kind: " + _cur.kind() ); |
| } |
| } |
| } |
| |
| emitEvent( ValidatorListener.END ); |
| } |
| |
| private void doAttrs ( ) |
| { |
| // When processing attrs, there can be no accumulated text because there would have been |
| // a preceeding event which would have flushged the text. |
| |
| assert !_hasText; |
| |
| if (_cur.toFirstAttr()) |
| { |
| do |
| { |
| if (_cur.isNormalAttr() && !_cur.getUri().equals( Locale._xsi )) |
| _sink.nextEvent( ValidatorListener.ATTR, this ); |
| } |
| while ( _cur.toNextAttr() ); |
| |
| _cur.toParent(); |
| } |
| |
| _sink.nextEvent( ValidatorListener.ENDATTRS, this ); |
| } |
| |
| private void emitText ( ) |
| { |
| assert _cur.isText(); |
| |
| if (_hasText) |
| { |
| if (_oneChunk) |
| { |
| if (_textSb == null) |
| _textSb = new StringBuffer(); |
| else |
| _textSb.delete( 0, _textSb.length() ); |
| |
| assert _textCur.isText(); |
| |
| CharUtil.getString( |
| _textSb, _textCur.getChars( -1 ), _textCur._offSrc, _textCur._cchSrc ); |
| |
| _oneChunk = false; |
| } |
| |
| assert _textSb != null && _textSb.length() > 0; |
| |
| CharUtil.getString( _textSb, _cur.getChars( -1 ), _cur._offSrc, _cur._cchSrc ); |
| } |
| else |
| { |
| _hasText = true; |
| _oneChunk = true; |
| _textCur.moveToCur( _cur ); |
| } |
| } |
| |
| private void emitEvent ( int kind ) |
| { |
| assert kind != ValidatorListener.TEXT; |
| assert kind != ValidatorListener.ATTR || !_hasText; |
| assert kind != ValidatorListener.ENDATTRS || !_hasText; |
| |
| if (_hasText) |
| { |
| _sink.nextEvent( ValidatorListener.TEXT, this ); |
| _hasText = false; |
| } |
| |
| _sink.nextEvent( kind, this ); |
| } |
| |
| public String getText ( ) |
| { |
| if (_cur.isAttr()) |
| return _cur.getValueAsString(); |
| |
| assert _hasText; |
| assert _oneChunk || (_textSb != null && _textSb.length() > 0); |
| assert !_oneChunk || _textCur.isText(); |
| |
| return _oneChunk ? _textCur.getCharsAsString() : _textSb.toString(); |
| } |
| |
| public String getText ( int wsr ) |
| { |
| if (_cur.isAttr()) |
| return _cur.getValueAsString( wsr ); |
| |
| assert _hasText; |
| assert _oneChunk || (_textSb != null && _textSb.length() > 0); |
| assert !_oneChunk || _textCur.isText(); |
| |
| if (_oneChunk) |
| return _textCur.getCharsAsString( wsr ); |
| |
| return Locale.applyWhiteSpaceRule( _textSb.toString(), wsr ); |
| } |
| |
| public boolean textIsWhitespace ( ) |
| { |
| if (_cur.isAttr()) |
| { |
| return |
| _cur._locale.getCharUtil().isWhiteSpace( |
| _cur.getFirstChars(), _cur._offSrc, _cur._cchSrc ); |
| } |
| |
| assert _hasText; |
| |
| if (_oneChunk) |
| { |
| return |
| _cur._locale.getCharUtil().isWhiteSpace( |
| _textCur.getChars( -1 ), _textCur._offSrc, _textCur._cchSrc ); |
| } |
| |
| String s = _textSb.toString(); |
| |
| return _cur._locale.getCharUtil().isWhiteSpace( s, 0, s.length() ); |
| } |
| |
| public String getNamespaceForPrefix ( String prefix ) |
| { |
| return _cur.namespaceForPrefix( prefix, true ); |
| } |
| |
| public XmlCursor getLocationAsCursor ( ) |
| { |
| return new Cursor( _cur ); |
| } |
| |
| public Location getLocation ( ) |
| { |
| return null; |
| } |
| |
| public String getXsiType ( ) |
| { |
| return _cur.getAttrValue( Locale._xsiType ); |
| } |
| |
| public String getXsiNil ( ) |
| { |
| return _cur.getAttrValue( Locale._xsiNil ); |
| } |
| |
| public String getXsiLoc ( ) |
| { |
| return _cur.getAttrValue( Locale._xsiLoc ); |
| } |
| |
| public String getXsiNoLoc ( ) |
| { |
| return _cur.getAttrValue( Locale._xsiNoLoc ); |
| } |
| |
| public QName getName ( ) |
| { |
| return _cur.isAtLastPush() ? null : _cur.getName(); |
| } |
| |
| // |
| // |
| // |
| |
| private ValidatorListener _sink; |
| |
| private Cur _cur; |
| |
| // Two ways to accumulate text. First, I can have a Cur positioned at the text. I do this |
| // instead of getting the there there because white space rules are applied at a later point. |
| // This way, when I turn the text into a String, I can cache the string. If multiple chunks |
| // of text exists for one event, then I accumulate all the text into a string buffer and I, |
| // then, don't care about caching Strings. |
| |
| private boolean _hasText; |
| private boolean _oneChunk; |
| |
| private Cur _textCur; |
| private StringBuffer _textSb; |
| } |