Annotation of gpl/axl/ns/axl_ns_doc.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  *  LibAxl:  Another XML library
        !             3:  *  Copyright (C) 2006 Advanced Software Production Line, S.L.
        !             4:  *
        !             5:  *  This program is free software; you can redistribute it and/or
        !             6:  *  modify it under the terms of the GNU Lesser General Public License
        !             7:  *  as published by the Free Software Foundation; either version 2.1 of
        !             8:  *  the License, or (at your option) any later version.
        !             9:  *
        !            10:  *  This program is distributed in the hope that it will be useful,
        !            11:  *  but WITHOUT ANY WARRANTY; without even the implied warranty of 
        !            12:  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  
        !            13:  *  GNU Lesser General Public License for more details.
        !            14:  *
        !            15:  *  You should have received a copy of the GNU Lesser General Public
        !            16:  *  License along with this program; if not, write to the Free
        !            17:  *  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
        !            18:  *  02111-1307 USA
        !            19:  *  
        !            20:  *  You may find a copy of the license under this software is released
        !            21:  *  at COPYING file. This is LGPL software: you are welcome to
        !            22:  *  develop proprietary applications using this library without any
        !            23:  *  royalty or fee but returning back any change, improvement or
        !            24:  *  addition in the form of source code, project image, documentation
        !            25:  *  patches, etc. 
        !            26:  *
        !            27:  *  For commercial support on build XML enabled solutions contact us:
        !            28:  *          
        !            29:  *      Postal address:
        !            30:  *         Advanced Software Production Line, S.L.
        !            31:  *         Edificio Alius A, Oficina 102,
        !            32:  *         C/ Antonio Suarez Nº 10,
        !            33:  *         Alcalá de Henares 28802 Madrid
        !            34:  *         Spain
        !            35:  *
        !            36:  *      Email address:
        !            37:  *         info@aspl.es - http://www.aspl.es/xml
        !            38:  */
        !            39: #include <axl_ns.h>
        !            40: 
        !            41: #define LOG_DOMAIN "axl-ns-doc"
        !            42: 
        !            43: /**
        !            44:  * \defgroup axl_ns_doc_module Axl Doc Namespace: Xml 1.0 namespace support for XML documents
        !            45:  */
        !            46: 
        !            47: /** 
        !            48:  * \addtogroup axl_ns_doc_module
        !            49:  * @{
        !            50:  */
        !            51: 
        !            52: /** 
        !            53:  * @internal Name of the key used to access and store the ns table for
        !            54:  * each node.
        !            55:  */
        !            56: #define NS_TABLE "__axl:ns-table"
        !            57: 
        !            58: 
        !            59: /** 
        !            60:  * @internal Table used to hold bindings from prefix declared and
        !            61:  * namespaces used. It also holds the default namespace declared for a
        !            62:  * particular node, refering to it and all its content until found
        !            63:  * another declaration.
        !            64:  */
        !            65: typedef struct _axlNsTable {
        !            66:        /* bindings between prefixes and namespaces */
        !            67:        axlHash * table;
        !            68:        
        !            69:        /* default namespace to be used */
        !            70:        const char * defaultNs;
        !            71: } axlNsTable;
        !            72: 
        !            73: void __axl_ns_free_table (axlNsTable * table)
        !            74: {
        !            75:        if (table == NULL)
        !            76:                return;
        !            77: 
        !            78:        /* free table */
        !            79:        axl_hash_free (table->table);
        !            80: 
        !            81:        /* free node */
        !            82:        axl_free (table);
        !            83: 
        !            84:        return;
        !            85: } /* end __axl_ns_free_table */
        !            86: 
        !            87: /* check and install all ns declerations found on this node */
        !            88: axl_bool __axl_ns_node_check_and_install_ns_decls (axlNode * node, axlAttrCursor * cursor, axlError ** error)
        !            89: {
        !            90:        const char    * attr;
        !            91:        axl_bool        default_found = axl_false;
        !            92:        axlNsTable    * ns_table = NULL;
        !            93: 
        !            94:        /* for each attribute installed on the node */
        !            95:        while (axl_node_attr_cursor_has_item (cursor)) {
        !            96: 
        !            97:                /* get the key */
        !            98:                attr = axl_node_attr_cursor_get_key (cursor);
        !            99:                if (! axl_memcmp (attr, "xmlns", 5)) {
        !           100:                        /* no xml namespace declaration found, go next */
        !           101:                        axl_node_attr_cursor_next (cursor);
        !           102:                        continue;
        !           103:                } /* end if */
        !           104: 
        !           105:                /* create the table (without creating the hash for now) */
        !           106:                if (ns_table == NULL) {
        !           107:                        ns_table = axl_new (axlNsTable, 1);
        !           108:                        
        !           109:                        /* install the table in the node */
        !           110:                        axl_node_annotate_data_full (node, 
        !           111:                                                     /* key and its destroy function */
        !           112:                                                     NS_TABLE, NULL,
        !           113:                                                     /* value and its destroy function */
        !           114:                                                     ns_table, (axlDestroyFunc)__axl_ns_free_table);
        !           115:                } /* end if */
        !           116:                
        !           117:                /* check if we have a default xmlns declaration */
        !           118:                if (strlen (attr) == 5) {
        !           119:                        /* check if there were previous xmlns default declarations */
        !           120:                        if (default_found) {
        !           121:                                /* free cursor */
        !           122:                                axl_node_attr_cursor_free (cursor);
        !           123:                                
        !           124:                                axl_error_new (-1, "Found that the document defines several default xmlns declarations at the same node", NULL, error);
        !           125:                                return axl_false;
        !           126:                        } /* end if */
        !           127:                        default_found = axl_true;
        !           128:                        
        !           129:                        /* copy the default namespace */
        !           130:                        ns_table->defaultNs = axl_node_attr_cursor_get_value (cursor);
        !           131:                        
        !           132:                        /* go next */
        !           133:                        axl_node_attr_cursor_next (cursor);
        !           134:                        continue;
        !           135:                        
        !           136:                } /* end if */
        !           137:                
        !           138:                /* found preffixed namespace declaration */
        !           139:                if (ns_table->table == NULL) {
        !           140:                        /* create the table with 1 step */
        !           141:                        ns_table->table = axl_hash_new_full (axl_hash_string, axl_hash_equal_string, 1);
        !           142:                } /* end if */
        !           143:                
        !           144:                /* check that the prefix wasn't declared at the same node */
        !           145:                attr = attr + 6;
        !           146:                if (axl_hash_exists (ns_table->table, (axlPointer) attr)) {
        !           147:                        /* free cursor */
        !           148:                        axl_node_attr_cursor_free (cursor);
        !           149:                        
        !           150:                        axl_error_new (-1, "Found that the document already declares the same prefix for the same node several times",
        !           151:                                       NULL, error);
        !           152:                        return axl_false;
        !           153:                } /* end if */
        !           154:                
        !           155:                /* install it in the current ns hash table */
        !           156:                axl_hash_insert (ns_table->table, (axlPointer) attr, (axlPointer) axl_node_attr_cursor_get_value (cursor));
        !           157: 
        !           158:                /* get next attribute */
        !           159:                axl_node_attr_cursor_next (cursor);
        !           160: 
        !           161:        } /* end while */
        !           162: 
        !           163:        /* all declarations done */
        !           164:        return axl_true;
        !           165:        
        !           166: } /* end __axl_ns_node_check_and_install_ns_decls */
        !           167: 
        !           168: /** 
        !           169:  * @internal Function that validates the node against current status
        !           170:  * of the namespace support.
        !           171:  */
        !           172: axl_bool __axl_ns_node_validate (axlNode * node, axlError ** error)
        !           173: {
        !           174:        int          iterator = 0;
        !           175:        char       * name;
        !           176:        axl_bool     found    = axl_false;
        !           177:        axlNsTable * ns_table;
        !           178:        axlNode    * parent;
        !           179:        axlNode    * child;
        !           180: 
        !           181:        /* create the cursor */
        !           182:        axlAttrCursor * cursor = NULL;
        !           183: 
        !           184:        /* check if the node has a namespace declaration */
        !           185:        if (axl_node_has_attributes (node)) {
        !           186: 
        !           187:                /* the node has attributes, check if there are some
        !           188:                 * namespace declaration */
        !           189:                cursor = axl_node_attr_cursor_new (node);
        !           190: 
        !           191:                /* check and install all ns declerations found on this
        !           192:                 * node (cursor is dellocated by this function on
        !           193:                 * error) */
        !           194:                if (! __axl_ns_node_check_and_install_ns_decls (node, cursor, error))
        !           195:                        return axl_false;
        !           196: 
        !           197:        } /* end if */
        !           198: 
        !           199:        /* now we have all namespace declarations, check that the node
        !           200:         * name and its attributes are properly declared */
        !           201:        name = (char*) axl_node_get_name (node);
        !           202: 
        !           203:        /* check if the node name uses a namespace scope declaration
        !           204:         * (prefix:name) */
        !           205:        while (name[iterator] != 0) {
        !           206:                if (name[iterator] == ':') {
        !           207:                        /* set the terminator */
        !           208:                        name[iterator] = 0;
        !           209: 
        !           210:                        /* namespace node found */
        !           211:                        found = axl_true;
        !           212:                        break;
        !           213:                }
        !           214: 
        !           215:                /* get next position */
        !           216:                iterator++;
        !           217:        } /* end if */
        !           218:        
        !           219:        if (found) {
        !           220:                /* namespace declaration found (this supposes that the
        !           221:                 * default ns is not used) */
        !           222:                parent = node;
        !           223:                while (parent != NULL) {
        !           224:                        /* try to get the ns table having the prefix declaration */
        !           225:                        ns_table = axl_node_annotate_get (parent, NS_TABLE, axl_false);
        !           226: 
        !           227:                        /* check the namespace */
        !           228:                        if (ns_table != NULL && axl_hash_exists (ns_table->table, name)) {
        !           229:                                /* prefix found, restore node name */
        !           230:                                name[iterator] = ':';
        !           231:                                break;
        !           232:                        } /* end if */
        !           233:                        
        !           234:                        /* get the parent */
        !           235:                        parent = axl_node_get_parent (parent);
        !           236:                        
        !           237:                        /* no more parents */
        !           238:                        if (parent == NULL) {
        !           239:                                /* prefix not found, restore node name */
        !           240:                                name[iterator] = ':';
        !           241: 
        !           242:                                /* free cursor */
        !           243:                                axl_node_attr_cursor_free (cursor);
        !           244:                        
        !           245:                                axl_error_new (-1, "Found prefix declaration for which a namespace binding wasn't found in the node or any parent (xmlns:prefix='ns')",
        !           246:                                               NULL, error);
        !           247:                                return axl_false;
        !           248:                        } /* end if */
        !           249:                } /* end while */
        !           250:        } /* end if */
        !           251: 
        !           252:        /* check if the node has a namespace declaration */
        !           253:        if (axl_node_has_attributes (node)) {
        !           254:                /* now check attributes */
        !           255:                axl_node_attr_cursor_first (cursor);
        !           256:                while (axl_node_attr_cursor_has_item (cursor)) {
        !           257:                        
        !           258:                        /* get the key */
        !           259:                        name = (char*) axl_node_attr_cursor_get_key (cursor);
        !           260:                        if (axl_memcmp (name, "xmlns", 5)) {
        !           261:                                /* skip namespace declarations */
        !           262:                                axl_node_attr_cursor_next (cursor);
        !           263:                                continue;
        !           264:                        } /* end if */
        !           265:                        
        !           266:                        __axl_log (LOG_DOMAIN, AXL_LEVEL_DEBUG, "checking attribute name='%s'", name);
        !           267:                        
        !           268:                        /* check if the node name uses a namespace scope declaration
        !           269:                         * (prefix:name) */
        !           270:                        found    = axl_false;
        !           271:                        iterator = 0;
        !           272:                        while (name[iterator] != 0) {
        !           273:                                if (name[iterator] == ':') {
        !           274:                                        /* set the terminator */
        !           275:                                        name[iterator] = 0;
        !           276:                                        
        !           277:                                        /* namespace node found */
        !           278:                                        found = axl_true;
        !           279:                                        break;
        !           280:                                }
        !           281:                                
        !           282:                                /* get next position */
        !           283:                                iterator++;
        !           284:                        } /* end while */
        !           285:                        
        !           286:                        if (found) {
        !           287:                                parent = node;
        !           288:                                while (parent != NULL) {
        !           289:                                        /* try to get the ns table having the prefix declaration */
        !           290:                                        ns_table = axl_node_annotate_get (parent, NS_TABLE, axl_false);
        !           291:                                        
        !           292:                                        /* check the namespace */
        !           293:                                        if (ns_table != NULL && axl_hash_exists (ns_table->table, name)) {
        !           294:                                                /* prefix found, restore attribute name */
        !           295:                                                name[iterator] = ':';
        !           296:                                                break;
        !           297:                                        } /* end if */
        !           298:                                        
        !           299:                                        /* get the parent */
        !           300:                                        parent = axl_node_get_parent (parent);
        !           301:                                        
        !           302:                                        /* no more parents */
        !           303:                                        if (parent == NULL) {
        !           304:                                                /* prefix not found, restore attribute name */
        !           305:                                                name[iterator] = ':';
        !           306:                                                
        !           307:                                                /* free cursor */
        !           308:                                                axl_node_attr_cursor_free (cursor);
        !           309:                                                
        !           310:                                                axl_error_new (-1, "Found prefix declaration, for an attribute, for which a namespace binding wasn't found in the node or any parent (xmlns:prefix='ns')",
        !           311:                                                               NULL, error);
        !           312:                                                return axl_false;
        !           313:                                        } /* end if */
        !           314:                                } /* end while */
        !           315:                        } /* end if */
        !           316:                        
        !           317:                        /* next attribute */
        !           318:                        axl_node_attr_cursor_next (cursor);
        !           319:                        
        !           320:                } /* end while */
        !           321:        
        !           322:                /* free cursor */
        !           323:                axl_node_attr_cursor_free (cursor);
        !           324: 
        !           325:        } /* end if */
        !           326: 
        !           327:        /* do the same for the rest of the child nodes */
        !           328:        child = axl_node_get_first_child (node);
        !           329:        while (child != NULL) {
        !           330:                /* check childs */
        !           331:                if (! __axl_ns_node_validate (child, error))
        !           332:                        return axl_false;
        !           333: 
        !           334:                /* go next child node */
        !           335:                child = axl_node_get_next (child);
        !           336:        } /* end while */
        !           337: 
        !           338:        /* return axl_true, if reached this place */
        !           339:        return axl_true;
        !           340: }
        !           341: 
        !           342: /** 
        !           343:  * @brief Allows to check XML 1.0 namespace rules for the document
        !           344:  * loaded.
        !           345:  *
        !           346:  * XML 1.0 Namespaces definition allows you to define documents that
        !           347:  * are resistent to tag clashing, making your product to be more
        !           348:  * usable at any level, being mixed with other products.
        !           349:  *
        !           350:  * This function allows you to check a xml document, already loaded
        !           351:  * with the Axl Doc base API (\ref axl_doc_parse_from_file, \ref
        !           352:  * axl_doc_parse or \ref axl_doc_create), if it follows the namespaces
        !           353:  * contrains (XML 1.0 Namespaces http://www.w3.org/TR/REC-xml-names/).
        !           354:  *
        !           355:  * The idea behind this function is to ensure that the document has
        !           356:  * the proper XML 1.0 Namespace declarations, which will be used by
        !           357:  * the following function to help you detect those tags recognized by
        !           358:  * your XML software, ensuring this matching is done inside your
        !           359:  * namespace:
        !           360:  * 
        !           361:  *   - \ref axl_ns_node_cmp
        !           362:  *   - \ref axl_ns_node_find_called
        !           363:  *   - See \ref axl_ns_node_module "Axl namespace support for nodes" to get more information about function used to read your xml documents in a namespace aware manner.
        !           364:  * 
        !           365:  * Here is a simple example on how to load a xml document, add
        !           366:  * namespace checking support, and read content inside without
        !           367:  * compromissing your code to node tag collitions:
        !           368:  * 
        !           369:  * \include ns_example.c
        !           370:  *
        !           371:  * As a gold rule, your code must not use prefixed names (full
        !           372:  * qualified names) to check xml node names (tags), because they will
        !           373:  * make your code fragile to changes introduced in xml documents read
        !           374:  * by your application.
        !           375:  * 
        !           376:  * Instead, you must provide your namespace where the validation will take
        !           377:  * place and the local name of the node being checked (knowing that
        !           378:  * the prefix and the local name for <b>&lt;shaper:xml-rpc-invoke&gt;</b> is
        !           379:  * <b>shaper</b> and <b>xml-rpc-invoke</b> respectively).
        !           380:  *
        !           381:  * Let's see an an example to clarify this. Assuming the following xml document: 
        !           382:  *
        !           383:  * <div class="xml-doc">
        !           384:  * \include ns_shaper.xml
        !           385:  * </div>
        !           386:  *
        !           387:  * Previous examples shows a simple shaper description, which is using
        !           388:  * <b>shaper</b> as prefix for its nodes. Under this situation you
        !           389:  * must not use the following to check for an xml node name:
        !           390:  *
        !           391:  * \code
        !           392:  * if (NODE_CMP_NAME (node, "shaper:xml-rpc-invoke")) {
        !           393:  *     // found shaper:xml-rpc-invoke tag 
        !           394:  * }
        !           395:  * \endcode
        !           396:  *
        !           397:  * This is because, as we have said, you are placing direct references
        !           398:  * to the namespace prefix declared at the document, but this is
        !           399:  * wrong. You can't ensure the user won't change the namespace binding
        !           400:  * that links your namespace with the prefix <b>shaper</b>.
        !           401:  *
        !           402:  * The proper way to check xml node names (tags) in a namespace aware
        !           403:  * manner is:
        !           404:  * 
        !           405:  * \code
        !           406:  * // supposing SHAPER_NS defines the namespace string 
        !           407:  * if (axl_ns_node_cmp (node, SHAPER_NS, "xml-rpc-invoke")) {
        !           408:  *     // found xml-rpc-invoke tag 
        !           409:  * }
        !           410:  * \endcode
        !           411:  * 
        !           412:  * Now, the user will be able to change the binding between your
        !           413:  * namespace and the prefix used. This will enable him to use the
        !           414:  * prefix <b>shaper</b> for its products, without breaking your
        !           415:  * software.
        !           416:  *
        !           417:  * <i><b>NOTE:</b> providing a document without content (at least one
        !           418:  * root node configured), will cause the function to return \ref
        !           419:  * axl_true.</i>
        !           420:  * 
        !           421:  * @param doc The document that is being required to be checked
        !           422:  * against the XML Namespaces 1.0 rules.
        !           423:  *
        !           424:  * @param error An optional variable where errors will be reported. If
        !           425:  * the function returns \ref axl_false, you can call to \ref axl_error_get
        !           426:  * to get a textual diagnostic.
        !           427:  * 
        !           428:  * @return \ref axl_true if the document is namespace-valid, otherwise,
        !           429:  * \ref axl_false is returned.
        !           430:  */
        !           431: axl_bool axl_ns_doc_validate (axlDoc * doc, axlError ** error)
        !           432: {
        !           433:        axlNode * node;
        !           434: 
        !           435:        /* check references */
        !           436:        if (doc == NULL) {
        !           437:                axl_error_new (-1, "Document provided is a null reference", NULL, error);
        !           438:                return axl_false;
        !           439:        } /* end if */
        !           440: 
        !           441:        /* get root document */
        !           442:        node = axl_doc_get_root (doc);
        !           443:        if (node == NULL) {
        !           444:                /* document has no nodes, return axl_true */
        !           445:                return axl_true;
        !           446:        } /* end if */
        !           447:  
        !           448:        /* call to produce and validate namespace content */
        !           449:        return __axl_ns_node_validate (node, error);
        !           450: }
        !           451: 
        !           452: /** 
        !           453:  * @internal Internal API that allows to check if the provided prefix
        !           454:  * (ns_prefix) is bound to the provided namespace, using the node
        !           455:  * provided as a reference for the parent lookup.
        !           456:  * 
        !           457:  * @param node The node that is used as reference for the
        !           458:  * lookup. Starting from this node the lookup will be performed,
        !           459:  * following with parents.
        !           460:  *
        !           461:  * @param ns_prefix The ns prefix to be checked.
        !           462:  *
        !           463:  * @param ns The namespace that must be bound the prefix.
        !           464:  * 
        !           465:  * @return axl_true if the prefix is bound to the namespace provided,
        !           466:  * using the node as reference for all declarations found inside the
        !           467:  * particular node (or its parents).
        !           468:  */
        !           469: axl_bool axl_ns_doc_node_check (axlNode    * node, 
        !           470:                                const char * ns_prefix, 
        !           471:                                const char * ns)
        !           472: {
        !           473:        axlNode    * parent   = node;
        !           474:        axlNsTable * ns_table = NULL;
        !           475: 
        !           476:        /* foreach node, up to the root parent, starting from the node
        !           477:         * provided, do: */
        !           478:        while (parent != NULL) {
        !           479:                /* try to get the ns table having the prefix declaration */
        !           480:                ns_table = axl_node_annotate_get (parent, NS_TABLE, axl_false);
        !           481:                
        !           482:                /* check the namespace */
        !           483:                if (ns_table != NULL && axl_hash_exists (ns_table->table, (axlPointer) ns_prefix)) {
        !           484:                        /* prefix found, check its value */
        !           485:                        return axl_cmp (axl_hash_get (ns_table->table, (axlPointer) ns_prefix), ns);
        !           486:                } /* end if */
        !           487:                
        !           488:                /* not found, get the parent */
        !           489:                parent = axl_node_get_parent (parent);
        !           490: 
        !           491:        } /* end while */       
        !           492: 
        !           493:        /* reached this point, return axl_false */
        !           494:        return axl_false;
        !           495: }
        !           496: 
        !           497: /** 
        !           498:  * @internal Function that allows to check if the default namespace is
        !           499:  * the value provided, using as reference, the node received.
        !           500:  * 
        !           501:  * @param node The node to check for its default namespace to match
        !           502:  * the value provided.
        !           503:  *
        !           504:  * @param ns The namespace that is provided to match.
        !           505:  * 
        !           506:  * @return \ref axl_true if the node has as default namespace the value
        !           507:  * received. Otherwise \ref axl_false is returned.
        !           508:  */
        !           509: axl_bool axl_ns_doc_check_default (axlNode    * node, 
        !           510:                                   const char * ns)
        !           511: {
        !           512:        axlNode    * parent   = node;
        !           513:        axlNsTable * ns_table = NULL;
        !           514: 
        !           515:        /* foreach node, up to the root parent, starting from the node
        !           516:         * provided, do: */
        !           517:        while (parent != NULL) {
        !           518:                /* try to get the ns table having the prefix declaration */
        !           519:                ns_table = axl_node_annotate_get (parent, NS_TABLE, axl_false);
        !           520:                
        !           521:                /* check the namespace */
        !           522:                if (ns_table != NULL && ns_table->defaultNs != NULL) {
        !           523:                        /* prefix found, check its value */
        !           524:                        return axl_cmp (ns_table->defaultNs, ns);
        !           525:                } /* end if */
        !           526:                
        !           527:                /* not found, get the parent */
        !           528:                parent = axl_node_get_parent (parent);
        !           529: 
        !           530:        } /* end while */       
        !           531: 
        !           532:        /* reached this point, return axl_false */
        !           533:        return axl_false;
        !           534: }
        !           535: 
        !           536: /**
        !           537:  * @}
        !           538:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>