View Javadoc

1   /*
2    * Copyright (c) 2003-2008 by Cosylab d. d.
3    *
4    * This file is part of CosyBeans.
5    *
6    * CosyBeans is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU General Public License as published by
8    * the Free Software Foundation, either version 3 of the License, or
9    * (at your option) any later version.
10   *
11   * CosyBeans is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU General Public License for more details.
15   *
16   * You should have received a copy of the GNU General Public License
17   * along with CosyBeans.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  
20  package com.cosylab.gui.adapters;
21  
22  import java.awt.Component;
23  import java.awt.GridBagConstraints;
24  import java.awt.GridBagLayout;
25  import java.awt.Insets;
26  import java.awt.event.ActionEvent;
27  import java.awt.event.ActionListener;
28  import java.beans.PropertyVetoException;
29  import java.util.HashMap;
30  import java.util.Iterator;
31  import java.util.Map;
32  
33  import javax.swing.JButton;
34  import javax.swing.JDialog;
35  import javax.swing.JFrame;
36  import javax.swing.JLabel;
37  import javax.swing.JPanel;
38  
39  import com.cosylab.gui.components.NumberField;
40  
41  
42  /**
43   * A utility class for Converter manipulation.
44   * 
45   * @author Jaka Bobnar, Cosylab
46   *
47   */
48  public class ConverterUtilities {
49  	
50  	protected static class CustomizerDialog extends JDialog {
51  		
52  		private static final long serialVersionUID = 1L;
53  		
54  		private JButton okButton = null, cancelButton = null;
55  		private JPanel panel;
56  		private JPanel contentPanel;
57  		private HashMap<String, NumberField> fields = new HashMap<String, NumberField>();
58  		private HashMap<String, Number> result;
59  		private JLabel functionLabel;
60  		private boolean okPressed = false;
61  				
62  		public CustomizerDialog() {
63  			setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
64  			setModal(true);
65  			setResizable(false);
66  			setSize(400, 300);
67  			setContentPane(getContentPanel());
68  //			addWindowListener(new WindowAdapter(){
69  //				@Override
70  //				public void windowClosing(WindowEvent e) {
71  //					result = new HashMap<String, Number>();
72  //					Iterator<String> it = fields.keySet().iterator();
73  //					String key;
74  //					while(it.hasNext()) {
75  //						key = it.next();
76  //						result.put(key, fields.get(key).getValue());
77  //					}
78  //				}
79  //			});
80  			pack();
81  		}
82  		
83  		private JPanel getContentPanel() {
84  			if (contentPanel == null) {
85  				contentPanel = new JPanel(new GridBagLayout());
86  				contentPanel.add(getPanel(), new GridBagConstraints(0, 0, 2, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(11, 11, 3, 11), 0, 0));
87  				
88  				JPanel buttonPanel = new JPanel(new GridBagLayout());
89  				okButton = new JButton("OK");
90  				okButton.addActionListener(new ActionListener(){
91  					public void actionPerformed(ActionEvent e) {
92  						result = new HashMap<String, Number>();
93  						Iterator<String> it = fields.keySet().iterator();
94  						String key;
95  						while(it.hasNext()) {
96  							key = it.next();
97  							result.put(key, fields.get(key).getValue());
98  						}
99  						okPressed = true;
100 						dispose();
101 					}
102 				});
103 				contentPanel.add(getFunctionLabel(), new GridBagConstraints(0, 1, 2, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 11, 5, 12), 0, 0));
104 				
105 				buttonPanel.add(okButton, new GridBagConstraints(0, 0, 1, 1, 1, 0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 0, 0, 2), 0, 0));
106 				cancelButton = new JButton("Cancel");
107 				cancelButton.addActionListener(new ActionListener(){
108 					public void actionPerformed(ActionEvent e) {
109 						okPressed = false;
110 						dispose();
111 					}
112 				});
113 				buttonPanel.add(cancelButton, new GridBagConstraints(1, 0, 1, 1, 1, 0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 2, 0, 0), 0, 0));
114 				
115 				contentPanel.add(buttonPanel, new GridBagConstraints(0,2,2,1,1,0,GridBagConstraints.EAST, GridBagConstraints.NONE,new Insets(11,11,11,12),0,0));
116 			}
117 			return contentPanel;
118 		}
119 		
120 		private JPanel getPanel() {
121 			if (panel == null) {
122 				panel = new JPanel(new GridBagLayout());
123 			}
124 			return panel;
125 		}
126 		
127 		private JLabel getFunctionLabel(){
128 			if (functionLabel == null) {
129 				functionLabel = new JLabel();
130 			}
131 
132 			return functionLabel;
133 		}
134 		
135 		private void setFunctionLabelText(Converter c){
136 			if (c instanceof IdentityConverter){
137 				getFunctionLabel().setText("<OUTPUT> = <INPUT>");
138 			}else if (c instanceof MultiplierConverter){
139 				getFunctionLabel().setText("<OUTPUT> = Factor * <INPUT>");
140 			}else if (c instanceof LinearConverter){
141 				getFunctionLabel().setText("<OUTPUT> = Factor * <INPUT> + Shift");
142 			}else if (c instanceof PotentialConverter){
143 				getFunctionLabel().setText("<OUTPUT> = <INPUT> ^ Exponent");
144 			}else if (c instanceof ExponentialConverter){
145 				getFunctionLabel().setText("<OUTPUT> = Base ^ <INPUT>");
146 			}else if (c instanceof LogarithmicConverter){
147 				getFunctionLabel().setText("<OUTPUT> = log_Base(<INPUT>)");
148 			}
149 		}
150 		
151 		public void initialize(String[] names) {
152 			panel.removeAll();
153 			fields.clear();
154 			for (int i = 0; i < names.length; i++) {
155 				getPanel().add(new JLabel(names[i] + ": "), new GridBagConstraints(0, i, 1, 1, 0.3, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(1, 0, 1, 0), 0, 0));
156 				NumberField field = new NumberField();
157 				fields.put(names[i],field);
158 				getPanel().add(field, new GridBagConstraints(1, i, 1, 1, 1, 0, GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(1, 0, 1, 0), 0, 0));
159 			}
160 			setSize(350, 120+names.length*30);
161 		}
162 		
163 		public HashMap<String, Number> showDialog(Map<String, Number> initialValues) {
164 			Iterator<String> it = initialValues.keySet().iterator();
165 			String key;
166 			NumberField field;
167 			while(it.hasNext()) {
168 				key = it.next();
169 				field = fields.get(key);
170 				field.setValue(initialValues.get(key));
171 				fields.put(key, field);
172 			}
173 			setVisible(true);
174 			return result;
175 		}
176 		
177 		@Override
178 		public void setVisible(boolean b) {
179 			okPressed = false;
180 			super.setVisible(b);
181 		}
182 		
183 		public boolean isOKPressed() {
184 			return okPressed;
185 		}
186 	}
187 	
188 	
189 	protected static HashMap<Converter, CustomizerDialog> cache = new HashMap<Converter, CustomizerDialog>();
190 	
191 	/**
192 	 * Shows a dialog window with converter editor.
193 	 * 
194 	 * @param parent parent of the dialog
195 	 * @param converter converter to be edited
196 	 * @return returns true if editing was confirmed or false if it was cancelled
197 	 */
198 	public static boolean showEditorDialog(Component parent, Converter converter) {
199 		CustomizerDialog dialog = cache.remove(converter);
200 		if (dialog == null) {
201 			dialog = new CustomizerDialog();
202 			dialog.initialize(getFieldNames(converter));
203 			dialog.setFunctionLabelText(converter);
204 		}
205 		cache.put(converter, dialog);
206 		dialog.setLocationRelativeTo(parent);
207 		dialog.setTitle(converter.getName() + " Converter Editor");
208 		HashMap<String, Number> map = dialog.showDialog(extractInitialValues(converter));
209 		if (dialog.isOKPressed()) {
210 			dispatchValues(converter, map);
211 			return true;
212 		}
213 		return false;
214 	}
215 	
216 	/**
217 	 * Obtains initial values of converter's fields. Method returns a map of all fields
218 	 * with their corresponding values. The name of the field (the key in the map) should
219 	 * be the same as returned by getFieldNames(Converter) method.
220 	 * 
221 	 * @param converter
222 	 * @return
223 	 */
224 	protected static Map<String, Number> extractInitialValues(Converter converter) {
225 		HashMap<String, Number> map = new HashMap<String, Number>();
226 		if (converter instanceof LinearConverter) {
227 			map.put("Factor", ((LinearConverter)converter).getMultiplicationFactor());
228 			map.put("Shift", ((LinearConverter)converter).getShift());
229 		} else if (converter instanceof MultiplierConverter) {
230 			map.put("Factor", ((MultiplierConverter)converter).getMultiplicationFactor());
231 		} else if (converter instanceof LogarithmicConverter) {
232 			map.put("Base", ((LogarithmicConverter)converter).getBase());
233 		} else if (converter instanceof ExponentialConverter) {
234 			map.put("Base", ((ExponentialConverter)converter).getBase()); 
235 		} else if (converter instanceof PotentialConverter){
236 			map.put("Exponent", ((PotentialConverter)converter).getExponent());
237 		}
238 		return map;
239 	}
240 	
241 	/**
242 	 * Sets the converter's fields with new values obtained from the editor dialog.
243 	 * 
244 	 * @param converter
245 	 * @param values
246 	 */
247 	protected static void dispatchValues(Converter converter, Map<String, Number> values) {
248 		if (values == null) values = extractInitialValues(converter);
249 		if (converter instanceof LinearConverter) {
250 			((LinearConverter)converter).setMultiplicationFactor(values.get("Factor").doubleValue());
251 			((LinearConverter)converter).setShift(values.get("Shift").doubleValue());
252 		} else if (converter instanceof MultiplierConverter) {
253 			((MultiplierConverter)converter).setMultiplicationFactor(values.get("Factor").doubleValue());
254 		} else if (converter instanceof LogarithmicConverter) {
255 			((LogarithmicConverter)converter).setBase(values.get("Base").doubleValue());
256 		} else if (converter instanceof ExponentialConverter) {
257 			((ExponentialConverter)converter).setBase(values.get("Base").doubleValue()); 
258 		} else if (converter instanceof PotentialConverter){
259 			((PotentialConverter)converter).setExponent(values.get("Exponent").doubleValue());
260 		}
261 		
262 	}
263 	
264 	/**
265 	 * Returns the names of the fields that will be visible in the dialog.
266 	 * 
267 	 * @param converter
268 	 * @return
269 	 */
270 	protected static String[] getFieldNames(Converter converter) {
271 		if (converter instanceof LinearConverter) {
272 			return new String[]{"Factor", "Shift"};
273 		} else if (converter instanceof MultiplierConverter) {
274 			return new String[]{"Factor"};
275 		} else if (converter instanceof LogarithmicConverter) {
276 			return new String[]{"Base"};
277 		} else if (converter instanceof ExponentialConverter) {
278 			return new String[]{"Base"};
279 		} else if (converter instanceof PotentialConverter){
280 			return new String[]{"Exponent"};
281 		} else 
282 			return new String[]{};
283 	}
284 	
285 	/**
286 	 * Checks if converter is editable.
287 	 * 
288 	 * @param converter
289 	 * @return true if editable
290 	 */
291 	public static boolean isEditable(Converter converter) {
292 		return (converter instanceof LinearConverter || 
293 				converter instanceof MultiplierConverter ||
294 				converter instanceof PotentialConverter ||
295 				converter instanceof LogarithmicConverter ||
296 				converter instanceof ExponentialConverter);
297 	}
298 	
299 	/**
300 	 * Composes a function from the given chain of converters. Parameter value is the input
301 	 * value that converter transforms. If converter does f(x) this method should
302 	 * give a string representing the f(value).
303 	 * 
304 	 * @param chain
305 	 * @param value
306 	 * @return
307 	 */
308 	public static String composeFunctionString(ConverterChain chain, String value) {
309 		Converter[] converters = chain.getConverters();
310 		for (Converter c : converters) {
311 			value = composeFunctionString(c, value);
312 		}
313 		return value;
314 	}
315 	
316 	/**
317 	 * Composes a function from the given converter. Parameter value is the input
318 	 * value that converter transforms. If converter does f(x) this method should
319 	 * give a string representing the f(value).
320 	 * 
321 	 * @param converter
322 	 * @param value
323 	 * @return
324 	 */
325 	public static String composeFunctionString(Converter converter, String value) {
326 		boolean valueStartsWithBracket = false;
327 		if (value != null && value.startsWith("(")) {
328 			valueStartsWithBracket = true;
329 		}
330 		if (converter instanceof ConverterChain) {
331 			return composeFunctionString((ConverterChain)converter, value);
332 		} else if (converter instanceof LinearConverter) {
333 			LinearConverter c = (LinearConverter)converter;
334 			//String multi = "", sign = "", shift = "";
335 			StringBuilder s= new StringBuilder(256);
336 			if (c.getMultiplicationFactor() == -1 ) {
337 				s.append('-');
338 			} else if (c.getMultiplicationFactor() != 1) {
339 				s.append(c.getMultiplicationFactor());
340 				s.append(" * ");
341 			}
342 			
343 			s.append('(');
344 			if (!valueStartsWithBracket) {
345 				s.append(' ');
346 			}
347 			s.append(value);
348 			s.append(')');
349 
350 			if (c.getShift() > 0){
351 				s.append(" + ");
352 				s.append(c.getShift());
353 			}
354 			else if (c.getShift() < 0){
355 				s.append(" - ");
356 				s.append(-c.getShift());
357 			}
358 			return s.toString();
359 		} else if (converter instanceof MultiplierConverter) {
360 			MultiplierConverter c = (MultiplierConverter)converter;
361 			StringBuilder s= new StringBuilder(256);
362 			//String multi = "";
363 			if (c.getMultiplicationFactor() == -1 ) {
364 				s.append("- ");
365 			} else if (c.getMultiplicationFactor() != 1) { 
366 				s.append(c.getMultiplicationFactor());
367 				s.append(" * ");
368 			}
369 			s.append('(');
370 			if (!valueStartsWithBracket) {
371 				s.append(' ');
372 			}
373 			s.append(value);
374 			s.append(')');
375 			return s.toString();
376 		} else if (converter instanceof ExponentialConverter) {
377 			return ((ExponentialConverter)converter).getBase() + "^(" + (valueStartsWithBracket?"":" ") + value + ")"; 
378 		} else if (converter instanceof LogarithmicConverter) {
379 			LogarithmicConverter c = (LogarithmicConverter)converter;
380 			return  "log_" + (c.getBase() == Math.E ? "e" : String.valueOf(c.getBase())) + "(" + (valueStartsWithBracket?"":" ") + value + ")";
381 		} else if (converter instanceof PotentialConverter){
382 			return "(" + (valueStartsWithBracket?"":" ") + value + ")^" + ((PotentialConverter)converter).getExponent();
383 		}
384 		else return "(" + (valueStartsWithBracket?"":" ") +value+")";
385 	}
386 	
387 	/**
388 	 * Constructs an initialization string used by PropertyEditor.
389 	 * @param converter
390 	 * @return
391 	 */
392 	public static String getInitializationString(Converter converter) {
393 		if (converter instanceof ConverterChain) {
394 			Converter[] conv = ((ConverterChain)converter).getConverters();
395 			StringBuffer buffer = new StringBuffer("new " + ConverterChain.class.getName() + "(new " +Converter.class.getName() + "[]{");
396 			for (Converter c : conv) {
397 				buffer.append(getInitStringInternal(c) + ",");
398 			}
399 			buffer = new StringBuffer(buffer.substring(0, buffer.length()-1));
400 			buffer.append("})");
401 			return buffer.substring(0);
402 		} else {
403 			return getInitStringInternal(converter);
404 		}
405 	}
406 	
407 	private static String getInitStringInternal(Converter converter) {
408 		StringBuffer str = new StringBuffer("new ");
409 		str.append(converter.getClass().getName() + "(");
410 		if (converter instanceof LinearConverter) {
411 			str.append(((LinearConverter)converter).getMultiplicationFactor() + "," + ((LinearConverter)converter).getShift());
412 		} else if (converter instanceof MultiplierConverter) {
413 			str.append(((MultiplierConverter)converter).getMultiplicationFactor());
414 		} else if (converter instanceof LogarithmicConverter) {
415 			str.append(((LogarithmicConverter)converter).getBase());
416 		} else if (converter instanceof PotentialConverter) {
417 			str.append(((PotentialConverter)converter).getExponent());
418 		}
419 		str.append(")");
420 		return str.substring(0);
421 	}
422 	
423 	/**
424 	 * Constructs a converter from a string. The given string is expected to be the same
425 	 * as converter.toString() would return.
426 	 * 
427 	 * @param converterToString
428 	 * @return
429 	 */
430 	public static Converter getConverterFromString(String converterToString) {
431 		String[] parameters = converterToString.split(":");
432 		if (converterToString.startsWith(MultiplierConverter.SHORT_NAME)) {
433 			return new MultiplierConverter(Double.parseDouble(parameters[1].trim()));
434 		} else if (converterToString.startsWith(LinearConverter.SHORT_NAME)) {
435 			return new LinearConverter(Double.parseDouble(parameters[1].trim()),Double.parseDouble(parameters[2].trim()));
436 		} else if (converterToString.startsWith(LogarithmicConverter.SHORT_NAME)) {
437 			return new LogarithmicConverter(Double.parseDouble(parameters[1].trim()));
438 		} else if (converterToString.startsWith(ExponentialConverter.SHORT_NAME)) {
439 			return new ExponentialConverter(Double.parseDouble(parameters[1].trim()));
440 		} else if(converterToString.startsWith(PotentialConverter.SHORT_NAME)){
441 			return new PotentialConverter(Double.parseDouble(parameters[1].trim()));
442 		} else 
443 			return new IdentityConverter();
444 	}
445 	
446 	public static void main(String[] args) throws PropertyVetoException {
447 		ConverterChain ch = new ConverterChain(new Converter[]{new LogarithmicConverter(5), new LinearConverter(4,3)});
448 		System.out.println(getInitializationString(ch));
449 	}
450 	
451 	
452 }