Annotation of gpl/axl/ns/axl_ns_doc.c, revision 1.1.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>