    1   /*
    2    * Copyright 1999-2004 The Apache Software Foundation
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *     http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  package org.apache.commons.jxpath.ri.axes;
    17
    18  import java.util.HashMap;
    19
    20  import junit.framework.TestCase;
    21
    22  import org.apache.commons.jxpath.JXPathContext;
    23  import org.apache.commons.jxpath.NestedTestBean;
    24  import org.apache.commons.jxpath.Pointer;
    25  import org.apache.commons.jxpath.TestNull;
    26  import org.apache.commons.jxpath.ri.model.NodePointer;
    27  import org.apache.commons.jxpath.ri.model.VariablePointer;
    28  import org.apache.commons.jxpath.ri.model.beans.BeanPointer;
    29  import org.apache.commons.jxpath.ri.model.beans.BeanPropertyPointer;
    30  import org.apache.commons.jxpath.ri.model.beans.CollectionPointer;
    31  import org.apache.commons.jxpath.ri.model.beans.NullElementPointer;
    32  import org.apache.commons.jxpath.ri.model.beans.NullPointer;
    33  import org.apache.commons.jxpath.ri.model.beans.NullPropertyPointer;
    34  import org.apache.commons.jxpath.ri.model.beans.TestBeanFactory;
    35  import org.apache.commons.jxpath.ri.model.dom.DOMNodePointer;
    36  import org.apache.commons.jxpath.ri.model.dynamic.DynamicPointer;
    37  import org.apache.commons.jxpath.ri.model.dynamic.DynamicPropertyPointer;
    38
    39  public class SimplePathInterpreterTest extends TestCase {
    40
    41      private TestBeanWithNode bean;
    42      private JXPathContext context;
    43
    44      /***
    45       * Constructor for SimplePathInterpreterTest.
    46       */
    47      public SimplePathInterpreterTest(String name) {
    48          super(name);
    49      }
    50
    51      public static void main(String[] args) {
    52          junit.textui.TestRunner.run(SimplePathInterpreterTest.class);
    53      }
    54
    55      /***
    56       * @see TestCase#setUp()
    57       */
    58      protected void setUp() throws Exception {
    59          bean = TestBeanWithNode.createTestBeanWithDOM();
    60          HashMap submap = new HashMap();
    61          submap.put("key", new NestedTestBean("Name 9"));
    62          submap.put("strings", bean.getNestedBean().getStrings());
    63          bean.getList().add(new int[]{1, 2});
    64          bean.getList().add(bean.getVendor());
    65          bean.getMap().put("Key3",
    66              new Object[]{
    67                  new NestedTestBean("some"),
    68                  new Integer(2),
    69                  bean.getVendor(),
    70                  submap
    71              }
    72          );
    73          bean.getMap().put("Key4", bean.getVendor());
    74          bean.getMap().put("Key5", submap);
    75          bean.getMap().put("Key6", new Object[0]);
    76          context = JXPathContext.newContext(null, bean);
    77          context.setLenient(true);
    78          context.setFactory(new TestBeanFactory());
    79      }
    80
    81      public void testDoStepNoPredicatesPropertyOwner() {
    82          // Existing scalar property
    83          assertValueAndPointer("/int",
    84                  new Integer(1),
    85                  "/int",
    86                  "Bb",
    87                  "BbB");
    88
    89          // self::
    90          assertValueAndPointer("/./int",
    91                  new Integer(1),
    92                  "/int",
    93                  "Bb",
    94                  "BbB");
    95
    96          // Missing property
    97          assertNullPointer("/foo",
    98                  "/foo",
    99                  "Bn");
    100
    101         // existingProperty/existingScalarProperty
    102         assertValueAndPointer("/nestedBean/int",
    103                 new Integer(1),
    104                 "/nestedBean/int",
    105                 "BbBb",
    106                 "BbBbB");
    107
    108         // existingProperty/collectionProperty
    109         assertValueAndPointer("/nestedBean/strings",
    110                 bean.getNestedBean().getStrings(),
    111                 "/nestedBean/strings",
    112                 "BbBb",
    113                 "BbBbC");
    114
    115         // existingProperty/missingProperty
    116         assertNullPointer("/nestedBean/foo",
    117                 "/nestedBean/foo",
    118                 "BbBn");
    119
    120         // map/missingProperty
    121         assertNullPointer("/map/foo",
    122                 "/map[@name='foo']",
    123                 "BbDd");
    124
    125         // Existing property by search in collection
    126         assertValueAndPointer("/list/int",
    127                 new Integer(1),
    128                 "/list[3]/int",
    129                 "BbBb",
    130                 "BbBbB");
    131
    132         // Missing property by search in collection
    133         assertNullPointer("/list/foo",
    134                 "/list[1]/foo",
    135                 "BbBn");
    136
    137         // existingProperty/missingProperty/missingProperty
    138         assertNullPointer("/nestedBean/foo/bar",
    139                 "/nestedBean/foo/bar",
    140                 "BbBnNn");
    141
    142         // collection/existingProperty/missingProperty
    143         assertNullPointer("/list/int/bar",
    144                 "/list[3]/int/bar",
    145                 "BbBbBn");
    146
    147         // collectionProperty/missingProperty/missingProperty
    148         assertNullPointer("/list/foo/bar",
    149                 "/list[1]/foo/bar",
    150                 "BbBnNn");
    151
    152         // map/missingProperty/anotherStep
    153         assertNullPointer("/map/foo/bar",
    154                 "/map[@name='foo']/bar",
    155                 "BbDdNn");
    156
    157         // Existing dynamic property
    158         assertValueAndPointer("/map/Key1",
    159                 "Value 1",
    160                 "/map[@name='Key1']",
    161                 "BbDd",
    162                 "BbDdB");
    163
    164         // collectionProperty
    165         assertValueAndPointer("/integers",
    166                 bean.getIntegers(),
    167                 "/integers",
    168                 "Bb",
    169                 "BbC");
    170     }
    171
    172     public void testDoStepNoPredicatesStandard() {
    173         // Existing DOM node
    174         assertValueAndPointer("/vendor/location/address/city",
    175                 "Fruit Market",
    176                 "/vendor/location[2]/address[1]/city[1]",
    177                 "BbMMMM");
    178
    179         // Missing DOM node
    180         assertNullPointer("/vendor/location/address/pity",
    181                 "/vendor/location[1]/address[1]/pity",
    182                 "BbMMMn");
    183
    184         // Missing DOM node inside a missing element
    185         assertNullPointer("/vendor/location/address/itty/bitty",
    186                 "/vendor/location[1]/address[1]/itty/bitty",
    187                 "BbMMMnNn");
    188
    189         // Missing DOM node by search for the best match
    190         assertNullPointer("/vendor/location/address/city/pretty",
    191                 "/vendor/location[2]/address[1]/city[1]/pretty",
    192                 "BbMMMMn");
    193     }
    194
    195     public void testDoStepPredicatesPropertyOwner() {
    196         // missingProperty[@name=foo]
    197         assertNullPointer("/foo[@name='foo']",
    198                 "/foo[@name='foo']",
    199                 "BnNn");
    200
    201         // missingProperty[index]
    202         assertNullPointer("/foo[3]",
    203                 "/foo[3]",
    204                 "Bn");
    205     }
    206
    207     public void testDoStepPredicatesStandard() {
    208         // Looking for an actual XML attribute called "name"
    209         // nodeProperty/name[@name=value]
    210         assertValueAndPointer("/vendor/contact[@name='jack']",
    211                 "Jack",
    212                 "/vendor/contact[2]",
    213                 "BbMM");
    214
    215         // Indexing in XML
    216         assertValueAndPointer("/vendor/contact[2]",
    217                 "Jack",
    218                 "/vendor/contact[2]",
    219                 "BbMM");
    220
    221         // Indexing in XML, no result
    222         assertNullPointer("/vendor/contact[5]",
    223                 "/vendor/contact[5]",
    224                 "BbMn");
    225
    226         // Combination of search by name and indexing in XML
    227         assertValueAndPointer("/vendor/contact[@name='jack'][2]",
    228                 "Jack Black",
    229                 "/vendor/contact[4]",
    230                 "BbMM");
    231
    232         // Combination of search by name and indexing in XML
    233         assertValueAndPointer("/vendor/contact[@name='jack'][2]",
    234                 "Jack Black",
    235                 "/vendor/contact[4]",
    236                 "BbMM");
    237     }
    238
    239     public void testDoPredicateName() {
    240         // existingProperty[@name=existingProperty]
    241         assertValueAndPointer("/nestedBean[@name='int']",
    242                 new Integer(1),
    243                 "/nestedBean/int",
    244                 "BbBb",
    245                 "BbBbB");
    246
    247         // /self::node()[@name=existingProperty]
    248         assertValueAndPointer("/.[@name='int']",
    249                 new Integer(1),
    250                 "/int",
    251                 "Bb",
    252                 "BbB");
    253
    254         // dynamicProperty[@name=existingProperty]
    255         assertValueAndPointer("/map[@name='Key1']",
    256                 "Value 1",
    257                 "/map[@name='Key1']",
    258                 "BbDd",
    259                 "BbDdB");
    260
    261         // existingProperty[@name=collectionProperty]
    262         assertValueAndPointer("/nestedBean[@name='strings']",
    263                 bean.getNestedBean().getStrings(),
    264                 "/nestedBean/strings",
    265                 "BbBb",
    266                 "BbBbC");
    267
    268         // existingProperty[@name=missingProperty]
    269         assertNullPointer("/nestedBean[@name='foo']",
    270                 "/nestedBean[@name='foo']",
    271                 "BbBn");
    272
    273         // map[@name=collectionProperty]
    274         assertValueAndPointer("/map[@name='Key3']",
    275                 bean.getMap().get("Key3"),
    276                 "/map[@name='Key3']",
    277                 "BbDd",
    278                 "BbDdC");
    279
    280         // map[@name=missingProperty]
    281         assertNullPointer("/map[@name='foo']",
    282                 "/map[@name='foo']",
    283                 "BbDd");
    284
    285         // collectionProperty[@name=...] (find node)
    286         assertValueAndPointer("/list[@name='fruitco']",
    287                 context.getValue("/vendor"),
    288                 "/list[5]",
    289                 "BbCM");
    290
    291         // collectionProperty[@name=...] (find map entry)
    292         assertValueAndPointer("/map/Key3[@name='key']/name",
    293                 "Name 9",
    294                 "/map[@name='Key3'][4][@name='key']/name",
    295                 "BbDdCDdBb",
    296                 "BbDdCDdBbB");
    297
    298         // map/collectionProperty[@name...]
    299         assertValueAndPointer("map/Key3[@name='fruitco']",
    300                 context.getValue("/vendor"),
    301                 "/map[@name='Key3'][3]",
    302                 "BbDdCM");
    303
    304         // Bean property -> DOM Node, name match
    305         assertValueAndPointer("/vendor[@name='fruitco']",
    306                 context.getValue("/vendor"),
    307                 "/vendor",
    308                 "BbM");
    309
    310         // Bean property -> DOM Node, name mismatch
    311         assertNullPointer("/vendor[@name='foo']",
    312                 "/vendor[@name='foo']",
    313                 "BbMn");
    314
    315         assertNullPointer("/vendor[@name='foo'][3]",
    316                 "/vendor[@name='foo'][3]",
    317                 "BbMn");
    318
    319         // existingProperty(bean)[@name=missingProperty]/anotherStep
    320         assertNullPointer("/nestedBean[@name='foo']/bar",
    321                 "/nestedBean[@name='foo']/bar",
    322                 "BbBnNn");
    323
    324         // map[@name=missingProperty]/anotherStep
    325         assertNullPointer("/map[@name='foo']/bar",
    326                 "/map[@name='foo']/bar",
    327                 "BbDdNn");
    328
    329         // existingProperty(node)[@name=missingProperty]/anotherStep
    330         assertNullPointer("/vendor[@name='foo']/bar",
    331                 "/vendor[@name='foo']/bar",
    332                 "BbMnNn");
    333
    334         // existingProperty(node)[@name=missingProperty][index]/anotherStep
    335         assertNullPointer("/vendor[@name='foo'][3]/bar",
    336                 "/vendor[@name='foo'][3]/bar",
    337                 "BbMnNn");
    338
    339         // Existing dynamic property + existing property
    340         assertValueAndPointer("/map[@name='Key2'][@name='name']",
    341                 "Name 6",
    342                 "/map[@name='Key2']/name",
    343                 "BbDdBb",
    344                 "BbDdBbB");
    345
    346         // Existing dynamic property + existing property + index
    347         assertValueAndPointer("/map[@name='Key2'][@name='strings'][2]",
    348                 "String 2",
    349                 "/map[@name='Key2']/strings[2]",
    350                 "BbDdBb",
    351                 "BbDdBbB");
    352
    353         // bean/map/map/property
    354         assertValueAndPointer("map[@name='Key5'][@name='key']/name",
    355                 "Name 9",
    356                 "/map[@name='Key5'][@name='key']/name",
    357                 "BbDdDdBb",
    358                 "BbDdDdBbB");
    359
    360         assertNullPointer("map[@name='Key2'][@name='foo']",
    361                 "/map[@name='Key2'][@name='foo']",
    362                 "BbDdBn");
    363
    364         assertNullPointer("map[@name='Key2'][@name='foo'][@name='bar']",
    365                 "/map[@name='Key2'][@name='foo'][@name='bar']",
    366                 "BbDdBnNn");
    367
    368         // bean/map/node
    369         assertValueAndPointer("map[@name='Key4'][@name='fruitco']",
    370                 context.getValue("/vendor"),
    371                 "/map[@name='Key4']",
    372                 "BbDdM");
    373     }
    374
    375     public void testDoPredicatesStandard() {
    376         // bean/map/collection/node
    377         assertValueAndPointer("map[@name='Key3'][@name='fruitco']",
    378                 context.getValue("/vendor"),
    379                 "/map[@name='Key3'][3]",
    380                 "BbDdCM");
    381
    382         // bean/map/collection/missingNode
    383         assertNullPointer("map[@name='Key3'][@name='foo']",
    384                 "/map[@name='Key3'][4][@name='foo']",
    385                 "BbDdCDd");
    386
    387         // bean/map/node
    388         assertValueAndPointer("map[@name='Key4'][@name='fruitco']",
    389                 context.getValue("/vendor"),
    390                 "/map[@name='Key4']",
    391                 "BbDdM");
    392
    393         // bean/map/emptyCollection[@name=foo]
    394         assertNullPointer("map[@name='Key6'][@name='fruitco']",
    395                 "/map[@name='Key6'][@name='fruitco']",
    396                 "BbDdCn");
    397
    398         // bean/node[@name=foo][index]
    399         assertValueAndPointer("/vendor/contact[@name='jack'][2]",
    400                 "Jack Black",
    401                 "/vendor/contact[4]",
    402                 "BbMM");
    403
    404         // bean/node[@name=foo][missingIndex]
    405         assertNullPointer("/vendor/contact[@name='jack'][5]",
    406                 "/vendor/contact[@name='jack'][5]",
    407                 "BbMnNn");
    408
    409         // bean/node/.[@name=foo][index]
    410         assertValueAndPointer("/vendor/contact/.[@name='jack']",
    411                 "Jack",
    412                 "/vendor/contact[2]",
    413                 "BbMM");
    414     }
    415
    416     public void testDoPredicateIndex() {
    417         // Existing dynamic property + existing property + index
    418         assertValueAndPointer("/map[@name='Key2'][@name='strings'][2]",
    419                 "String 2",
    420                 "/map[@name='Key2']/strings[2]",
    421                 "BbDdBb",
    422                 "BbDdBbB");
    423
    424         // existingProperty[@name=collectionProperty][index]
    425         assertValueAndPointer("/nestedBean[@name='strings'][2]",
    426                 bean.getNestedBean().getStrings()[1],
    427                 "/nestedBean/strings[2]",
    428                 "BbBb",
    429                 "BbBbB");
    430
    431         // existingProperty[@name=missingProperty][index]
    432         assertNullPointer("/nestedBean[@name='foo'][3]",
    433                 "/nestedBean[@name='foo'][3]",
    434                 "BbBn");
    435
    436         // existingProperty[@name=collectionProperty][missingIndex]
    437         assertNullPointer("/nestedBean[@name='strings'][5]",
    438                 "/nestedBean/strings[5]",
    439                 "BbBbE");
    440
    441         // map[@name=collectionProperty][index]
    442         assertValueAndPointer("/map[@name='Key3'][2]",
    443                 new Integer(2),
    444                 "/map[@name='Key3'][2]",
    445                 "BbDd",
    446                 "BbDdB");
    447
    448         // map[@name=collectionProperty][missingIndex]
    449         assertNullPointer("/map[@name='Key3'][5]",
    450                 "/map[@name='Key3'][5]",
    451                 "BbDdE");
    452
    453         // map[@name=collectionProperty][missingIndex]/property
    454         assertNullPointer("/map[@name='Key3'][5]/foo",
    455                 "/map[@name='Key3'][5]/foo",
    456                 "BbDdENn");
    457
    458         // map[@name=map][@name=collection][index]
    459         assertValueAndPointer("/map[@name='Key5'][@name='strings'][2]",
    460                 "String 2",
    461                 "/map[@name='Key5'][@name='strings'][2]",
    462                 "BbDdDd",
    463                 "BbDdDdB");
    464
    465         // map[@name=map][@name=collection][missingIndex]
    466         assertNullPointer("/map[@name='Key5'][@name='strings'][5]",
    467                 "/map[@name='Key5'][@name='strings'][5]",
    468                 "BbDdDdE");
    469
    470         // Existing dynamic property + indexing
    471         assertValueAndPointer("/map[@name='Key3'][2]",
    472                 new Integer(2),
    473                 "/map[@name='Key3'][2]",
    474                 "BbDd",
    475                 "BbDdB");
    476
    477         // Existing dynamic property + indexing
    478         assertValueAndPointer("/map[@name='Key3'][1]/name",
    479                 "some",
    480                 "/map[@name='Key3'][1]/name",
    481                 "BbDdBb",
    482                 "BbDdBbB");
    483
    484         // map[@name=missingProperty][index]
    485         assertNullPointer("/map[@name='foo'][3]",
    486                 "/map[@name='foo'][3]",
    487                 "BbDdE");
    488
    489         // collectionProperty[index]
    490         assertValueAndPointer("/integers[2]",
    491                 new Integer(2),
    492                 "/integers[2]",
    493                 "Bb",
    494                 "BbB");
    495
    496         // existingProperty/collectionProperty[index]
    497         assertValueAndPointer("/nestedBean/strings[2]",
    498                 bean.getNestedBean().getStrings()[1],
    499                 "/nestedBean/strings[2]",
    500                 "BbBb",
    501                 "BbBbB");
    502
    503         // existingProperty[index]/existingProperty
    504         assertValueAndPointer("/list[3]/int",
    505                 new Integer(1),
    506                 "/list[3]/int",
    507                 "BbBb",
    508                 "BbBbB");
    509
    510         // existingProperty[missingIndex]
    511         assertNullPointer("/list[6]",
    512                 "/list[6]",
    513                 "BbE");
    514
    515         // existingProperty/missingProperty[index]
    516         assertNullPointer("/nestedBean/foo[3]",
    517                 "/nestedBean/foo[3]",
    518                 "BbBn");
    519
    520         // map[@name=missingProperty][index]
    521         assertNullPointer("/map/foo[3]",
    522                 "/map[@name='foo'][3]",
    523                 "BbDdE");
    524
    525         // existingProperty/collectionProperty[missingIndex]
    526         assertNullPointer("/nestedBean/strings[5]",
    527                 "/nestedBean/strings[5]",
    528                 "BbBbE");
    529
    530         // map/collectionProperty[missingIndex]/property
    531         assertNullPointer("/map/Key3[5]/foo",
    532                 "/map[@name='Key3'][5]/foo",
    533                 "BbDdENn");
    534
    535         // map[@name=map]/collection[index]
    536         assertValueAndPointer("/map[@name='Key5']/strings[2]",
    537                 "String 2",
    538                 "/map[@name='Key5'][@name='strings'][2]",
    539                 "BbDdDd",
    540                 "BbDdDdB");
    541
    542         // map[@name=map]/collection[missingIndex]
    543         assertNullPointer("/map[@name='Key5']/strings[5]",
    544                 "/map[@name='Key5'][@name='strings'][5]",
    545                 "BbDdDdE");
    546
    547         // scalarPropertyAsCollection[index]
    548         assertValueAndPointer("/int[1]",
    549                 new Integer(1),
    550                 "/int",
    551                 "Bb",
    552                 "BbB");
    553
    554         // scalarPropertyAsCollection[index]
    555         assertValueAndPointer(".[1]/int",
    556                 new Integer(1),
    557                 "/int",
    558                 "Bb",
    559                 "BbB");
    560     }
    561
    562     public void testInterpretExpressionPath() {
    563         context.getVariables().declareVariable("array", new String[]{"Value1"});
    564         context.getVariables().declareVariable("testnull", new TestNull());
    565
    566         assertNullPointer("$testnull/nothing[2]",
    567                 "$testnull/nothing[2]",
    568                 "VBbE");
    569     }
    570
    571     private void assertValueAndPointer(
    572             String path, Object expectedValue, String expectedPath,
    573             String expectedSignature)
    574     {
    575         assertValueAndPointer(
    576             path,
    577             expectedValue,
    578             expectedPath,
    579             expectedSignature,
    580             expectedSignature);
    581     }
    582
    583     private void assertValueAndPointer(
    584             String path, Object expectedValue, String expectedPath,
    585             String expectedSignature, String expectedValueSignature)
    586     {
    587         Object value = context.getValue(path);
    588         assertEquals("Checking value: " + path, expectedValue, value);
    589
    590         Pointer pointer = context.getPointer(path);
    591         assertEquals("Checking pointer: " + path,
    592                 expectedPath, pointer.toString());
    593
    594         assertEquals("Checking signature: " + path,
    595                 expectedSignature, pointerSignature(pointer));
    596
    597         Pointer vPointer = ((NodePointer) pointer).getValuePointer();
    598         assertEquals("Checking value pointer signature: " + path,
    599                 expectedValueSignature, pointerSignature(vPointer));
    600     }
    601
    602     private void assertNullPointer(String path, String expectedPath,
    603             String expectedSignature)
    604     {
    605         Pointer pointer = context.getPointer(path);
    606         assertNotNull("Null path exists: " + path,
    607                     pointer);
    608         assertEquals("Null path as path: " + path,
    609                     expectedPath, pointer.asPath());
    610         assertEquals("Checking Signature: " + path,
    611                     expectedSignature, pointerSignature(pointer));
    612
    613         Pointer vPointer = ((NodePointer) pointer).getValuePointer();
    614         assertTrue("Null path is null: " + path,
    615                     !((NodePointer) vPointer).isActual());
    616         assertEquals("Checking value pointer signature: " + path,
    617                     expectedSignature + "N", pointerSignature(vPointer));
    618     }
    619
    620     /***
    621      * Since we need to test the internal Signature of a pointer,
    622      * we will get a signature which will contain a single character
    623      * per pointer in the chain, representing that pointer's type.
    624      */
    625     private String pointerSignature(Pointer pointer) {
    626         if (pointer == null) {
    627             return "";
    628         }
    629
    630         char type = '?';
    631         if (pointer instanceof NullPointer) {                 type = 'N'; }
    632         else if (pointer instanceof NullPropertyPointer) {    type = 'n'; }
    633         else if (pointer instanceof NullElementPointer) {     type = 'E'; }
    634         else if (pointer instanceof VariablePointer) {        type = 'V'; }
    635         else if (pointer instanceof CollectionPointer) {      type = 'C'; }
    636         else if (pointer instanceof BeanPointer) {            type = 'B'; }
    637         else if (pointer instanceof BeanPropertyPointer) {    type = 'b'; }
    638         else if (pointer instanceof DynamicPointer) {         type = 'D'; }
    639         else if (pointer instanceof DynamicPropertyPointer) { type = 'd'; }
    640         else if (pointer instanceof DOMNodePointer) {         type = 'M'; }
    641         else {
    642             System.err.println("UNKNOWN TYPE: " + pointer.getClass());
    643         }
    644         NodePointer parent =
    645             ((NodePointer) pointer).getImmediateParentPointer();
    646         return pointerSignature(parent) + type;
    647     }
    648 }
    649