##### Coverage Report - org.apache.commons.configuration.HierarchicalConfiguration

Classes in this File | Line Coverage | Branch Coverage | Complexity

HierarchicalConfiguration

93% | 148/159

100% | 39/39

1.9545454545454546;1,955

HierarchicalConfiguration$BuilderVisitor

100% | 26/26

100% | 9/9

1.9545454545454546;1,955

HierarchicalConfiguration$CloneVisitor

100% | 16/16

100% | 3/3

1.9545454545454546;1,955

HierarchicalConfiguration$DefinedKeysVisitor

100% | 16/16

100% | 2/2

1.9545454545454546;1,955

HierarchicalConfiguration$DefinedVisitor

100% | 5/5

100% | 1/1

1.9545454545454546;1,955

HierarchicalConfiguration$Node

85% | 33/39

82% | 9/11

1.9545454545454546;1,955

HierarchicalConfiguration$NodeVisitor

100% | 4/4

N/A | N/A

1.9545454545454546;1,955

1

     /*

2

      * Licensed to the Apache Software Foundation (ASF) under one or more

3

      * contributor license agreements.  See the NOTICE file distributed with

4

      * this work for additional information regarding copyright ownership.

5

      * The ASF licenses this file to You under the Apache License, Version 2.0

6

      * (the "License"); you may not use this file except in compliance with

7

      * the License.  You may obtain a copy of the License at

8

      *

9

      *     http://www.apache.org/licenses/LICENSE-2.0

10

      *

11

      * Unless required by applicable law or agreed to in writing, software

12

      * distributed under the License is distributed on an "AS IS" BASIS,

13

      * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

14

      * See the License for the specific language governing permissions and

15

      * limitations under the License.

16

      */

17

18

     package org.apache.commons.configuration;

19

20

     import java.io.Serializable;

21

     import java.util.ArrayList;

22

     import java.util.Collection;

23

     import java.util.Iterator;

24

     import java.util.LinkedList;

25

     import java.util.List;

26

     import java.util.Set;

27

     import java.util.Stack;

28

29

     import org.apache.commons.collections.set.ListOrderedSet;

30

     import org.apache.commons.collections.iterators.SingletonIterator;

31

     import org.apache.commons.configuration.tree.ConfigurationNode;

32

     import org.apache.commons.configuration.tree.ConfigurationNodeVisitorAdapter;

33

     import org.apache.commons.configuration.tree.DefaultConfigurationNode;

34

     import org.apache.commons.configuration.tree.DefaultExpressionEngine;

35

     import org.apache.commons.configuration.tree.ExpressionEngine;

36

     import org.apache.commons.configuration.tree.NodeAddData;

37

     import org.apache.commons.lang.StringUtils;

38

39

     /**

40

      * <p>A specialized configuration class that extends its base class by the

41

      * ability of keeping more structure in the stored properties.</p><p>There

42

      * are some sources of configuration data that cannot be stored very well in a

43

      * <code>BaseConfiguration</code> object because then their structure is lost.

44

      * This is especially true for XML documents. This class can deal with such

45

      * structured configuration sources by storing the properties in a tree-like

46

      * organization.</p><p>The internal used storage form allows for a more

47

      * sophisticated access to single properties. As an example consider the

48

      * following XML document:</p><p>

49

      *

50

      * <pre>

51

      * &lt;database&gt;

52

      *   &lt;tables&gt;

53

      *     &lt;table&gt;

54

      *       &lt;name&gt;users&lt;/name&gt;

55

      *       &lt;fields&gt;

56

      *         &lt;field&gt;

57

      *           &lt;name&gt;lid&lt;/name&gt;

58

      *           &lt;type&gt;long&lt;/name&gt;

59

      *         &lt;/field&gt;

60

      *         &lt;field&gt;

61

      *           &lt;name&gt;usrName&lt;/name&gt;

62

      *           &lt;type&gt;java.lang.String&lt;/type&gt;

63

      *         &lt;/field&gt;

64

      *        ...

65

      *       &lt;/fields&gt;

66

      *     &lt;/table&gt;

67

      *     &lt;table&gt;

68

      *       &lt;name&gt;documents&lt;/name&gt;

69

      *       &lt;fields&gt;

70

      *         &lt;field&gt;

71

      *           &lt;name&gt;docid&lt;/name&gt;

72

      *           &lt;type&gt;long&lt;/type&gt;

73

      *         &lt;/field&gt;

74

      *         ...

75

      *       &lt;/fields&gt;

76

      *     &lt;/table&gt;

77

      *     ...

78

      *   &lt;/tables&gt;

79

      * &lt;/database&gt;

80

      * </pre>

81

      *

82

      * </p><p>If this document is parsed and stored in a

83

      * <code>HierarchicalConfiguration</code> object (which can be done by one of

84

      * the sub classes), there are enhanced possibilities of accessing properties.

85

      * The keys for querying information can contain indices that select a certain

86

      * element if there are multiple hits.</p><p>For instance the key

87

      * <code>tables.table(0).name</code> can be used to find out the name of the

88

      * first table. In opposite <code>tables.table.name</code> would return a

89

      * collection with the names of all available tables. Similarily the key

90

      * <code>tables.table(1).fields.field.name</code> returns a collection with

91

      * the names of all fields of the second table. If another index is added after

92

      * the <code>field</code> element, a single field can be accessed:

93

      * <code>tables.table(1).fields.field(0).name</code>.</p><p>There is a

94

      * <code>getMaxIndex()</code> method that returns the maximum allowed index

95

      * that can be added to a given property key. This method can be used to iterate

96

      * over all values defined for a certain property.</p>

97

      *

98

      * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger </a>

99

      * @version $Id: HierarchicalConfiguration.java,v 1.14 2004/12/02 22:05:52

100

      * ebourg Exp $

101

      */

102

     public class HierarchicalConfiguration extends AbstractConfiguration implements Serializable, Cloneable

103

     {

104

         /** Constant for the clear tree event.*/

105

         public static final int EVENT_CLEAR_TREE = 10;

106

107

         /** Constant for the add nodes event.*/

108

         public static final int EVENT_ADD_NODES = 11;

109

110

         /**

111

          * The serial version UID.

112

          */

113

         private static final long serialVersionUID = 3373812230395363192L;

114

115

         /** Stores the default expression engine to be used for new objects.*/

116

24

         private static ExpressionEngine defaultExpressionEngine = new DefaultExpressionEngine();

117

118

         /** Stores the root node of this configuration. This field is required for

119

          * backwards compatibility only.

120

          */

121

         private Node root;

122

123

         /** Stores the root configuration node.*/

124

         private ConfigurationNode rootNode;

125

126

         /** Stores the expression engine for this instance.*/

127

         private ExpressionEngine expressionEngine;

128

129

         /**

130

          * Creates a new instance of <code>HierarchicalConfiguration</code>.

131

          */

132

         public HierarchicalConfiguration()

133

810

         {

134

810

             setRootNode(new Node());

135

810

         }

136

137

         /**

138

          * Returns the root node of this hierarchical configuration. This method

139

          * exists for backwards compatibility only. New code should use the

140

          * <code>{@link #getRootNode()}</code> method instead, which operates on

141

          * the preferred data type <code>ConfigurationNode</code>.

142

          *

143

          * @return the root node

144

          */

145

         public Node getRoot()

146

         {

147

720

             return root;

148

         }

149

150

         /**

151

          * Sets the root node of this hierarchical configuration. This method

152

          * exists for backwards compatibility only. New code should use the

153

          * <code>{@link #setRootNode(ConfigurationNode)}</code> method instead,

154

          * which operates on the preferred data type <code>ConfigurationNode</code>.

155

          *

156

          * @param node the root node

157

          */

158

         public void setRoot(Node node)

159

         {

160

16

             if (node == null)

161

             {

162

1

                 throw new IllegalArgumentException("Root node must not be null!");

163

             }

164

15

             root = node;

165

15

             rootNode = null;

166

15

         }

167

168

         /**

169

          * Returns the root node of this hierarchical configuration.

170

          *

171

          * @return the root node

172

          * @since 1.3

173

          */

174

         public ConfigurationNode getRootNode()

175

         {

176

5058

             return (rootNode != null) ? rootNode : root;

177

         }

178

179

         /**

180

          * Sets the root node of this hierarchical configuration.

181

          *

182

          * @param rootNode the root node

183

          * @since 1.3

184

          */

185

         public void setRootNode(ConfigurationNode rootNode)

186

         {

187

1024

             if (rootNode == null)

188

             {

189

1

                 throw new IllegalArgumentException("Root node must not be null!");

190

             }

191

1023

             this.rootNode = rootNode;

192

193

             // For backward compatibility also set the old root field.

194

1023

             root = (rootNode instanceof Node) ? (Node) rootNode : new Node(rootNode);

195

1023

         }

196

197

         /**

198

          * Returns the default expression engine.

199

          *

200

          * @return the default expression engine

201

          * @since 1.3

202

          */

203

         public static ExpressionEngine getDefaultExpressionEngine()

204

         {

205

5446

             return defaultExpressionEngine;

206

         }

207

208

         /**

209

          * Sets the default expression engine. This expression engine will be used

210

          * if no specific engine was set for an instance. It is shared between all

211

          * hierarchical configuration instances. So modifying its properties will

212

          * impact all instances, for which no specific engine is set.

213

          *

214

          * @param engine the new default expression engine

215

          * @since 1.3

216

          */

217

         public static void setDefaultExpressionEngine(ExpressionEngine engine)

218

         {

219

3

             if (engine == null)

220

             {

221

1

                 throw new IllegalArgumentException(

222

                         "Default expression engine must not be null!");

223

             }

224

2

             defaultExpressionEngine = engine;

225

2

         }

226

227

         /**

228

          * Returns the expression engine used by this configuration. This method

229

          * will never return <b>null</b>; if no specific expression engine was set,

230

          * the default expression engine will be returned.

231

          *

232

          * @return the current expression engine

233

          * @since 1.3

234

          */

235

         public ExpressionEngine getExpressionEngine()

236

         {

237

5926

             return (expressionEngine != null) ? expressionEngine

238

                     : getDefaultExpressionEngine();

239

         }

240

241

         /**

242

          * Sets the expression engine to be used by this configuration. All property

243

          * keys this configuration has to deal with will be interpreted by this

244

          * engine.

245

          *

246

          * @param expressionEngine the new expression engine; can be <b>null</b>,

247

          * then the default expression engine will be used

248

          * @since 1.3

249

          */

250

         public void setExpressionEngine(ExpressionEngine expressionEngine)

251

         {

252

336

             this.expressionEngine = expressionEngine;

253

336

         }

254

255

         /**

256

          * Fetches the specified property. This task is delegated to the associated

257

          * expression engine.

258

          *

259

          * @param key the key to be looked up

260

          * @return the found value

261

          */

262

         public Object getProperty(String key)

263

         {

264

1471

             List nodes = fetchNodeList(key);

265

266

1471

             if (nodes.size() == 0)

267

             {

268

259

                 return null;

269

             }

270

             else

271

             {

272

1212

                 List list = new ArrayList();

273

4511

                 for (Iterator it = nodes.iterator(); it.hasNext();)

274

                 {

275

2087

                     ConfigurationNode node = (ConfigurationNode) it.next();

276

2087

                     if (node.getValue() != null)

277

                     {

278

1931

                         list.add(node.getValue());

279

                     }

280

                 }

281

282

1212

                 if (list.size() < 1)

283

                 {

284

21

                     return null;

285

                 }

286

                 else

287

                 {

288

1191

                     return (list.size() == 1) ? list.get(0) : list;

289

                 }

290

             }

291

         }

292

293

         /**

294

          * Adds the property with the specified key. This task will be delegated to

295

          * the associated <code>ExpressionEngine</code>, so the passed in key

296

          * must match the requirements of this implementation.

297

          *

298

          * @param key the key of the new property

299

          * @param obj the value of the new property

300

          */

301

         protected void addPropertyDirect(String key, Object obj)

302

         {

303

1684

             NodeAddData data = getExpressionEngine().prepareAdd(getRootNode(), key);

304

1683

             ConfigurationNode node = processNodeAddData(data);

305

1683

             node.setValue(obj);

306

1683

         }

307

308

         /**

309

          * Adds a collection of nodes at the specified position of the configuration

310

          * tree. This method works similar to <code>addProperty()</code>, but

311

          * instead of a single property a whole collection of nodes can be added -

312

          * and thus complete configuration sub trees. E.g. with this method it is

313

          * possible to add parts of another <code>HierarchicalConfiguration</code>

314

          * object to this object. If the passed in key refers to an existing and

315

          * unique node, the new nodes are added to this node. Otherwise a new node

316

          * will be created at the specified position in the hierarchy.

317

          *

318

          * @param key the key where the nodes are to be added; can be <b>null </b>,

319

          * then they are added to the root node

320

          * @param nodes a collection with the <code>Node</code> objects to be

321

          * added

322

          */

323

         public void addNodes(String key, Collection nodes)

324

         {

325

25

             if (nodes == null || nodes.isEmpty())

326

             {

327

4

                 return;

328

             }

329

330

21

             fireEvent(EVENT_ADD_NODES, key, nodes, true);

331

             ConfigurationNode parent;

332

21

             List target = fetchNodeList(key);

333

21

             if (target.size() == 1)

334

             {

335

                 // existing unique key

336

12

                 parent = (ConfigurationNode) target.get(0);

337

             }

338

             else

339

             {

340

                 // otherwise perform an add operation

341

9

                 parent = processNodeAddData(getExpressionEngine().prepareAdd(

342

                         getRootNode(), key));

343

             }

344

345

21

             if (parent.isAttribute())

346

             {

347

1

                 throw new IllegalArgumentException(

348

                         "Cannot add nodes to an attribute node!");

349

             }

350

88

             for (Iterator it = nodes.iterator(); it.hasNext();)

351

             {

352

48

                 ConfigurationNode child = (ConfigurationNode) it.next();

353

48

                 if (child.isAttribute())

354

                 {

355

1

                     parent.addAttribute(child);

356

                 }

357

                 else

358

                 {

359

47

                     parent.addChild(child);

360

                 }

361

             }

362

20

             fireEvent(EVENT_ADD_NODES, key, nodes, false);

363

20

         }

364

365

         /**

366

          * Checks if this configuration is empty. Empty means that there are no keys

367

          * with any values, though there can be some (empty) nodes.

368

          *

369

          * @return a flag if this configuration is empty

370

          */

371

         public boolean isEmpty()

372

         {

373

34

             return !nodeDefined(getRootNode());

374

         }

375

376

         /**

377

          * Creates a new <code>Configuration</code> object containing all keys

378

          * that start with the specified prefix. This implementation will return a

379

          * <code>HierarchicalConfiguration</code> object so that the structure of

380

          * the keys will be saved.

381

          *

382

          * @param prefix the prefix of the keys for the subset

383

          * @return a new configuration object representing the selected subset

384

          */

385

         public Configuration subset(String prefix)

386

         {

387

8

             Collection nodes = fetchNodeList(prefix);

388

8

             if (nodes.isEmpty())

389

             {

390

2

                 return new HierarchicalConfiguration();

391

             }

392

393

6

             HierarchicalConfiguration result = new HierarchicalConfiguration();

394

6

             CloneVisitor visitor = new CloneVisitor();

395

396

38

             for (Iterator it = nodes.iterator(); it.hasNext();)

397

             {

398

26

                 ConfigurationNode nd = (ConfigurationNode) it.next();

399

26

                 nd.visit(visitor);

400

401

83

                 for (Iterator it2 = visitor.getClone().getChildren().iterator(); it2.hasNext();)

402

                 {

403

31

                     result.getRootNode().addChild((ConfigurationNode) it2.next());

404

                 }

405

53

                 for (Iterator it2 = visitor.getClone().getAttributes().iterator(); it2.hasNext();)

406

                 {

407

1

                     result.getRootNode().addAttribute((ConfigurationNode) it2.next());

408

                 }

409

             }

410

411

6

             return (result.isEmpty()) ? new HierarchicalConfiguration() : result;

412

         }

413

414

         /**

415

          * <p>

416

          * Returns a hierarchical subnode configuration object that wraps the

417

          * configuration node specified by the given key. This method provides an

418

          * easy means of accessing sub trees of a hierarchical configuration. In the

419

          * returned configuration the sub tree can directly be accessed, it becomes

420

          * the root node of this configuration. Because of this the passed in key

421

          * must select exactly one configuration node; otherwise an

422

          * <code>IllegalArgumentException</code> will be thrown.

423

          * </p>

424

          * <p>

425

          * The difference between this method and the

426

          * <code>{@link #subset(String)}</code> method is that

427

          * <code>subset()</code> supports arbitrary subsets of configuration nodes

428

          * while <code>configurationAt()</code> only returns a single sub tree.

429

          * Please refer to the documentation of the

430

          * <code>SubnodeConfiguration</code> class to obtain further information

431

          * about subnode configurations and when they should be used.

432

          * </p>

433

          *

434

          * @param key the key that selects the sub tree

435

          * @return a hierarchical configuration that contains this sub tree

436

          * @see SubnodeConfiguration

437

          * @since 1.3

438

          */

439

         public SubnodeConfiguration configurationAt(String key)

440

         {

441

134

             List nodes = fetchNodeList(key);

442

134

             if (nodes.size() != 1)

443

             {

444

19

                 throw new IllegalArgumentException(

445

                         "Passed in key must select exactly one node: " + key);

446

             }

447

115

             return createSubnodeConfiguration((ConfigurationNode) nodes.get(0));

448

         }

449

450

         /**

451

          * Returns a list of sub configurations for all configuration nodes selected

452

          * by the given key. This method will evaluate the passed in key (using the

453

          * current <code>ExpressionEngine</code>) and then create a subnode

454

          * configuration for each returned node (like

455

          * <code>{@link #configurationAt(String)}</code>}). This is especially

456

          * useful when dealing with list-like structures. As an example consider the

457

          * configuration that contains data about database tables and their fields.

458

          * If you need access to all fields of a certain table, you can simply do

459

          *

460

          * <pre>

461

          * List fields = config.configurationsAt("tables.table(0).fields.field");

462

          * for(Iterator it = fields.iterator(); it.hasNext();)

463

          * {

464

          *     HierarchicalConfiguration sub = (HierarchicalConfiguration) it.next();

465

          *     // now the children and attributes of the field node can be

466

          *     // directly accessed

467

          *     String fieldName = sub.getString("name");

468

          *     String fieldType = sub.getString("type");

469

          *     ...

470

          * </pre>

471

          *

472

          * @param key the key for selecting the desired nodes

473

          * @return a list with hierarchical configuration objects; each

474

          * configuration represents one of the nodes selected by the passed in key

475

          * @since 1.3

476

          */

477

         public List configurationsAt(String key)

478

         {

479

52

             List nodes = fetchNodeList(key);

480

52

             List configs = new ArrayList(nodes.size());

481

166

             for (Iterator it = nodes.iterator(); it.hasNext();)

482

             {

483

62

                 configs.add(createSubnodeConfiguration((ConfigurationNode) it.next()));

484

             }

485

52

             return configs;

486

         }

487

488

         /**

489

          * Creates a subnode configuration for the specified node. This method is

490

          * called by <code>configurationAt()</code> and

491

          * <code>configurationsAt()</code>.

492

          *

493

          * @param node the node, for which a subnode configuration is to be created

494

          * @return the configuration for the given node

495

          * @since 1.3

496

          */

497

         protected SubnodeConfiguration createSubnodeConfiguration(ConfigurationNode node)

498

         {

499

102

             return new SubnodeConfiguration(this, node);

500

         }

501

502

         /**

503

          * Checks if the specified key is contained in this configuration. Note that

504

          * for this configuration the term &quot;contained&quot; means that the key

505

          * has an associated value. If there is a node for this key that has no

506

          * value but children (either defined or undefined), this method will still

507

          * return <b>false </b>.

508

          *

509

          * @param key the key to be chekced

510

          * @return a flag if this key is contained in this configuration

511

          */

512

         public boolean containsKey(String key)

513

         {

514

388

             return getProperty(key) != null;

515

         }

516

517

         /**

518

          * Sets the value of the specified property.

519

          *

520

          * @param key the key of the property to set

521

          * @param value the new value of this property

522

          */

523

         public void setProperty(String key, Object value)

524

         {

525

1129

             fireEvent(EVENT_SET_PROPERTY, key, value, true);

526

527

1129

             Iterator itNodes = fetchNodeList(key).iterator();

528

             Iterator itValues;

529

1129

             if (!isDelimiterParsingDisabled())

530

             {

531

1129

                 itValues = PropertyConverter.toIterator(value, getListDelimiter());

532

             }

533

             else

534

             {

535

0

                 itValues = new SingletonIterator(value);

536

             }

537

1176

             while (itNodes.hasNext() && itValues.hasNext())

538

             {

539

47

                 ((ConfigurationNode) itNodes.next()).setValue(itValues.next());

540

             }

541

542

             // Add additional nodes if necessary

543

2453

             while (itValues.hasNext())

544

             {

545

1323

                 addPropertyDirect(key, itValues.next());

546

             }

547

548

             // Remove remaining nodes

549

2263

             while (itNodes.hasNext())

550

             {

551

5

                 clearNode((ConfigurationNode) itNodes.next());

552

             }

553

554

1129

             fireEvent(EVENT_SET_PROPERTY, key, value, false);

555

1129

         }

556

557

         /**

558

          * Removes all values of the property with the given name and of keys that

559

          * start with this name. So if there is a property with the key

560

          * &quot;foo&quot; and a property with the key &quot;foo.bar&quot;, a call

561

          * of <code>clearTree("foo")</code> would remove both properties.

562

          *

563

          * @param key the key of the property to be removed

564

          */

565

         public void clearTree(String key)

566

         {

567

6

             fireEvent(EVENT_CLEAR_TREE, key, null, true);

568

6

             List nodes = fetchNodeList(key);

569

570

18

             for (Iterator it = nodes.iterator(); it.hasNext();)

571

             {

572

6

                 removeNode((ConfigurationNode) it.next());

573

             }

574

6

             fireEvent(EVENT_CLEAR_TREE, key, nodes, false);

575

6

         }

576

577

         /**

578

          * Removes the property with the given key. Properties with names that start

579

          * with the given key (i.e. properties below the specified key in the

580

          * hierarchy) won't be affected.

581

          *

582

          * @param key the key of the property to be removed

583

          */

584

         public void clearProperty(String key)

585

         {

586

206

             fireEvent(EVENT_CLEAR_PROPERTY, key, null, true);

587

206

             List nodes = fetchNodeList(key);

588

589

827

             for (Iterator it = nodes.iterator(); it.hasNext();)

590

             {

591

415

                 clearNode((ConfigurationNode) it.next());

592

             }

593

594

206

             fireEvent(EVENT_CLEAR_PROPERTY, key, null, false);

595

206

         }

596

597

         /**

598

          * Returns an iterator with all keys defined in this configuration.

599

          * Note that the keys returned by this method will not contain any

600

          * indices. This means that some structure will be lost.</p>

601

          *

602

          * @return an iterator with the defined keys in this configuration

603

          */

604

         public Iterator getKeys()

605

         {

606

38

             DefinedKeysVisitor visitor = new DefinedKeysVisitor();

607

38

             getRootNode().visit(visitor);

608

609

38

             return visitor.getKeyList().iterator();

610

         }

611

612

         /**

613

          * Returns an iterator with all keys defined in this configuration that

614

          * start with the given prefix. The returned keys will not contain any

615

          * indices.

616

          *

617

          * @param prefix the prefix of the keys to start with

618

          * @return an iterator with the found keys

619

          */

620

         public Iterator getKeys(String prefix)

621

         {

622

9

             DefinedKeysVisitor visitor = new DefinedKeysVisitor(prefix);

623

9

             List nodes = fetchNodeList(prefix);

624

625

29

             for (Iterator itNodes = nodes.iterator(); itNodes.hasNext();)

626

             {

627

11

                 ConfigurationNode node = (ConfigurationNode) itNodes.next();

628

47

                 for (Iterator it = node.getChildren().iterator(); it.hasNext();)

629

                 {

630

25

                     ((ConfigurationNode) it.next()).visit(visitor);

631

                 }

632

24

                 for (Iterator it = node.getAttributes().iterator(); it.hasNext();)

633

                 {

634

2

                     ((ConfigurationNode) it.next()).visit(visitor);

635

                 }

636

             }

637

638

9

             return visitor.getKeyList().iterator();

639

         }

640

641

         /**

642

          * Returns the maximum defined index for the given key. This is useful if

643

          * there are multiple values for this key. They can then be addressed

644

          * separately by specifying indices from 0 to the return value of this

645

          * method.

646

          *

647

          * @param key the key to be checked

648

          * @return the maximum defined index for this key

649

          */

650

         public int getMaxIndex(String key)

651

         {

652

68

             return fetchNodeList(key).size() - 1;

653

         }

654

655

         /**

656

          * Creates a copy of this object. This new configuration object will contain

657

          * copies of all nodes in the same structure. Registered event listeners

658

          * won't be cloned; so they are not registered at the returned copy.

659

          *

660

          * @return the copy

661

          * @since 1.2

662

          */

663

         public Object clone()

664

         {

665

             try

666

             {

667

9

                 HierarchicalConfiguration copy = (HierarchicalConfiguration) super

668

                         .clone();

669

670

                 // clone the nodes, too

671

9

                 CloneVisitor v = new CloneVisitor();

672

9

                 getRootNode().visit(v);

673

9

                 copy.setRootNode(v.getClone());

674

9

                 copy.clearConfigurationListeners();

675

676

9

                 return copy;

677

             }

678

             catch (CloneNotSupportedException cex)

679

             {

680

                 // should not happen

681

0

                 throw new ConfigurationRuntimeException(cex);

682

             }

683

         }

684

685

         /**

686

          * Helper method for fetching a list of all nodes that are addressed by the

687

          * specified key.

688

          *

689

          * @param key the key

690

          * @return a list with all affected nodes (never <b>null </b>)

691

          */

692

         protected List fetchNodeList(String key)

693

         {

694

3104

             return getExpressionEngine().query(getRootNode(), key);

695

         }

696

697

         /**

698

          * Recursive helper method for fetching a property. This method processes

699

          * all facets of a configuration key, traverses the tree of properties and

700

          * fetches the the nodes of all matching properties.

701

          *

702

          * @param keyPart the configuration key iterator

703

          * @param node the actual node

704

          * @param nodes here the found nodes are stored

705

          * @deprecated Property keys are now evaluated by the expression engine

706

          * associated with the configuration; this method will no longer be called.

707

          * If you want to modify the way properties are looked up, consider

708

          * implementing you own <code>ExpressionEngine</code> implementation.

709

          */

710

         protected void findPropertyNodes(ConfigurationKey.KeyIterator keyPart,

711

                 Node node, Collection nodes)

712

         {

713

0

         }

714

715

         /**

716

          * Checks if the specified node is defined.

717

          *

718

          * @param node the node to be checked

719

          * @return a flag if this node is defined

720

          * @deprecated Use the method <code>{@link #nodeDefined(ConfigurationNode)}</code>

721

          * instead.

722

          */

723

         protected boolean nodeDefined(Node node)

724

         {

725

0

             return nodeDefined((ConfigurationNode) node);

726

         }

727

728

         /**

729

          * Checks if the specified node is defined.

730

          *

731

          * @param node the node to be checked

732

          * @return a flag if this node is defined

733

          */

734

         protected boolean nodeDefined(ConfigurationNode node)

735

         {

736

936

             DefinedVisitor visitor = new DefinedVisitor();

737

936

             node.visit(visitor);

738

936

             return visitor.isDefined();

739

         }

740

741

         /**

742

          * Removes the specified node from this configuration. This method ensures

743

          * that parent nodes that become undefined by this operation are also

744

          * removed.

745

          *

746

          * @param node the node to be removed

747

          * @deprecated Use the method <code>{@link #removeNode(ConfigurationNode)}</code>

748

          * instead.

749

          */

750

         protected void removeNode(Node node)

751

         {

752

0

             removeNode((ConfigurationNode) node);

753

0

         }

754

755

         /**

756

          * Removes the specified node from this configuration. This method ensures

757

          * that parent nodes that become undefined by this operation are also

758

          * removed.

759

          *

760

          * @param node the node to be removed

761

          */

762

         protected void removeNode(ConfigurationNode node)

763

         {

764

498

             ConfigurationNode parent = node.getParentNode();

765

498

             if (parent != null)

766

             {

767

482

                 parent.removeChild(node);

768

482

                 if (!nodeDefined(parent))

769

                 {

770

184

                     removeNode(parent);

771

                 }

772

             }

773

498

         }

774

775

         /**

776

          * Clears the value of the specified node. If the node becomes undefined by

777

          * this operation, it is removed from the hierarchy.

778

          *

779

          * @param node the node to be cleard

780

          * @deprecated Use the method <code>{@link #clearNode(ConfigurationNode)}</code>

781

          * instead

782

          */

783

         protected void clearNode(Node node)

784

         {

785

0

             clearNode((ConfigurationNode) node);

786

0

         }

787

788

         /**

789

          * Clears the value of the specified node. If the node becomes undefined by

790

          * this operation, it is removed from the hierarchy.

791

          *

792

          * @param node the node to be cleard

793

          */

794

         protected void clearNode(ConfigurationNode node)

795

         {

796

420

             node.setValue(null);

797

420

             if (!nodeDefined(node))

798

             {

799

308

                 removeNode(node);

800

             }

801

420

         }

802

803

         /**

804

          * Returns a reference to the parent node of an add operation. Nodes for new

805

          * properties can be added as children of this node. If the path for the

806

          * specified key does not exist so far, it is created now.

807

          *

808

          * @param keyIt the iterator for the key of the new property

809

          * @param startNode the node to start the search with

810

          * @return the parent node for the add operation

811

          * @deprecated Adding new properties is now to a major part delegated to the

812

          * <code>ExpressionEngine</code> associated with this configuration instance.

813

          * This method will no longer be called. Developers who want to modify the

814

          * process of adding new properties should consider implementing their own

815

          * expression engine.

816

          */

817

         protected Node fetchAddNode(ConfigurationKey.KeyIterator keyIt, Node startNode)

818

         {

819

0

             return null;

820

         }

821

822

         /**

823

          * Finds the last existing node for an add operation. This method traverses

824

          * the configuration tree along the specified key. The last existing node on

825

          * this path is returned.

826

          *

827

          * @param keyIt the key iterator

828

          * @param node the actual node

829

          * @return the last existing node on the given path

830

          * @deprecated Adding new properties is now to a major part delegated to the

831

          * <code>ExpressionEngine</code> associated with this configuration instance.

832

          * This method will no longer be called. Developers who want to modify the

833

          * process of adding new properties should consider implementing their own

834

          * expression engine.

835

          */

836

         protected Node findLastPathNode(ConfigurationKey.KeyIterator keyIt, Node node)

837

         {

838

0

             return null;

839

         }

840

841

         /**

842

          * Creates the missing nodes for adding a new property. This method ensures

843

          * that there are corresponding nodes for all components of the specified

844

          * configuration key.

845

          *

846

          * @param keyIt the key iterator

847

          * @param root the base node of the path to be created

848

          * @return the last node of the path

849

          * @deprecated Adding new properties is now to a major part delegated to the

850

          * <code>ExpressionEngine</code> associated with this configuration instance.

851

          * This method will no longer be called. Developers who want to modify the

852

          * process of adding new properties should consider implementing their own

853

          * expression engine.

854

          */

855

         protected Node createAddPath(ConfigurationKey.KeyIterator keyIt, Node root)

856

         {

857

0

             return null;

858

         }

859

860

         /**

861

          * Creates a new <code>Node</code> object with the specified name. This

862

          * method can be overloaded in derived classes if a specific node type is

863

          * needed. This base implementation always returns a new object of the

864

          * <code>Node</code> class.

865

          *

866

          * @param name the name of the new node

867

          * @return the new node

868

          */

869

         protected Node createNode(String name)

870

         {

871

2371

             return new Node(name);

872

         }

873

874

         /**

875

          * Helper method for processing a node add data object obtained from the

876

          * expression engine. This method will create all new nodes.

877

          *

878

          * @param data the data object

879

          * @return the new node

880

          * @since 1.3

881

          */

882

         private ConfigurationNode processNodeAddData(NodeAddData data)

883

         {

884

1692

             ConfigurationNode node = data.getParent();

885

886

             // Create missing nodes on the path

887

4162

             for (Iterator it = data.getPathNodes().iterator(); it.hasNext();)

888

             {

889

778

                 ConfigurationNode child = createNode((String) it.next());

890

778

                 node.addChild(child);

891

778

                 node = child;

892

             }

893

894

             // Add new target node

895

1692

             ConfigurationNode child = createNode(data.getNewNodeName());

896

1692

             if (data.isAttribute())

897

             {

898

75

                 node.addAttribute(child);

899

             }

900

             else

901

             {

902

1617

                 node.addChild(child);

903

             }

904

1692

             return child;

905

         }

906

907

         /**

908

          * A data class for storing (hierarchical) property information. A property

909

          * can have a value and an arbitrary number of child properties. From version 1.3 on this class

910

          * is only a thin wrapper over the

911

          * <code>{@link org.apache.commons.configuration.tree.DefaultConfigurationNode DefaultconfigurationNode}</code>

912

          * class that exists mainly for the purpose of backwards compatibility.

913

          */

914

         public static class Node extends DefaultConfigurationNode implements Serializable

915

         {

916

             /**

917

              * The serial version UID.

918

              */

919

             private static final long serialVersionUID = -6357500633536941775L;

920

921

             /**

922

              * Creates a new instance of <code>Node</code>.

923

              */

924

             public Node()

925

             {

926

1389

                 super();

927

1389

             }

928

929

             /**

930

              * Creates a new instance of <code>Node</code> and sets the name.

931

              *

932

              * @param name the node's name

933

              */

934

             public Node(String name)

935

             {

936

14305

                 super(name);

937

14305

             }

938

939

             /**

940

              * Creates a new instance of <code>Node</code> and sets the name and the value.

941

              *

942

              * @param name the node's name

943

              * @param value the value

944

              */

945

             public Node(String name, Object value)

946

             {

947

19

                 super(name, value);

948

19

             }

949

950

             /**

951

              * Creates a new instance of <code>Node</code> based on the given

952

              * source node. All properties of the source node, including its

953

              * children and attributes, will be copied.

954

              *

955

              * @param src the node to be copied

956

              */

957

             public Node(ConfigurationNode src)

958

             {

959

18

                 this(src.getName(), src.getValue());

960

18

                 setReference(src.getReference());

961

89

                 for (Iterator it = src.getChildren().iterator(); it.hasNext();)

962

                 {

963

53

                     addChild((ConfigurationNode) it.next());

964

                 }

965

37

                 for (Iterator it = src.getAttributes().iterator(); it.hasNext();)

966

                 {

967

1

                     addAttribute((ConfigurationNode) it.next());

968

                 }

969

18

             }

970

971

             /**

972

              * Returns the parent of this node.

973

              *

974

              * @return this node's parent (can be <b>null</b>)

975

              */

976

             public Node getParent()

977

             {

978

97

                 return (Node) getParentNode();

979

             }

980

981

             /**

982

              * Sets the parent of this node.

983

              *

984

              * @param node the parent node

985

              */

986

             public void setParent(Node node)

987

             {

988

0

                 setParentNode(node);

989

0

             }

990

991

             /**

992

              * Adds the given node to the children of this node.

993

              *

994

              * @param node the child to be added

995

              */

996

             public void addChild(Node node)

997

             {

998

10191

                 addChild((ConfigurationNode) node);

999

10191

             }

1000

1001

             /**

1002

              * Returns a flag whether this node has child elements.

1003

              *

1004

              * @return <b>true</b> if there is a child node, <b>false</b> otherwise

1005

              */

1006

             public boolean hasChildren()

1007

             {

1008

3538

                 return getChildrenCount() > 0 || getAttributeCount() > 0;

1009

             }

1010

1011

             /**

1012

              * Removes the specified child from this node.

1013

              *

1014

              * @param child the child node to be removed

1015

              * @return a flag if the child could be found

1016

              */

1017

             public boolean remove(Node child)

1018

             {

1019

137

                 return child.isAttribute() ? removeAttribute(child) : removeChild(child);

1020

             }

1021

1022

             /**

1023

              * Removes all children with the given name.

1024

              *

1025

              * @param name the name of the children to be removed

1026

              * @return a flag if children with this name existed

1027

              */

1028

             public boolean remove(String name)

1029

             {

1030

5

                 boolean childrenRemoved = removeChild(name);

1031

5

                 boolean attrsRemoved = removeAttribute(name);

1032

5

                 return childrenRemoved || attrsRemoved;

1033

             }

1034

1035

             /**

1036

              * A generic method for traversing this node and all of its children.

1037

              * This method sends the passed in visitor to this node and all of its

1038

              * children.

1039

              *

1040

              * @param visitor the visitor

1041

              * @param key here a configuration key with the name of the root node of

1042

              * the iteration can be passed; if this key is not <b>null </b>, the

1043

              * full pathes to the visited nodes are builded and passed to the

1044

              * visitor's <code>visit()</code> methods

1045

              */

1046

             public void visit(NodeVisitor visitor, ConfigurationKey key)

1047

             {

1048

1035

                 int length = 0;

1049

1035

                 if (key != null)

1050

                 {

1051

0

                     length = key.length();

1052

0

                     if (getName() != null)

1053

                     {

1054

0

                         key

1055

                                 .append(StringUtils

1056

                                         .replace(

1057

                                                 isAttribute() ? ConfigurationKey

1058

                                                         .constructAttributeKey(getName())

1059

                                                         : getName(),

1060

                                                 String

1061

                                                         .valueOf(ConfigurationKey.PROPERTY_DELIMITER),

1062

                                                 ConfigurationKey.ESCAPED_DELIMITER));

1063

                     }

1064

                 }

1065

1066

1035

                 visitor.visitBeforeChildren(this, key);

1067

1068

1035

                 for (Iterator it = getChildren().iterator(); it.hasNext()

1069

1855

                         && !visitor.terminate();)

1070

                 {

1071

820

                     ((Node) it.next()).visit(visitor, key);

1072

                 }

1073

1035

                 for (Iterator it = getAttributes().iterator(); it.hasNext()

1074

1228

                         && !visitor.terminate();)

1075

                 {

1076

193

                     ((Node) it.next()).visit(visitor, key);

1077

                 }

1078

1079

1035

                 if (key != null)

1080

                 {

1081

0

                     key.setLength(length);

1082

                 }

1083

1035

                 visitor.visitAfterChildren(this, key);

1084

1035

             }

1085

         }

1086

1087

         /**

1088

          * <p>Definition of a visitor class for traversing a node and all of its

1089

          * children.</p><p>This class defines the interface of a visitor for

1090

          * <code>Node</code> objects and provides a default implementation. The

1091

          * method <code>visit()</code> of <code>Node</code> implements a generic

1092

          * iteration algorithm based on the <em>Visitor</em> pattern. By providing

1093

          * different implementations of visitors it is possible to collect different

1094

          * data during the iteration process.</p>

1095

          *

1096

          */

1097

22

         public static class NodeVisitor

1098

         {

1099

             /**

1100

              * Visits the specified node. This method is called during iteration for

1101

              * each node before its children have been visited.

1102

              *

1103

              * @param node the actual node

1104

              * @param key the key of this node (may be <b>null </b>)

1105

              */

1106

             public void visitBeforeChildren(Node node, ConfigurationKey key)

1107

             {

1108

28

             }

1109

1110

             /**

1111

              * Visits the specified node after its children have been processed.

1112

              * This gives a visitor the opportunity of collecting additional data

1113

              * after the child nodes have been visited.

1114

              *

1115

              * @param node the node to be visited

1116

              * @param key the key of this node (may be <b>null </b>)

1117

              */

1118

             public void visitAfterChildren(Node node, ConfigurationKey key)

1119

             {

1120

995

             }

1121

1122

             /**

1123

              * Returns a flag that indicates if iteration should be stopped. This

1124

              * method is called after each visited node. It can be useful for

1125

              * visitors that search a specific node. If this node is found, the

1126

              * whole process can be stopped. This base implementation always returns

1127

              * <b>false </b>.

1128

              *

1129

              * @return a flag if iteration should be stopped

1130

              */

1131

             public boolean terminate()

1132

             {

1133

974

                 return false;

1134

             }

1135

         }

1136

1137

         /**

1138

          * A specialized visitor that checks if a node is defined.

1139

          * &quot;Defined&quot; in this terms means that the node or at least one of

1140

          * its sub nodes is associated with a value.

1141

          *

1142

          */

1143

936

         static class DefinedVisitor extends ConfigurationNodeVisitorAdapter

1144

         {

1145

             /** Stores the defined flag. */

1146

             private boolean defined;

1147

1148

             /**

1149

              * Checks if iteration should be stopped. This can be done if the first

1150

              * defined node is found.

1151

              *

1152

              * @return a flag if iteration should be stopped

1153

              */

1154

             public boolean terminate()

1155

             {

1156

2738

                 return isDefined();

1157

             }

1158

1159

             /**

1160

              * Visits the node. Checks if a value is defined.

1161

              *

1162

              * @param node the actual node

1163

              */

1164

             public void visitBeforeChildren(ConfigurationNode node)

1165

             {

1166

1648

                 defined = node.getValue() != null;

1167

1648

             }

1168

1169

             /**

1170

              * Returns the defined flag.

1171

              *

1172

              * @return the defined flag

1173

              */

1174

             public boolean isDefined()

1175

             {

1176

3674

                 return defined;

1177

             }

1178

         }

1179

1180

         /**

1181

          * A specialized visitor that fills a list with keys that are defined in a

1182

          * node hierarchy.

1183

          */

1184

         class DefinedKeysVisitor extends ConfigurationNodeVisitorAdapter

1185

         {

1186

             /** Stores the list to be filled. */

1187

             private Set keyList;

1188

1189

             /** A stack with the keys of the already processed nodes. */

1190

             private Stack parentKeys;

1191

1192

             /**

1193

              * Default constructor.

1194

              */

1195

             public DefinedKeysVisitor()

1196

47

             {

1197

47

                 keyList = new ListOrderedSet();

1198

47

                 parentKeys = new Stack();

1199

47

             }

1200

1201

             /**

1202

              * Creates a new <code>DefinedKeysVisitor</code> instance and sets the

1203

              * prefix for the keys to fetch.

1204

              *

1205

              * @param prefix the prefix

1206

              */

1207

             public DefinedKeysVisitor(String prefix)

1208

             {

1209

9

                 this();

1210

9

                 parentKeys.push(prefix);

1211

9

             }

1212

1213

             /**

1214

              * Returns the list with all defined keys.

1215

              *

1216

              * @return the list with the defined keys

1217

              */

1218

             public Set getKeyList()

1219

             {

1220

47

                 return keyList;

1221

             }

1222

1223

             /**

1224

              * Visits the node after its children has been processed. Removes this

1225

              * node's key from the stack.

1226

              *

1227

              * @param node the node

1228

              */

1229

             public void visitAfterChildren(ConfigurationNode node)

1230

             {

1231

934

                 parentKeys.pop();

1232

934

             }

1233

1234

             /**

1235

              * Visits the specified node. If this node has a value, its key is added

1236

              * to the internal list.

1237

              *

1238

              * @param node the node to be visited

1239

              */

1240

             public void visitBeforeChildren(ConfigurationNode node)

1241

             {

1242

934

                 String parentKey = parentKeys.isEmpty() ? null

1243

                         : (String) parentKeys.peek();

1244

934

                 String key = getExpressionEngine().nodeKey(node, parentKey);

1245

934

                 parentKeys.push(key);

1246

934

                 if (node.getValue() != null)

1247

                 {

1248

650

                     keyList.add(key);

1249

                 }

1250

934

             }

1251

         }

1252

1253

         /**

1254

          * A specialized visitor that is able to create a deep copy of a node

1255

          * hierarchy.

1256

          */

1257

         static class CloneVisitor extends ConfigurationNodeVisitorAdapter

1258

         {

1259

             /** A stack with the actual object to be copied. */

1260

             private Stack copyStack;

1261

1262

             /** Stores the result of the clone process. */

1263

             private ConfigurationNode result;

1264

1265

             /**

1266

              * Creates a new instance of <code>CloneVisitor</code>.

1267

              */

1268

             public CloneVisitor()

1269

15

             {

1270

15

                 copyStack = new Stack();

1271

15

             }

1272

1273

             /**

1274

              * Visits the specified node after its children have been processed.

1275

              *

1276

              * @param node the node

1277

              */

1278

             public void visitAfterChildren(ConfigurationNode node)

1279

             {

1280

228

                 ConfigurationNode copy = (ConfigurationNode) copyStack.pop();

1281

228

                 if (copyStack.isEmpty())

1282

                 {

1283

35

                     result = copy;

1284

                 }

1285

228

             }

1286

1287

             /**

1288

              * Visits and copies the specified node.

1289

              *

1290

              * @param node the node

1291

              */

1292

             public void visitBeforeChildren(ConfigurationNode node)

1293

             {

1294

228

                 ConfigurationNode copy = (ConfigurationNode) node.clone();

1295

228

                 copy.setParentNode(null);

1296

1297

228

                 if (!copyStack.isEmpty())

1298

                 {

1299

193

                     if (node.isAttribute())

1300

                     {

1301

15

                         ((ConfigurationNode) copyStack.peek()).addAttribute(copy);

1302

                     }

1303

                     else

1304

                     {

1305

178

                         ((ConfigurationNode) copyStack.peek()).addChild(copy);

1306

                     }

1307

                 }

1308

1309

228

                 copyStack.push(copy);

1310

228

             }

1311

1312

             /**

1313

              * Returns the result of the clone process. This is the root node of the

1314

              * cloned node hierarchy.

1315

              *

1316

              * @return the cloned root node

1317

              */

1318

             public ConfigurationNode getClone()

1319

             {

1320

61

                 return result;

1321

             }

1322

         }

1323

1324

         /**

1325

          * A specialized visitor base class that can be used for storing the tree of

1326

          * configuration nodes. The basic idea is that each node can be associated

1327

          * with a reference object. This reference object has a concrete meaning in

1328

          * a derived class, e.g. an entry in a JNDI context or an XML element. When

1329

          * the configuration tree is set up, the <code>load()</code> method is

1330

          * responsible for setting the reference objects. When the configuration

1331

          * tree is later modified, new nodes do not have a defined reference object.

1332

          * This visitor class processes all nodes and finds the ones without a

1333

          * defined reference object. For those nodes the <code>insert()</code>

1334

          * method is called, which must be defined in concrete sub classes. This

1335

          * method can perform all steps to integrate the new node into the original

1336

          * structure.

1337

          *

1338

          */

1339

19

         protected abstract static class BuilderVisitor extends NodeVisitor

1340

         {

1341

             /**

1342

              * Visits the specified node before its children have been traversed.

1343

              *

1344

              * @param node the node to visit

1345

              * @param key the current key

1346

              */

1347

             public void visitBeforeChildren(Node node, ConfigurationKey key)

1348

             {

1349

905

                 Collection subNodes = new LinkedList(node.getChildren());

1350

905

                 subNodes.addAll(node.getAttributes());

1351

905

                 Iterator children = subNodes.iterator();

1352

905

                 Node sibling1 = null;

1353

905

                 Node nd = null;

1354

1355

2192

                 while (children.hasNext())

1356

                 {

1357

                     // find the next new node

1358

                     do

1359

                     {

1360

821

                         sibling1 = nd;

1361

821

                         nd = (Node) children.next();

1362

821

                     } while (nd.getReference() != null && children.hasNext());

1363

1364

382

                     if (nd.getReference() == null)

1365

                     {

1366

                         // find all following new nodes

1367

43

                         List newNodes = new LinkedList();

1368

43

                         newNodes.add(nd);

1369

141

                         while (children.hasNext())

1370

                         {

1371

65

                             nd = (Node) children.next();

1372

65

                             if (nd.getReference() == null)

1373

                             {

1374

55

                                 newNodes.add(nd);

1375

                             }

1376

                             else

1377

                             {

1378

                                 break;

1379

                             }

1380

                         }

1381

1382

                         // Insert all new nodes

1383

43

                         Node sibling2 = (nd.getReference() == null) ? null : nd;

1384

184

                         for (Iterator it = newNodes.iterator(); it.hasNext();)

1385

                         {

1386

98

                             Node insertNode = (Node) it.next();

1387

98

                             if (insertNode.getReference() == null)

1388

                             {

1389

94

                                 Object ref = insert(insertNode, node, sibling1, sibling2);

1390

94

                                 if (ref != null)

1391

                                 {

1392

89

                                     insertNode.setReference(ref);

1393

                                 }

1394

94

                                 sibling1 = insertNode;

1395

                             }

1396

                         }

1397

                     }

1398

                 }

1399

905

             }

1400

1401

             /**

1402

              * Inserts a new node into the structure constructed by this builder.

1403

              * This method is called for each node that has been added to the

1404

              * configuration tree after the configuration has been loaded from its

1405

              * source. These new nodes have to be inserted into the original

1406

              * structure. The passed in nodes define the position of the node to be

1407

              * inserted: its parent and the siblings between to insert. The return

1408

              * value is interpreted as the new reference of the affected

1409

              * <code>Node</code> object; if it is not <b>null </b>, it is passed

1410

              * to the node's <code>setReference()</code> method.

1411

              *

1412

              * @param newNode the node to be inserted

1413

              * @param parent the parent node

1414

              * @param sibling1 the sibling after which the node is to be inserted;

1415

              * can be <b>null </b> if the new node is going to be the first child

1416

              * node

1417

              * @param sibling2 the sibling before which the node is to be inserted;

1418

              * can be <b>null </b> if the new node is going to be the last child

1419

              * node

1420

              * @return the reference object for the node to be inserted

1421

              */

1422

             protected abstract Object insert(Node newNode, Node parent, Node sibling1, Node sibling2);

1423

         }

1424

     }