001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    
021    package org.apache.directory.shared.dsmlv2;
022    
023    
024    import java.util.List;
025    
026    import javax.xml.transform.Transformer;
027    import javax.xml.transform.TransformerConfigurationException;
028    import javax.xml.transform.TransformerException;
029    import javax.xml.transform.TransformerFactory;
030    import javax.xml.transform.stream.StreamSource;
031    
032    import org.apache.directory.shared.dsmlv2.engine.Dsmlv2Engine;
033    import org.apache.directory.shared.dsmlv2.request.BatchRequest;
034    import org.apache.directory.shared.dsmlv2.request.BatchRequest.Processing;
035    import org.apache.directory.shared.dsmlv2.request.BatchRequest.ResponseOrder;
036    import org.apache.directory.shared.i18n.I18n;
037    import org.apache.directory.shared.ldap.ldif.LdifUtils;
038    import org.apache.directory.shared.ldap.message.control.Control;
039    import org.apache.directory.shared.ldap.util.Base64;
040    import org.apache.directory.shared.ldap.util.StringTools;
041    import org.dom4j.Document;
042    import org.dom4j.Element;
043    import org.dom4j.Namespace;
044    import org.dom4j.QName;
045    import org.dom4j.io.DocumentResult;
046    import org.dom4j.io.DocumentSource;
047    import org.xmlpull.v1.XmlPullParser;
048    import org.xmlpull.v1.XmlPullParserException;
049    
050    
051    /**
052     * This class is a Helper class for the DSML Parser
053     *
054     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
055     * @version $Rev$, $Date$
056     */
057    public class ParserUtils
058    {
059        public static final String XML_SCHEMA_URI = "http://www.w3c.org/2001/XMLSchema";
060        public static final String XML_SCHEMA_INSTANCE_URI = "http://www.w3c.org/2001/XMLSchema-instance";
061        public static final String BASE64BINARY = "base64Binary";
062        public static final String XSI = "xsi";
063        public static final String XSD = "xsd";
064    
065    
066        /**
067         * Returns the value of the attribute 'type' of the "XMLSchema-instance' namespace if it exists
068         *
069         * @param xpp 
070         *      the XPP parser to use
071         * @return 
072         *      the value of the attribute 'type' of the "XMLSchema-instance' namespace if it exists
073         */
074        public static String getXsiTypeAttributeValue( XmlPullParser xpp )
075        {
076            String type = null;
077            int nbAttributes = xpp.getAttributeCount();
078            for ( int i = 0; i < nbAttributes; i++ )
079            {
080                // Checking if the attribute 'type' from XML Schema Instance namespace is used.
081                if ( xpp.getAttributeName( i ).equals( "type" )
082                    && xpp.getNamespace( xpp.getAttributePrefix( i ) ).equals( XML_SCHEMA_INSTANCE_URI ) )
083                {
084                    type = xpp.getAttributeValue( i );
085                    break;
086                }
087            }
088            return type;
089        }
090    
091    
092        /**
093         * Tells is the given value is a Base64 binary value
094         * 
095         * @param parser
096         *      the XPP parser to use
097         * @param attrValue 
098         *      the attribute value
099         * @return 
100         *      true if the value of the current tag is Base64BinaryEncoded, false if not
101         */
102        public static boolean isBase64BinaryValue( XmlPullParser parser, String attrValue )
103        {
104            if ( attrValue == null )
105            {
106                return false;
107            }
108            // We are looking for something that should look like that: "aNameSpace:base64Binary"
109            // We split the String. The first element should be the namespace prefix and the second "base64Binary"
110            String[] splitedString = attrValue.split( ":" );
111            return ( splitedString.length == 2 ) && ( XML_SCHEMA_URI.equals( parser.getNamespace( splitedString[0] ) ) )
112                && ( BASE64BINARY.equals( splitedString[1] ) );
113        }
114    
115    
116        /**
117         * Indicates if the value needs to be encoded as Base64
118         *
119         * @param value 
120         *      the value to check
121         * @return 
122         *      true if the value needs to be encoded as Base64
123         */
124        public static boolean needsBase64Encoding( Object value )
125        {
126            if ( value instanceof byte[] )
127            {
128                return true;
129            }
130            else if ( value instanceof String )
131            {
132                return !LdifUtils.isLDIFSafe( ( String ) value );
133            }
134            return true;
135        }
136    
137    
138        /**
139         * Encodes the value as a Base64 String
140         *
141         * @param value 
142         *      the value to encode
143         * @return 
144         *      the value encoded as a Base64 String 
145         */
146        public static String base64Encode( Object value )
147        {
148            if ( value instanceof byte[] )
149            {
150                return new String( Base64.encode( ( byte[] ) value ) );
151            }
152            else if ( value instanceof String )
153            {
154                return new String( Base64.encode( StringTools.getBytesUtf8( ( String ) value ) ) );
155            }
156    
157            return "";
158        }
159    
160    
161        /**
162         * Parses and verify the parsed value of the requestID
163         * 
164         * @param attributeValue 
165         *      the value of the attribute
166         * @param xpp 
167         *      the XmlPullParser
168         * @return
169         *      the int value of the resquestID
170         * @throws XmlPullParserException
171         *      if RequestID isn't an Integer and if requestID equals 0
172         */
173        public static int parseAndVerifyRequestID( String attributeValue, XmlPullParser xpp ) throws XmlPullParserException
174        {
175            try
176            {
177                int requestID = Integer.parseInt( attributeValue );
178    
179                if ( requestID == 0 )
180                {
181                    throw new XmlPullParserException( I18n.err( I18n.ERR_03038 ), xpp, null );
182                }
183    
184                return requestID;
185            }
186            catch ( NumberFormatException e )
187            {
188                throw new XmlPullParserException( I18n.err( I18n.ERR_03039 ), xpp, null );
189            }
190        }
191    
192    
193        /**
194         * Adds Controls to the given Element.
195         *
196         * @param element
197         *      the element to add the Controls to
198         * @param controls
199         *      a List of Controls
200         */
201        public static void addControls( Element element, List<Control> controls )
202        {
203            if ( controls != null )
204            {
205                for ( int i = 0; i < controls.size(); i++ )
206                {
207                    Control control = controls.get( i );
208    
209                    Element controlElement = element.addElement( "control" );
210    
211                    if ( control.getOid() != null )
212                    {
213                        controlElement.addAttribute( "type", control.getOid() );
214                    }
215    
216                    if ( control.isCritical() )
217                    {
218                        controlElement.addAttribute( "criticality", "true" );
219                    }
220    
221                    Object value = control.getValue();
222                    
223                    if ( value != null )
224                    {
225                        if ( ParserUtils.needsBase64Encoding( value ) )
226                        {
227                            Namespace xsdNamespace = new Namespace( ParserUtils.XSD, ParserUtils.XML_SCHEMA_URI );
228                            Namespace xsiNamespace = new Namespace( ParserUtils.XSI, ParserUtils.XML_SCHEMA_INSTANCE_URI );
229                            element.getDocument().getRootElement().add( xsdNamespace );
230                            element.getDocument().getRootElement().add( xsiNamespace );
231    
232                            Element valueElement = controlElement.addElement( "controlValue" ).addText(
233                                ParserUtils.base64Encode( value ) );
234                            valueElement.addAttribute( new QName( "type", xsiNamespace ), ParserUtils.XSD + ":"
235                                + ParserUtils.BASE64BINARY );
236                        }
237                        else
238                        {
239                            controlElement.addElement( "controlValue" ).setText( ( String ) value );
240                        }
241                    }
242                }
243            }
244        }
245    
246    
247        /**
248         * Indicates if a request ID is needed.
249         *
250         * @param container
251         *      the associated container
252         * @return
253         *      true if a request ID is needed (ie Processing=Parallel and ResponseOrder=Unordered)
254         * @throws XmlPullParserException
255         *      if the batch request has not been parsed yet
256         */
257        public static boolean isRequestIdNeeded( Dsmlv2Container container ) throws XmlPullParserException
258        {
259            BatchRequest batchRequest = container.getBatchRequest();
260    
261            if ( batchRequest == null )
262            {
263                throw new XmlPullParserException( I18n.err( I18n.ERR_03040 ), container.getParser(), null );
264            }
265    
266            return ( ( batchRequest.getProcessing() == Processing.PARALLEL ) && ( batchRequest.getResponseOrder() == ResponseOrder.UNORDERED ) );
267        }
268    
269    
270        /**
271         * XML Pretty Printer XSLT Tranformation
272         * 
273         * @param document
274         *      the Dom4j Document
275         * @return
276         *      the transformed document
277         */
278        public static Document styleDocument( Document document )
279        {
280            // load the transformer using JAXP
281            TransformerFactory factory = TransformerFactory.newInstance();
282            Transformer transformer = null;
283            try
284            {
285                transformer = factory.newTransformer( new StreamSource( Dsmlv2Engine.class
286                    .getResourceAsStream( "DSMLv2.xslt" ) ) );
287            }
288            catch ( TransformerConfigurationException e1 )
289            {
290                // TODO Auto-generated catch block
291                e1.printStackTrace();
292            }
293    
294            // now lets style the given document
295            DocumentSource source = new DocumentSource( document );
296            DocumentResult result = new DocumentResult();
297            try
298            {
299                transformer.transform( source, result );
300            }
301            catch ( TransformerException e )
302            {
303                // TODO Auto-generated catch block
304                e.printStackTrace();
305            }
306    
307            // return the transformed document
308            Document transformedDoc = result.getDocument();
309            return transformedDoc;
310        }
311    }