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

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

UnionCombiner

100% | 21/21

100% | 5/5

4.0;4

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.Iterator;

20

     import java.util.LinkedList;

21

     import java.util.List;

22

23

     /**

24

      * <p>

25

      * A specialized implementation of the <code>NodeCombiner</code> interface

26

      * that constructs a union from two passed in node hierarchies.

27

      * </p>

28

      * <p>

29

      * The given source hierarchies are traversed and their nodes are added to the

30

      * resulting structure. Under some circumstances two nodes can be combined

31

      * rather than adding both. This is the case if both nodes are single children

32

      * (no lists) of their parents and do not have values. The corresponding check

33

      * is implemented in the <code>findCombineNode()</code> method.

34

      * </p>

35

      * <p>

36

      * Sometimes it is not possible for this combiner to detect whether two nodes

37

      * can be combined or not. Consider the following two node hierarchies:

38

      * </p>

39

      * <p>

40

      *

41

      * <pre>

42

      * Hierarchy 1:

43

      *

44

      * Database

45

      *   +--Tables

46

      *        +--Table

47

      *             +--name [users]

48

      *             +--fields

49

      *                   +--field

50

      *                   |    +--name [uid]

51

      *                   +--field

52

      *                   |    +--name [usrname]

53

      *                     ...

54

      * </pre>

55

      *

56

      * </p>

57

      * <p>

58

      *

59

      * <pre>

60

      * Hierarchy 2:

61

      *

62

      * Database

63

      *   +--Tables

64

      *        +--Table

65

      *             +--name [documents]

66

      *             +--fields

67

      *                   +--field

68

      *                   |    +--name [docid]

69

      *                   +--field

70

      *                   |    +--name [docname]

71

      *                     ...

72

      * </pre>

73

      *

74

      * </p>

75

      * <p>

76

      * Both hierarchies contain data about database tables. Each describes a single

77

      * table. If these hierarchies are to be combined, the result should probably

78

      * look like the following:

79

      * <p>

80

      *

81

      * <pre>

82

      * Database

83

      *   +--Tables

84

      *        +--Table

85

      *        |    +--name [users]

86

      *        |    +--fields

87

      *        |          +--field

88

      *        |          |    +--name [uid]

89

      *        |            ...

90

      *        +--Table

91

      *             +--name [documents]

92

      *             +--fields

93

      *                   +--field

94

      *                   |    +--name [docid]

95

      *                     ...

96

      * </pre>

97

      *

98

      * </p>

99

      * <p>

100

      * i.e. the <code>Tables</code> nodes should be combined, while the

101

      * <code>Table</code> nodes should both be added to the resulting tree. From

102

      * the combiner's point of view there is no difference between the

103

      * <code>Tables</code> and the <code>Table</code> nodes in the source trees,

104

      * so the developer has to help out and give a hint that the <code>Table</code>

105

      * nodes belong to a list structure. This can be done using the

106

      * <code>addListNode()</code> method; this method expects the name of a node,

107

      * which should be treated as a list node. So if

108

      * <code>addListNode("Table");</code> was called, the combiner knows that it

109

      * must not combine the <code>Table</code> nodes, but add it both to the

110

      * resulting tree.

111

      * </p>

112

      *

113

      * @author <a

114

      * href="http://jakarta.apache.org/commons/configuration/team-list.html">Commons

115

      * Configuration team</a>

116

      * @version $Id: UnionCombiner.java 439648 2006-09-02 20:42:10Z oheger $

117

      * @since 1.3

118

      */

119

16

     public class UnionCombiner extends NodeCombiner

120

     {

121

         /**

122

          * Combines the given nodes to a new union node.

123

          *

124

          * @param node1 the first source node

125

          * @param node2 the second source node

126

          * @return the union node

127

          */

128

         public ConfigurationNode combine(ConfigurationNode node1,

129

                 ConfigurationNode node2)

130

         {

131

83

             ViewNode result = createViewNode();

132

83

             result.setName(node1.getName());

133

83

             result.appendAttributes(node1);

134

83

             result.appendAttributes(node2);

135

136

             // Check if nodes can be combined

137

83

             List children2 = new LinkedList(node2.getChildren());

138

399

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

139

             {

140

233

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

141

233

                 ConfigurationNode child2 = findCombineNode(node1, node2, child1,

142

                         children2);

143

233

                 if (child2 != null)

144

                 {

145

65

                     result.addChild(combine(child1, child2));

146

65

                     children2.remove(child2);

147

                 }

148

                 else

149

                 {

150

168

                     result.addChild(child1);

151

                 }

152

             }

153

154

             // Add remaining children of node 2

155

251

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

156

             {

157

85

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

158

             }

159

160

83

             return result;

161

         }

162

163

         /**

164

          * <p>

165

          * Tries to find a child node of the second source node, with whitch a child

166

          * of the first source node can be combined. During combining of the source

167

          * nodes an iteration over the first source node's children is performed.

168

          * For each child node it is checked whether a corresponding child node in

169

          * the second source node exists. If this is the case, these corresponsing

170

          * child nodes are recursively combined and the result is added to the

171

          * combined node. This method implements the checks whether such a recursive

172

          * combination is possible. The actual implementation tests the following

173

          * conditions:

174

          * </p>

175

          * <p>

176

          * <ul>

177

          * <li>In both the first and the second source node there is only one child

178

          * node with the given name (no list structures).</li>

179

          * <li>The given name is not in the list of known list nodes, i.e. it was

180

          * not passed to the <code>addListNode()</code> method.</li>

181

          * <li>None of these matching child nodes has a value.</li>

182

          * </ul>

183

          * </p>

184

          * <p>

185

          * If all of these tests are successfull, the matching child node of the

186

          * second source node is returned. Otherwise the result is <b>null</b>.

187

          * </p>

188

          *

189

          * @param node1 the first source node

190

          * @param node2 the second source node

191

          * @param child the child node of the first source node to be checked

192

          * @param children a list with all children of the second source node

193

          * @return the matching child node of the second source node or <b>null</b>

194

          * if there is none

195

          */

196

         protected ConfigurationNode findCombineNode(ConfigurationNode node1,

197

                 ConfigurationNode node2, ConfigurationNode child, List children)

198

         {

199

233

             if (child.getValue() == null && !isListNode(child)

200

                     && node1.getChildrenCount(child.getName()) == 1

201

                     && node2.getChildrenCount(child.getName()) == 1)

202

             {

203

71

                 ConfigurationNode child2 = (ConfigurationNode) node2.getChildren(

204

                         child.getName()).iterator().next();

205

71

                 if (child2.getValue() == null)

206

                 {

207

65

                     return child2;

208

                 }

209

             }

210

168

             return null;

211

         }

212

     }