File D:\maven-qcr-plugin-testsunitaires\qcr4java\jenkins-master\target\site\qc\core\src\main\java\jenkins\util\TreeString.java.html

1 /*
2 * The MIT License
3 *
4 * Copyright (c) 2012, CloudBees, Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24 package jenkins.util;
25
26 import java.io.Serializable;
27 import java.util.Map;
28
29 import org.apache.commons.lang.StringUtils;
30
31 import com.thoughtworks.xstream.XStream;
32 import com.thoughtworks.xstream.converters.Converter;
33 import com.thoughtworks.xstream.converters.MarshallingContext;
34 import com.thoughtworks.xstream.converters.UnmarshallingContext;
35 import com.thoughtworks.xstream.io.HierarchicalStreamReader;
36 import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
37
38 /**
39 * {@link TreeString} is an alternative string representation that saves the
40 * memory when you have a large number of strings that share common prefixes
41 * (such as various file names.)
42 * <p>
43 * {@link TreeString} can be built with {@link TreeStringBuilder}.
44 *
45 * @author Kohsuke Kawaguchi
46 * @since 1.473
47 */
48 // CHECKSTYLE:OFF
49 @SuppressWarnings("PMD")
50 public final class TreeString implements Serializable {
51 private static final long serialVersionUID = 3621959682117480904L;
52
53 /**
54 * Parent node that represents the prefix.
55 */
56 private TreeString parent;
57
58 /**
59 * {@link #parent}+{@link #label} is the string value of this node.
60 */
61 private char[] label;
62
63 /**
64 * Creates a new root {@link TreeString}
65 */
66 /* package */TreeString() {
67 this(null, "");
68 }
69
70 /* package */TreeString(final TreeString parent, final String label) {
71 assert parent == null || label.length() > 0; // if there's a parent,
72 // label can't be empty.
73
74 this.parent = parent;
75 this.label = label.toCharArray(); // string created as a substring of
76 // another string can have a lot of
77 // garbage attached to it.
78 }
79
80 /* package */String getLabel() {
81 return new String(label);
82 }
83
84 /**
85 * Inserts a new node between this node and its parent, and returns the
86 * newly inserted node.
87 * <p>
88 * This operation doesn't change the string representation of this node.
89 */
90 /* package */TreeString split(final String prefix) {
91 assert getLabel().startsWith(prefix);
92 char[] suffix = new char[label.length - prefix.length()];
93 System.arraycopy(label, prefix.length(), suffix, 0, suffix.length);
94
95 TreeString middle = new TreeString(parent, prefix);
96 label = suffix;
97 parent = middle;
98
99 return middle;
100 }
101
102 /**
103 * How many nodes do we have from the root to this node (including 'this'
104 * itself?) Thus depth of the root node is 1.
105 */
106 private int depth() {
107 int i = 0;
108 for (TreeString p = this; p != null; p = p.parent) {
109 i++;
110 }
111 return i;
112 }
113
114 @Override
115 public boolean equals(final Object rhs) {
116 if (rhs == null) {
117 return false;
118 }
119 return rhs.getClass() == TreeString.class
120 && ((TreeString)rhs).getLabel().equals(getLabel());
121 }
122
123 @Override
124 public int hashCode() {
125 int h = parent == null ? 0 : parent.hashCode();
126
127 for (int i = 0; i < label.length; i++) {
128 h = 31 * h + label[i];
129 }
130
131 assert toString().hashCode() == h;
132 return h;
133 }
134
135 /**
136 * Returns the full string representation.
137 */
138 @Override
139 public String toString() {
140 char[][] tokens = new char[depth()][];
141 int i = tokens.length;
142 int sz = 0;
143 for (TreeString p = this; p != null; p = p.parent) {
144 tokens[--i] = p.label;
145 sz += p.label.length;
146 }
147
148 StringBuilder buf = new StringBuilder(sz);
149 for (char[] token : tokens) {
150 buf.append(token);
151 }
152
153 return buf.toString();
154 }
155
156 /**
157 * Interns {@link #label}
158 */
159 /* package */void dedup(final Map<String, char[]> table) {
160 String l = getLabel();
161 char[] v = table.get(l);
162 if (v != null) {
163 label = v;
164 }
165 else {
166 table.put(l, label);
167 }
168 }
169
170 public boolean isBlank() {
171 return StringUtils.isBlank(toString());
172 }
173
174 public static String toString(final TreeString t) {
175 return t == null ? null : t.toString();
176 }
177
178 /**
179 * Creates a {@link TreeString}. Useful if you need to create one-off
180 * {@link TreeString} without {@link TreeStringBuilder}. Memory consumption
181 * is still about the same to {@code new String(s)}.
182 *
183 * @return null if the parameter is null
184 */
185 public static TreeString of(final String s) {
186 if (s == null) {
187 return null;
188 }
189 return new TreeString(null, s);
190 }
191
192 /**
193 * Default {@link Converter} implementation for XStream that does interning
194 * scoped to one unmarshalling.
195 */
196 @SuppressWarnings("all")
197 public static final class ConverterImpl implements Converter {
198 public ConverterImpl(final XStream xs) {}
199
200 public void marshal(final Object source, final HierarchicalStreamWriter writer,
201 final MarshallingContext context) {
202 writer.setValue(source == null ? null : source.toString());
203 }
204
205 public Object unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) {
206 TreeStringBuilder builder = (TreeStringBuilder)context.get(TreeStringBuilder.class);
207 if (builder == null) {
208 context.put(TreeStringBuilder.class, builder = new TreeStringBuilder());
209
210 // dedup at the end
211 final TreeStringBuilder _builder = builder;
212 context.addCompletionCallback(new Runnable() {
213 public void run() {
214 _builder.dedup();
215 }
216 }, 0);
217 }
218 return builder.intern(reader.getValue());
219 }
220
221 public boolean canConvert(final Class type) {
222 return type == TreeString.class;
223 }
224 }
225 }
226