##### Coverage Report - org.apache.commons.configuration.tree.DefaultConfigurationNode

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

DefaultConfigurationNode

99% | 72/73

100% | 4/4

1.5833333333333333;1,583

DefaultConfigurationNode$SubNodes

100% | 58/58

100% | 18/18

1.5833333333333333;1,583

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

     package org.apache.commons.configuration.tree;

18

19

     import java.util.ArrayList;

20

     import java.util.Collection;

21

     import java.util.Collections;

22

     import java.util.HashMap;

23

     import java.util.Iterator;

24

     import java.util.LinkedList;

25

     import java.util.List;

26

     import java.util.Map;

27

28

     /**

29

      * <p>

30

      * A default implementation of the <code>ConfigurationNode</code> interface.

31

      * </p>

32

      *

33

      * @since 1.3

34

      * @author Oliver Heger

35

      */

36

     public class DefaultConfigurationNode implements ConfigurationNode, Cloneable

37

     {

38

         /** Stores the children of this node. */

39

         private SubNodes children;

40

41

         /** Stores the attributes of this node. */

42

         private SubNodes attributes;

43

44

         /** Stores a reference to this node's parent. */

45

         private ConfigurationNode parent;

46

47

         /** Stores the value of this node. */

48

         private Object value;

49

50

         /** Stores the reference. */

51

         private Object reference;

52

53

         /** Stores the name of this node. */

54

         private String name;

55

56

         /** Stores a flag if this is an attribute. */

57

         private boolean attribute;

58

59

         /**

60

          * Creates a new uninitialized instance of

61

          * <code>DefaultConfigurationNode</code>.

62

          */

63

         public DefaultConfigurationNode()

64

         {

65

1829

             this(null);

66

1829

         }

67

68

         /**

69

          * Creates a new instance of <code>DefaultConfigurationNode</code> and

70

          * initializes it with the node name.

71

          *

72

          * @param name the name of this node

73

          */

74

         public DefaultConfigurationNode(String name)

75

         {

76

16566

             this(name, null);

77

16566

         }

78

79

         /**

80

          * Creates a new instance of <code>DefaultConfigurationNode</code> and

81

          * initializes it with the name and a value.

82

          *

83

          * @param name the node's name

84

          * @param value the node's value

85

          */

86

         public DefaultConfigurationNode(String name, Object value)

87

243733

         {

88

243733

             setName(name);

89

243733

             setValue(value);

90

243733

             initSubNodes();

91

243733

         }

92

93

         /**

94

          * Returns the name of this node.

95

          *

96

          * @return the name of this node

97

          */

98

         public String getName()

99

         {

100

679346

             return name;

101

         }

102

103

         /**

104

          * Sets the name of this node.

105

          *

106

          * @param name the new name

107

          */

108

         public void setName(String name)

109

         {

110

244596

             checkState();

111

244596

             this.name = name;

112

244596

         }

113

114

         /**

115

          * Returns the value of this node.

116

          *

117

          * @return the value of this node

118

          */

119

         public Object getValue()

120

         {

121

70095

             return value;

122

         }

123

124

         /**

125

          * Sets the value of this node.

126

          *

127

          * @param val the value of this node

128

          */

129

         public void setValue(Object val)

130

         {

131

260969

             value = val;

132

260969

         }

133

134

         /**

135

          * Returns the reference.

136

          *

137

          * @return the reference

138

          */

139

         public Object getReference()

140

         {

141

28213

             return reference;

142

         }

143

144

         /**

145

          * Sets the reference.

146

          *

147

          * @param reference the reference object

148

          */

149

         public void setReference(Object reference)

150

         {

151

11452

             this.reference = reference;

152

11452

         }

153

154

         /**

155

          * Returns a reference to this node's parent.

156

          *

157

          * @return the parent node or <b>null </b> if this is the root

158

          */

159

         public ConfigurationNode getParentNode()

160

         {

161

491129

             return parent;

162

         }

163

164

         /**

165

          * Sets the parent of this node.

166

          *

167

          * @param parent the parent of this node

168

          */

169

         public void setParentNode(ConfigurationNode parent)

170

         {

171

490985

             this.parent = parent;

172

490985

         }

173

174

         /**

175

          * Adds a new child to this node.

176

          *

177

          * @param child the new child

178

          */

179

         public void addChild(ConfigurationNode child)

180

         {

181

128479

             children.addNode(child);

182

128477

             child.setAttribute(false);

183

128477

             child.setParentNode(this);

184

128477

         }

185

186

         /**

187

          * Returns a list with all children of this node.

188

          *

189

          * @return a list with all child nodes

190

          */

191

         public List getChildren()

192

         {

193

29037

             return children.getSubNodes();

194

         }

195

196

         /**

197

          * Returns the number of all children of this node.

198

          *

199

          * @return the number of all children

200

          */

201

         public int getChildrenCount()

202

         {

203

23119

             return children.getSubNodes().size();

204

         }

205

206

         /**

207

          * Returns a list of all children with the given name.

208

          *

209

          * @param name the name; can be <b>null </b>, then all children are returned

210

          * @return a list of all children with the given name

211

          */

212

         public List getChildren(String name)

213

         {

214

8711

             return children.getSubNodes(name);

215

         }

216

217

         /**

218

          * Returns the number of children with the given name.

219

          *

220

          * @param name the name; can be <b>null </b>, then the number of all

221

          * children is returned

222

          * @return the number of child nodes with this name

223

          */

224

         public int getChildrenCount(String name)

225

         {

226

6122

             return children.getSubNodes(name).size();

227

         }

228

229

         /**

230

          * Returns the child node with the given index.

231

          *

232

          * @param index the index (0-based)

233

          * @return the child with this index

234

          */

235

         public ConfigurationNode getChild(int index)

236

         {

237

3987

             return children.getNode(index);

238

         }

239

240

         /**

241

          * Removes the specified child node from this node.

242

          *

243

          * @param child the node to be removed

244

          * @return a flag if a node was removed

245

          */

246

         public boolean removeChild(ConfigurationNode child)

247

         {

248

623

             return children.removeNode(child);

249

         }

250

251

         /**

252

          * Removes all children with the given name.

253

          *

254

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

255

          * @return a flag if at least one child node was removed

256

          */

257

         public boolean removeChild(String childName)

258

         {

259

9

             return children.removeNodes(childName);

260

         }

261

262

         /**

263

          * Removes all child nodes of this node.

264

          */

265

         public void removeChildren()

266

         {

267

3

             children.clear();

268

3

         }

269

270

         /**

271

          * Checks if this node is an attribute node.

272

          *

273

          * @return a flag if this is an attribute node

274

          */

275

         public boolean isAttribute()

276

         {

277

12025

             return attribute;

278

         }

279

280

         /**

281

          * Sets the attribute flag. Note: this method can only be called if the node

282

          * is not already part of a node hierarchy.

283

          *

284

          * @param f the attribute flag

285

          */

286

         public void setAttribute(boolean f)

287

         {

288

244311

             checkState();

289

244310

             attribute = f;

290

244310

         }

291

292

         /**

293

          * Adds the specified attribute to this node.

294

          *

295

          * @param attr the attribute to be added

296

          */

297

         public void addAttribute(ConfigurationNode attr)

298

         {

299

115829

             attributes.addNode(attr);

300

115829

             attr.setAttribute(true);

301

115829

             attr.setParentNode(this);

302

115829

         }

303

304

         /**

305

          * Returns a list with the attributes of this node. This list contains

306

          * <code>ConfigurationNode</code> objects, too.

307

          *

308

          * @return the attribute list, never <b>null </b>

309

          */

310

         public List getAttributes()

311

         {

312

10498

             return attributes.getSubNodes();

313

         }

314

315

         /**

316

          * Returns the number of attributes contained in this node.

317

          *

318

          * @return the number of attributes

319

          */

320

         public int getAttributeCount()

321

         {

322

8644

             return attributes.getSubNodes().size();

323

         }

324

325

         /**

326

          * Returns a list with all attributes of this node with the given name.

327

          *

328

          * @param name the attribute's name

329

          * @return all attributes with this name

330

          */

331

         public List getAttributes(String name)

332

         {

333

5651

             return attributes.getSubNodes(name);

334

         }

335

336

         /**

337

          * Returns the number of attributes of this node with the given name.

338

          *

339

          * @param name the name

340

          * @return the number of attributes with this name

341

          */

342

         public int getAttributeCount(String name)

343

         {

344

39

             return getAttributes(name).size();

345

         }

346

347

         /**

348

          * Removes the specified attribute.

349

          *

350

          * @param node the attribute node to be removed

351

          * @return a flag if the attribute could be removed

352

          */

353

         public boolean removeAttribute(ConfigurationNode node)

354

         {

355

1

             return attributes.removeNode(node);

356

         }

357

358

         /**

359

          * Removes all attributes with the specified name.

360

          *

361

          * @param name the name

362

          * @return a flag if at least one attribute was removed

363

          */

364

         public boolean removeAttribute(String name)

365

         {

366

7

             return attributes.removeNodes(name);

367

         }

368

369

         /**

370

          * Returns the attribute with the given index.

371

          *

372

          * @param index the index (0-based)

373

          * @return the attribute with this index

374

          */

375

         public ConfigurationNode getAttribute(int index)

376

         {

377

3916

             return attributes.getNode(index);

378

         }

379

380

         /**

381

          * Removes all attributes of this node.

382

          */

383

         public void removeAttributes()

384

         {

385

1

             attributes.clear();

386

1

         }

387

388

         /**

389

          * Returns a flag if this node is defined. This means that the node contains

390

          * some data.

391

          *

392

          * @return a flag whether this node is defined

393

          */

394

         public boolean isDefined()

395

         {

396

1

             return getValue() != null || getChildrenCount() > 0

397

                     || getAttributeCount() > 0;

398

         }

399

400

         /**

401

          * Visits this node and all its sub nodes.

402

          *

403

          * @param visitor the visitor

404

          */

405

         public void visit(ConfigurationNodeVisitor visitor)

406

         {

407

2840

             if (visitor == null)

408

             {

409

1

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

410

             }

411

412

2839

             if (!visitor.terminate())

413

             {

414

2839

                 visitor.visitBeforeChildren(this);

415

2839

                 children.visit(visitor);

416

2839

                 attributes.visit(visitor);

417

2839

                 visitor.visitAfterChildren(this);

418

             } /* if */

419

2839

         }

420

421

         /**

422

          * Creates a copy of this object. This is not a deep copy, the children are

423

          * not cloned.

424

          *

425

          * @return a copy of this object

426

          */

427

         public Object clone()

428

         {

429

             try

430

             {

431

229

                 DefaultConfigurationNode copy = (DefaultConfigurationNode) super

432

                         .clone();

433

229

                 copy.initSubNodes();

434

229

                 return copy;

435

             }

436

             catch (CloneNotSupportedException cex)

437

             {

438

0

                 return null; // should not happen

439

             }

440

         }

441

442

         /**

443

          * Checks if a modification of this node is allowed. Some properties of a

444

          * node must not be changed when the node has a parent. This method checks

445

          * this and throws a runtime exception if necessary.

446

          */

447

         protected void checkState()

448

         {

449

488907

             if (getParentNode() != null)

450

             {

451

1

                 throw new IllegalStateException(

452

                         "Node cannot be modified when added to a parent!");

453

             } /* if */

454

488906

         }

455

456

         /**

457

          * Creates a <code>SubNodes</code> instance that is used for storing

458

          * either this node's children or attributes.

459

          *

460

          * @param attributes <b>true</b> if the returned instance is used for

461

          * storing attributes, <b>false</b> for storing child nodes

462

          * @return the <code>SubNodes</code> object to use

463

          */

464

         protected SubNodes createSubNodes(boolean attributes)

465

         {

466

487924

             return new SubNodes();

467

         }

468

469

         /**

470

          * Deals with the reference when a node is removed. This method is called

471

          * for each removed child node or attribute. It can be overloaded in sub

472

          * classes, for which the reference has a concrete meaning and remove

473

          * operations need some update actions. This default implementation is

474

          * empty.

475

          */

476

         protected void removeReference()

477

         {

478

49

         }

479

480

         /**

481

          * Helper method for initializing the sub nodes objects.

482

          */

483

         private void initSubNodes()

484

         {

485

243962

             children = createSubNodes(false);

486

243962

             attributes = createSubNodes(true);

487

243962

         }

488

489

         /**

490

          * An internally used helper class for managing a collection of sub nodes.

491

          */

492

487924

         protected static class SubNodes

493

         {

494

             /** Stores a list for the sub nodes. */

495

             private List nodes;

496

497

             /** Stores a map for accessing subnodes by name. */

498

             private Map namedNodes;

499

500

             /**

501

              * Adds a new sub node.

502

              *

503

              * @param node the node to add

504

              */

505

             public void addNode(ConfigurationNode node)

506

             {

507

244308

                 if (node == null || node.getName() == null)

508

                 {

509

2

                     throw new IllegalArgumentException(

510

                             "Node to add must have a defined name!");

511

                 }

512

244306

                 node.setParentNode(null);  // reset, will later be set

513

514

244306

                 if (nodes == null)

515

                 {

516

143492

                     nodes = new ArrayList();

517

143492

                     namedNodes = new HashMap();

518

                 }

519

520

244306

                 nodes.add(node);

521

244306

                 List lst = (List) namedNodes.get(node.getName());

522

244306

                 if (lst == null)

523

                 {

524

172421

                     lst = new LinkedList();

525

172421

                     namedNodes.put(node.getName(), lst);

526

                 }

527

244306

                 lst.add(node);

528

244306

             }

529

530

             /**

531

              * Removes a sub node.

532

              *

533

              * @param node the node to remove

534

              * @return a flag if the node could be removed

535

              */

536

             public boolean removeNode(ConfigurationNode node)

537

             {

538

624

                 if (nodes != null && node != null && nodes.contains(node))

539

                 {

540

520

                     detachNode(node);

541

520

                     nodes.remove(node);

542

543

520

                     List lst = (List) namedNodes.get(node.getName());

544

520

                     if (lst != null)

545

                     {

546

520

                         lst.remove(node);

547

520

                         if (lst.isEmpty())

548

                         {

549

382

                             namedNodes.remove(node.getName());

550

                         }

551

                     }

552

520

                     return true;

553

                 }

554

555

                 else

556

                 {

557

104

                     return false;

558

                 }

559

             }

560

561

             /**

562

              * Removes all sub nodes with the given name.

563

              *

564

              * @param name the name

565

              * @return a flag if at least on sub node was removed

566

              */

567

             public boolean removeNodes(String name)

568

             {

569

16

                 if (nodes != null && name != null)

570

                 {

571

9

                     List lst = (List) namedNodes.remove(name);

572

9

                     if (lst != null)

573

                     {

574

7

                         detachNodes(lst);

575

7

                         nodes.removeAll(lst);

576

7

                         return true;

577

                     }

578

                 }

579

9

                 return false;

580

             }

581

582

             /**

583

              * Removes all sub nodes.

584

              */

585

             public void clear()

586

             {

587

4

                 if (nodes != null)

588

                 {

589

3

                     detachNodes(nodes);

590

3

                     nodes = null;

591

3

                     namedNodes = null;

592

                 }

593

4

             }

594

595

             /**

596

              * Returns the node with the given index. If this index cannot be found,

597

              * an <code>IndexOutOfBoundException</code> exception will be thrown.

598

              *

599

              * @param index the index (0-based)

600

              * @return the sub node at the specified index

601

              */

602

             public ConfigurationNode getNode(int index)

603

             {

604

7903

                 if (nodes == null)

605

                 {

606

1

                     throw new IndexOutOfBoundsException("No sub nodes available!");

607

                 }

608

7902

                 return (ConfigurationNode) nodes.get(index);

609

             }

610

611

             /**

612

              * Returns a list with all stored sub nodes. The return value is never

613

              * <b>null</b>.

614

              *

615

              * @return a list with the sub nodes

616

              */

617

             public List getSubNodes()

618

             {

619

71299

                 return (nodes == null) ? Collections.EMPTY_LIST : Collections

620

                         .unmodifiableList(nodes);

621

             }

622

623

             /**

624

              * Returns a list of the sub nodes with the given name. The return value

625

              * is never <b>null</b>.

626

              *

627

              * @param name the name; if <b>null</b> is passed, all sub nodes will

628

              * be returned

629

              * @return all sub nodes with this name

630

              */

631

             public List getSubNodes(String name)

632

             {

633

20484

                 if (name == null)

634

                 {

635

1

                     return getSubNodes();

636

                 }

637

638

                 List result;

639

20483

                 if (nodes == null)

640

                 {

641

608

                     result = null;

642

                 }

643

                 else

644

                 {

645

19875

                     result = (List) namedNodes.get(name);

646

                 }

647

648

20483

                 return (result == null) ? Collections.EMPTY_LIST : Collections

649

                         .unmodifiableList(result);

650

             }

651

652

             /**

653

              * Let the passed in visitor visit all sub nodes.

654

              *

655

              * @param visitor the visitor

656

              */

657

             public void visit(ConfigurationNodeVisitor visitor)

658

             {

659

5678

                 if (nodes != null)

660

                 {

661

1398

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

662

3199

                             && !visitor.terminate();)

663

                     {

664

1801

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

665

                     }

666

                 }

667

5678

             }

668

669

             /**

670

              * This method is called whenever a sub node is removed from this

671

              * object. It ensures that the removed node's parent is reset and its

672

              * <code>removeReference()</code> method gets called.

673

              *

674

              * @param subNode the node to be removed

675

              */

676

             protected void detachNode(ConfigurationNode subNode)

677

             {

678

545

                 subNode.setParentNode(null);

679

545

                 if (subNode instanceof DefaultConfigurationNode)

680

                 {

681

545

                     ((DefaultConfigurationNode) subNode).removeReference();

682

                 }

683

545

             }

684

685

             /**

686

              * Detaches a list of sub nodes. This method calls

687

              * <code>detachNode()</code> for each node contained in the list.

688

              *

689

              * @param subNodes the list with nodes to be detached

690

              */

691

             protected void detachNodes(Collection subNodes)

692

             {

693

45

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

694

                 {

695

25

                     detachNode((ConfigurationNode) it.next());

696

                 }

697

10

             }

698

         }

699

     }