~ubuntu-branches/ubuntu/saucy/jenkins/saucy

« back to all changes in this revision

Viewing changes to core/src/main/java/jenkins/util/TreeString.java

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2013-01-10 09:50:50 UTC
  • mfrom: (5.1.10 experimental)
  • Revision ID: package-import@ubuntu.com-20130110095050-kj8xuw20gcfh62k3
Tags: 1.480.2+dfsg-1~exp1
* New upstream release (Closes: #696816, #697617):
  - d/control: Added new BD on libjbcrypt-java.
  - d/control: Versioned BD jenkins-winstone >= 0.9.10-jenkins-40.
  - d/control: Versioned BD jenkins-trilead-ssh2 >= 214-jenkins-1.
  - Fixes the following security vulnerabilities:
    CVE-2012-6072, CVE-2012-6073, CVE-2012-6072, CVE-2013-0158.
* Tidied lintian warnings.
* Bumped Standards-Version: 3.9.4, no changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
}