View Javadoc

1   /**
2    * Copyright (C) 2008 Atlassian
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *    http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.atlassian.theplugin.idea.config.serverconfig;
18  
19  import com.atlassian.theplugin.commons.SubscribedPlan;
20  import com.atlassian.theplugin.commons.bamboo.BambooPlan;
21  import com.atlassian.theplugin.commons.bamboo.BambooPlanData;
22  import com.atlassian.theplugin.commons.bamboo.BambooServerFacade;
23  import com.atlassian.theplugin.commons.cfg.BambooServerCfg;
24  import com.atlassian.theplugin.commons.cfg.ServerId;
25  import com.atlassian.theplugin.commons.exception.ServerPasswordNotProvidedException;
26  import com.atlassian.theplugin.commons.remoteapi.RemoteApiException;
27  import com.atlassian.theplugin.commons.util.MiscUtil;
28  import com.atlassian.theplugin.idea.ProgressAnimationProvider;
29  import com.intellij.uiDesigner.core.GridConstraints;
30  import com.intellij.uiDesigner.core.GridLayoutManager;
31  
32  import javax.swing.*;
33  import java.awt.*;
34  import java.awt.event.ActionEvent;
35  import java.awt.event.ActionListener;
36  import java.awt.event.KeyAdapter;
37  import java.awt.event.KeyEvent;
38  import java.awt.event.MouseAdapter;
39  import java.awt.event.MouseEvent;
40  import static java.lang.System.arraycopy;
41  import java.util.ArrayList;
42  import java.util.Collection;
43  import java.util.List;
44  import java.util.Map;
45  
46  
47  public class BambooPlansForm extends JPanel {
48  	private JPanel statusPanel;
49  	private JPanel toolbarPanel;
50  	private JCheckBox cbUseFavouriteBuilds;
51  	private JButton btRefresh;
52  	private JList list;
53  	private JPanel rootComponent;
54  	private JEditorPane statusPane;
55  	private JScrollPane scrollList;
56  	private JPanel listPanel;
57  	private ProgressAnimationProvider progressAnimation = new ProgressAnimationProvider();
58  
59  	private DefaultListModel model;
60  
61  	private boolean isListModified;
62  	private Boolean isUseFavourite = null;
63  	private transient BambooServerCfg bambooServerCfg;
64  	private static final int NUM_SERVERS = 10;
65  	private Map<ServerId, List<BambooPlanItem>> serverPlans = MiscUtil.buildConcurrentHashMap(NUM_SERVERS);
66  	private final transient BambooServerFacade bambooServerFacade;
67      private final BambooServerConfigForm serverPanel;
68  
69      public BambooPlansForm(BambooServerFacade bambooServerFacade, BambooServerCfg bambooServerCfg,
70              final BambooServerConfigForm bambooServerConfigForm) {
71  		this.bambooServerFacade = bambooServerFacade;
72  		this.bambooServerCfg = bambooServerCfg;
73          this.serverPanel = bambooServerConfigForm;
74  
75          $$$setupUI$$$();
76  
77  		final GridConstraints constraint = new GridConstraints(0, 0, 1, 1,
78  				GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
79  				GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
80  				GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false);
81  
82  		progressAnimation.configure(listPanel, scrollList, constraint);
83  
84  		list.addMouseListener(new MouseAdapter() {
85  			@Override
86              public void mousePressed(MouseEvent e) {
87  				int index = list.locationToIndex(e.getPoint());
88  				setCheckboxState(index);
89  			}
90  		});
91  
92  		list.addKeyListener(new KeyAdapter() {
93  			@Override
94              public void keyPressed(KeyEvent e) {
95  				if (e.getKeyCode() == KeyEvent.VK_SPACE) {
96  					int index = list.getSelectedIndex();
97  					setCheckboxState(index);
98  				}
99  			}
100 		});
101 
102 		cbUseFavouriteBuilds.addActionListener(new ActionListener() {
103 			public void actionPerformed(ActionEvent e) {
104 				setEnabled(!cbUseFavouriteBuilds.isSelected());
105 			}
106 		});
107 
108 		btRefresh.addActionListener(new ActionListener() {
109 			public void actionPerformed(ActionEvent e) {
110 				refreshServerPlans();
111 			}
112 		});
113 	}
114 
115 	private void refreshServerPlans() {
116 		serverPlans.remove(bambooServerCfg.getServerId());
117 		serverPanel.saveData();
118         retrievePlans(bambooServerCfg);
119 	}
120 
121 	private void setCheckboxState(int index) {
122 		if (index != -1 && isEnabled()) {
123 			BambooPlanItem pi = (BambooPlanItem) list.getModel().getElementAt(index);
124 			pi.setSelected(!pi.isSelected());
125 			setViewState(index, pi.isSelected());
126 			repaint();
127 
128 			setModifiedState();
129 		}
130 	}
131 
132 	private void setViewState(int index, boolean newState) {
133 		int[] oldIdx = list.getSelectedIndices();
134 		int[] newIdx;
135 		if (newState) {
136 			newIdx = new int[oldIdx.length + 1];
137 			arraycopy(newIdx, 0, oldIdx, 0, oldIdx.length);
138 			newIdx[newIdx.length - 1] = index;
139 		} else {
140 			newIdx = new int[Math.max(0, oldIdx.length - 1)];
141 			int i = 0;
142 			for (int id : oldIdx) {
143 				if (id == index) {
144 					continue;
145 				}
146 				newIdx[i++] = id;
147 			}
148 		}
149 		list.setSelectedIndices(newIdx);
150 	}
151 
152 	private void setModifiedState() {
153 		isListModified = false;
154 		List<BambooPlanItem> local = serverPlans.get(bambooServerCfg.getServerId());
155 		if (local != null) {
156 			for (int i = 0; i < model.getSize(); i++) {
157 				if (local.get(i) != null) {
158 					if (((BambooPlanItem) model.getElementAt(i)).isSelected()
159 							!= local.get(i).isSelected()) {
160 						isListModified = true;
161 						break;
162 					}
163 				}
164 			}
165 		} else {
166 			isListModified = !model.isEmpty();
167 		}
168 	}
169 
170 	public void setData(final BambooServerCfg serverCfg) {
171 		bambooServerCfg = serverCfg;
172 		cbUseFavouriteBuilds.setEnabled(false);
173         if (bambooServerCfg.getUrl().length() > 0) {
174 			retrievePlans(bambooServerCfg);
175 		} else {
176 			model.removeAllElements();
177 		}
178 	}
179 
180 	private void retrievePlans(final BambooServerCfg queryServer) {
181 		list.setEnabled(false);
182 		if (isUseFavourite != null) {
183 			cbUseFavouriteBuilds.setSelected(isUseFavourite);
184 			isUseFavourite = null;
185 		} else {
186 			cbUseFavouriteBuilds.setSelected(queryServer.isUseFavourites());
187 		}
188 		model.removeAllElements();
189 		statusPane.setText("Waiting for server plans...");
190 
191 		new Thread(new Runnable() {
192 			public void run() {
193 				progressAnimation.startProgressAnimation();
194                 StringBuilder msg = new StringBuilder();
195                 try {
196                     ServerId key = queryServer.getServerId();
197                     if (!serverPlans.containsKey(key)) {
198                         Collection<BambooPlan> plans;
199                         try {
200                             plans = bambooServerFacade.getPlanList(queryServer);
201                         } catch (ServerPasswordNotProvidedException e) {
202                             msg.append("Unable to connect: password for server not provided\n");
203                             return;
204                         } catch (RemoteApiException e) {
205                             msg.append("Unable to connect: ");
206                             msg.append(e.getMessage());
207                             msg.append("\n");
208                             return;
209                         }
210                         List<BambooPlanItem> plansForServer = new ArrayList<BambooPlanItem>();
211                         if (plans != null) {
212                             for (BambooPlan plan : plans) {
213                                 plansForServer.add(new BambooPlanItem(plan, false));
214                             }
215                             msg.append("Build plans updated from server\n");
216                         }
217                         for (SubscribedPlan sPlan : queryServer.getSubscribedPlans()) {
218                             boolean exists = false;
219                             for (BambooPlanItem bambooPlanItem : plansForServer) {
220                                 if (bambooPlanItem.getPlan().getPlanKey().equals(sPlan.getPlanId())) {
221                                     exists = true;
222                                     break;
223                                 }
224                             }
225                             if (!exists) {
226                                 BambooPlanData p = new BambooPlanData(sPlan.getPlanId(), sPlan.getPlanId());
227                                 p.setEnabled(false);
228                                 p.setFavourite(false);
229                                 plansForServer.add(new BambooPlanItem(p, true));
230                             }
231                         }
232                         msg.append("Build plans updated based on stored configuration");
233                         serverPlans.put(key, plansForServer);
234                     } else {
235                         msg.append("Build plans updated based on cached values");
236                     }
237             } finally {
238                 progressAnimation.stopProgressAnimation();
239                 final String message = msg.toString();
240                 EventQueue.invokeLater(new Runnable() {
241                     public void run() {
242                         updatePlanNames(queryServer, message);
243                     }
244                 });
245             }
246 
247             }
248 		}, "atlassian-idea-plugin bamboo panel retrieve plans").start();
249 	}
250 
251 	private synchronized void updatePlanNames(BambooServerCfg server, String message) {
252 		if (server.equals(bambooServerCfg)) {
253 			List<BambooPlanItem> plans = serverPlans.get(server.getServerId());
254 			if (plans != null) {
255 				model.removeAllElements();
256 				for (BambooPlanItem plan : plans) {
257 					plan.setSelected(false);
258 					for (SubscribedPlan sPlan : server.getSubscribedPlans()) {
259 						if (sPlan.getPlanId().equals(plan.getPlan().getPlanKey())) {
260 							plan.setSelected(true);
261 							break;
262 						}
263 					}
264 					model.addElement(new BambooPlanItem(plan.getPlan(), plan.isSelected()));
265 				}
266 			}
267 			statusPane.setText(message);
268 			statusPane.setCaretPosition(0);
269 			setVisible(true);
270 			cbUseFavouriteBuilds.setEnabled(true);
271 			list.setEnabled(!cbUseFavouriteBuilds.isSelected());
272 			isListModified = false;
273 		}
274 	}
275 
276 	public void saveData() {
277         if (bambooServerCfg == null) {
278             return;
279         }
280 		// move data only when we have fetched the data - otherwise we could overwrite user data due to e.g. network problems
281 		if (serverPlans.containsKey(bambooServerCfg.getServerId()) == true) {
282 			bambooServerCfg.clearSubscribedPlans();
283 			for (int i = 0; i < model.getSize(); ++i) {
284 				if (model.getElementAt(i) instanceof BambooPlanItem) {
285 					BambooPlanItem p = (BambooPlanItem) model.getElementAt(i);
286 
287 					if (p.isSelected()) {
288 						SubscribedPlan spb = new SubscribedPlan(p.getPlan().getPlanKey());
289 						bambooServerCfg.getSubscribedPlans().add(spb);
290 					}
291 				}
292 			}
293 		}
294 		bambooServerCfg.setUseFavourites(cbUseFavouriteBuilds.isSelected());
295 	}
296 
297 	public boolean isModified() {
298 		boolean isFavModified = false;
299 		if (bambooServerCfg != null) {
300 			if (cbUseFavouriteBuilds.isSelected() != bambooServerCfg.isUseFavourites()) {
301 				isFavModified = true;
302 			}
303 		} else {
304 			return false;
305 		}
306 
307 		return isListModified || isFavModified;
308 	}
309 
310 	@Override
311     public void setEnabled(boolean enabled) {
312 		super.setEnabled(enabled);
313 		list.setEnabled(enabled);
314 	}
315 
316 	private void createUIComponents() {
317 		model = new DefaultListModel();
318 		list = new JList(model);
319 		list.setCellRenderer(new PlanListCellRenderer());
320 		list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
321 	}
322 
323 	/**
324 	 * Method generated by IntelliJ IDEA GUI Designer
325 	 * >>> IMPORTANT!! <<<
326 	 * DO NOT edit this method OR call it in your code!
327 	 *
328 	 * @noinspection ALL
329 	 */
330 	private void $$$setupUI$$$() {
331 		createUIComponents();
332 		rootComponent = new JPanel();
333 		rootComponent.setLayout(new GridBagLayout());
334 		rootComponent.setBorder(BorderFactory.createTitledBorder("Build Plans"));
335 		toolbarPanel = new JPanel();
336 		toolbarPanel.setLayout(new GridBagLayout());
337 		GridBagConstraints gbc;
338 		gbc = new GridBagConstraints();
339 		gbc.gridx = 0;
340 		gbc.gridy = 0;
341 		gbc.weightx = 1.0;
342 		gbc.fill = GridBagConstraints.HORIZONTAL;
343 		gbc.insets = new Insets(0, 10, 12, 8);
344 		rootComponent.add(toolbarPanel, gbc);
345 		cbUseFavouriteBuilds = new JCheckBox();
346 		cbUseFavouriteBuilds.setText("Use Favourite Builds For Server");
347 		cbUseFavouriteBuilds.setMnemonic('F');
348 		cbUseFavouriteBuilds.setDisplayedMnemonicIndex(4);
349 		gbc = new GridBagConstraints();
350 		gbc.gridx = 0;
351 		gbc.gridy = 0;
352 		gbc.weightx = 1.0;
353 		gbc.weighty = 1.0;
354 		gbc.anchor = GridBagConstraints.WEST;
355 		toolbarPanel.add(cbUseFavouriteBuilds, gbc);
356 		btRefresh = new JButton();
357 		btRefresh.setText("Refresh");
358 		gbc = new GridBagConstraints();
359 		gbc.gridx = 2;
360 		gbc.gridy = 0;
361 		gbc.weighty = 1.0;
362 		gbc.fill = GridBagConstraints.HORIZONTAL;
363 		toolbarPanel.add(btRefresh, gbc);
364 		statusPanel = new JPanel();
365 		statusPanel.setLayout(new GridLayoutManager(1, 1, new Insets(0, 0, 0, 0), -1, -1));
366 		gbc = new GridBagConstraints();
367 		gbc.gridx = 0;
368 		gbc.gridy = 2;
369 		gbc.fill = GridBagConstraints.HORIZONTAL;
370 		gbc.insets = new Insets(0, 12, 12, 12);
371 		rootComponent.add(statusPanel, gbc);
372 		final JScrollPane scrollPane1 = new JScrollPane();
373 		scrollPane1.setEnabled(true);
374 		scrollPane1.setHorizontalScrollBarPolicy(31);
375 		scrollPane1.setVerticalScrollBarPolicy(20);
376 		statusPanel.add(scrollPane1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, 1, 1, new Dimension(-1, 40), null, new Dimension(-1, 40), 0, false));
377 		statusPane = new JEditorPane();
378 		statusPane.setEditable(false);
379 		scrollPane1.setViewportView(statusPane);
380 		listPanel = new JPanel();
381 		listPanel.setLayout(new GridLayoutManager(1, 1, new Insets(0, 0, 0, 0), -1, -1));
382 		listPanel.setBackground(new Color(-1));
383 		gbc = new GridBagConstraints();
384 		gbc.gridx = 0;
385 		gbc.gridy = 1;
386 		gbc.weightx = 1.0;
387 		gbc.weighty = 1.0;
388 		gbc.fill = GridBagConstraints.BOTH;
389 		gbc.insets = new Insets(0, 12, 0, 12);
390 		rootComponent.add(listPanel, gbc);
391 		scrollList = new JScrollPane();
392 		scrollList.setBackground(new Color(-1));
393 		listPanel.add(scrollList, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
394 		scrollList.setViewportView(list);
395 	}
396 
397 	/**
398 	 * @noinspection ALL
399 	 */
400 	public JComponent $$$getRootComponent$$$() {
401 		return rootComponent;
402 	}
403 }