File D:\maven-qcr-plugin-testsunitaires\qcr4java\jenkins-master\target\site\qc\core\src\main\java\hudson\model\labels\LabelAtom.java.html

1 /*
2 * The MIT License
3 *
4 * Copyright (c) 2010, InfraDNA, 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 hudson.model.labels;
25
26 import com.thoughtworks.xstream.converters.MarshallingContext;
27 import com.thoughtworks.xstream.converters.UnmarshallingContext;
28 import com.thoughtworks.xstream.io.HierarchicalStreamReader;
29 import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
30 import hudson.BulkChange;
31 import hudson.CopyOnWrite;
32 import hudson.XmlFile;
33 import hudson.model.Action;
34 import hudson.model.Descriptor.FormException;
35 import hudson.model.Failure;
36 import hudson.util.EditDistance;
37 import jenkins.model.Jenkins;
38 import hudson.model.Label;
39 import hudson.model.Saveable;
40 import hudson.model.listeners.SaveableListener;
41 import hudson.util.DescribableList;
42 import hudson.util.QuotedStringTokenizer;
43 import hudson.util.VariableResolver;
44 import hudson.util.XStream2;
45 import org.kohsuke.stapler.StaplerRequest;
46 import org.kohsuke.stapler.StaplerResponse;
47 import org.kohsuke.stapler.export.Exported;
48 import org.kohsuke.stapler.interceptor.RequirePOST;
49
50 import javax.servlet.ServletException;
51 import java.io.File;
52 import java.io.IOException;
53 import java.util.ArrayList;
54 import java.util.Collections;
55 import java.util.List;
56 import java.util.Set;
57 import java.util.Vector;
58 import java.util.logging.Level;
59 import java.util.logging.Logger;
60 import javax.annotation.CheckForNull;
61 import javax.annotation.Nullable;
62
63 /**
64 * Atomic single token label, like "foo" or "bar".
65 *
66 * @author Kohsuke Kawaguchi
67 * @since 1.372
68 */
69 public class LabelAtom extends Label implements Saveable {
70 private DescribableList<LabelAtomProperty,LabelAtomPropertyDescriptor> properties =
71 new DescribableList<LabelAtomProperty,LabelAtomPropertyDescriptor>(this);
72
73 @CopyOnWrite
74 protected transient volatile List<Action> transientActions = new Vector<Action>();
75
76 public LabelAtom(String name) {
77 super(name);
78 }
79
80 /**
81 * If the label contains 'unsafe' chars, escape them.
82 */
83 @Override
84 public String getExpression() {
85 return escape(name);
86 }
87
88 /**
89 * {@inheritDoc}
90 *
91 * <p>
92 * Note that this method returns a read-only view of {@link Action}s.
93 * {@link LabelAtomProperty}s who want to add a project action
94 * should do so by implementing {@link LabelAtomProperty#getActions(LabelAtom)}.
95 */
96 @Override
97 public List<Action> getActions() {
98 // add all the transient actions, too
99 List<Action> actions = new Vector<Action>(super.getActions());
100 actions.addAll(transientActions);
101 // return the read only list to cause a failure on plugins who try to add an action here
102 return Collections.unmodifiableList(actions);
103 }
104
105 protected void updateTransientActions() {
106 Vector<Action> ta = new Vector<Action>();
107
108 // add the config link
109 if (!getApplicablePropertyDescriptors().isEmpty()) {
110 // if there's no property descriptor, there's nothing interesting to configure.
111 ta.add(new Action() {
112 public String getIconFileName() {
113 if (Jenkins.getInstance().hasPermission(Jenkins.ADMINISTER))
114 return "setting.png";
115 else
116 return null;
117 }
118
119 public String getDisplayName() {
120 return "Configure";
121 }
122
123 public String getUrlName() {
124 return "configure";
125 }
126 });
127 }
128
129 for (LabelAtomProperty p : properties)
130 ta.addAll(p.getActions(this));
131
132 transientActions = ta;
133 }
134
135 /**
136 * Properties associated with this label.
137 */
138 public DescribableList<LabelAtomProperty, LabelAtomPropertyDescriptor> getProperties() {
139 return properties;
140 }
141
142 @Exported
143 public List<LabelAtomProperty> getPropertiesList() {
144 return properties.toList();
145 }
146
147 @Override
148 public boolean matches(VariableResolver<Boolean> resolver) {
149 return resolver.resolve(name);
150 }
151
152 @Override
153 public <V, P> V accept(LabelVisitor<V, P> visitor, P param) {
154 return visitor.onAtom(this,param);
155 }
156
157 @Override
158 public Set<LabelAtom> listAtoms() {
159 return Collections.singleton(this);
160 }
161
162 @Override
163 public LabelOperatorPrecedence precedence() {
164 return LabelOperatorPrecedence.ATOM;
165 }
166
167 /*package*/ XmlFile getConfigFile() {
168 return new XmlFile(XSTREAM, new File(Jenkins.getInstance().root, "labels/"+name+".xml"));
169 }
170
171 public void save() throws IOException {
172 if(BulkChange.contains(this)) return;
173 try {
174 getConfigFile().write(this);
175 SaveableListener.fireOnChange(this, getConfigFile());
176 } catch (IOException e) {
177 LOGGER.log(Level.WARNING, "Failed to save "+getConfigFile(),e);
178 }
179 }
180
181 public void load() {
182 XmlFile file = getConfigFile();
183 if(file.exists()) {
184 try {
185 file.unmarshal(this);
186 } catch (IOException e) {
187 LOGGER.log(Level.WARNING, "Failed to load "+file, e);
188 }
189 }
190 properties.setOwner(this);
191 updateTransientActions();
192 }
193
194 /**
195 * Returns all the {@link LabelAtomPropertyDescriptor}s that can be potentially configured
196 * on this label.
197 */
198 public List<LabelAtomPropertyDescriptor> getApplicablePropertyDescriptors() {
199 return LabelAtomProperty.all();
200 }
201
202 /**
203 * Accepts the update to the node configuration.
204 */
205 @RequirePOST
206 public void doConfigSubmit( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException, FormException {
207 final Jenkins app = Jenkins.getInstance();
208
209 app.checkPermission(Jenkins.ADMINISTER);
210
211 properties.rebuild(req, req.getSubmittedForm(), getApplicablePropertyDescriptors());
212 updateTransientActions();
213 save();
214
215 // take the user back to the label top page.
216 rsp.sendRedirect2(".");
217 }
218
219 /**
220 * Obtains an atom by its {@linkplain #getName() name}.
221 * @see Jenkins#getLabelAtom
222 */
223 public static @Nullable LabelAtom get(@CheckForNull String l) {
224 return Jenkins.getInstance().getLabelAtom(l);
225 }
226
227 public static LabelAtom findNearest(String name) {
228 List<String> candidates = new ArrayList<String>();
229 for (LabelAtom a : Jenkins.getInstance().getLabelAtoms()) {
230 candidates.add(a.getName());
231 }
232 return get(EditDistance.findNearest(name, candidates));
233 }
234
235 public static boolean needsEscape(String name) {
236 try {
237 Jenkins.checkGoodName(name);
238 // additional restricted chars
239 for( int i=0; i<name.length(); i++ ) {
240 char ch = name.charAt(i);
241 if(" ()\t\n".indexOf(ch)!=-1)
242 return true;
243 }
244 return false;
245 } catch (Failure failure) {
246 return true;
247 }
248 }
249
250 public static String escape(String name) {
251 if (needsEscape(name))
252 return QuotedStringTokenizer.quote(name);
253 return name;
254 }
255
256 private static final Logger LOGGER = Logger.getLogger(LabelAtom.class.getName());
257
258 private static final XStream2 XSTREAM = new XStream2();
259
260 static {
261 // Don't want Label.ConverterImpl to be used:
262 XSTREAM.registerConverter(new LabelAtomConverter(), 100);
263 }
264
265 // class name is not ConverterImpl, to avoid getting picked up by AssociatedConverterImpl
266 private static class LabelAtomConverter extends XStream2.PassthruConverter<LabelAtom> {
267 private Label.ConverterImpl leafLabelConverter = new Label.ConverterImpl();
268
269 private LabelAtomConverter() {
270 super(XSTREAM);
271 }
272
273 public boolean canConvert(Class type) {
274 return LabelAtom.class.isAssignableFrom(type);
275 }
276
277 public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
278 if (context.get(IN_NESTED)==null) {
279 context.put(IN_NESTED,true);
280 try {
281 super.marshal(source,writer,context);
282 } finally {
283 context.put(IN_NESTED,false);
284 }
285 } else
286 leafLabelConverter.marshal(source,writer,context);
287 }
288
289 public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
290 if (context.get(IN_NESTED)==null) {
291 context.put(IN_NESTED,true);
292 try {
293 return super.unmarshal(reader,context);
294 } finally {
295 context.put(IN_NESTED,false);
296 }
297 } else
298 return leafLabelConverter.unmarshal(reader,context);
299 }
300
301 @Override
302 protected void callback(LabelAtom obj, UnmarshallingContext context) {
303 // noop
304 }
305
306 private static final Object IN_NESTED = "VisitingInnerLabelAtom";
307 }
308 }
309