1 // Copyright Hugh Perkins 2009 2 // hughperkins@gmail.com http://manageddreams.com 3 // 4 // This program is free software; you can redistribute it and/or modify it 5 // under the terms of the GNU General Public License as published by the 6 // Free Software Foundation; either version 2 of the License, or 7 // (at your option) any later version. 8 // 9 // This program is distributed in the hope that it will be useful, but 10 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 // more details. 13 // 14 // You should have received a copy of the GNU General Public License along 15 // with this program in the file licence.txt; if not, write to the 16 // Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111- 17 // 1307 USA 18 // You can find the licence also on the web at: 19 // http://www.opensource.org/licenses/gpl-license.php 20 // 21 // ====================================================================================== 22 // 23 24 package hughai.ui; 25 26 import java.util.*; 27 import java.util.Map; 28 import java.awt.Component; 29 import java.awt.GridLayout; 30 import java.awt.event.ActionEvent; 31 import java.awt.event.ActionListener; 32 import java.lang.annotation.*; 33 import java.lang.reflect.*; 34 35 import javax.swing.*; 36 import javax.swing.event.ListSelectionEvent; 37 import javax.swing.event.ListSelectionListener; 38 39 import com.springrts.ai.*; 40 import com.springrts.ai.oo.clb.*; 41 42 import hughai.*; 43 import hughai.EnemyTracker.EnemyAdapter; 44 import hughai.basictypes.*; 45 import hughai.building.Workflows; 46 import hughai.mapping.*; 47 import hughai.packcoordinators.*; 48 import hughai.unitdata.*; 49 import hughai.unitdata.UnitController.UnitAdapter; 50 import hughai.utils.*; 51 import hughai.utils.ReflectionHelper.CustomClass; 52 import hughai.utils.ReflectionHelper.Exclude; 53 import hughai.utils.ReflectionHelper.ListTypeInfo; 54 55 public class UiReflectionHelper { 56 PlayerObjects playerObjects; 57 UiReflectionHelper( PlayerObjects playerObjects )58 public UiReflectionHelper( PlayerObjects playerObjects ) { 59 this.playerObjects = playerObjects; 60 } 61 dispose()62 public void dispose() { 63 64 } 65 objectToEditComponent( Object object, Annotation[] annotations )66 JComponent objectToEditComponent( Object object, Annotation[] annotations ) { 67 Class<?> objectClass = object.getClass(); 68 if( objectClass == String.class ) { 69 JTextField textField = new JTextField(); 70 textField.setText( "" + object ); 71 return textField; 72 } 73 if( objectClass == boolean.class ) { 74 JCheckBox checkBox = new JCheckBox(); 75 checkBox.setSelected( (Boolean)object ); 76 return checkBox; 77 } 78 if( objectClass == float.class ) { 79 JTextField textField = new JTextField(); 80 textField.setText( "" + object ); 81 return textField; 82 } 83 if( objectClass == int.class ) { 84 JTextField textField = new JTextField(); 85 textField.setText( "" + object ); 86 return textField; 87 } 88 if( List.class.isAssignableFrom( objectClass ) ) { 89 90 } 91 // boolean foundCustomClassAttribute = false; 92 // for( Annotation annotation : annotations ) { 93 // if( CustomClass.class.isAssignableFrom( annotation.getClass() ) ) { 94 // foundCustomClassAttribute = true; 95 // } 96 // } 97 // if( foundCustomClassAttribute ) { 98 // then, we put each field into its own column in a row? 99 // and if one of them is a list? 100 for( Field field : objectClass.getDeclaredFields() ) { 101 102 } 103 // } 104 105 throw new RuntimeException("UiReflectionhelper: unhandled type: " + objectClass.getSimpleName() ); 106 } 107 108 public enum DisplayContext { 109 detailed, 110 listrow, 111 brief 112 } 113 114 // ok, so if we call with a primitive, it's easy. 115 // if we call with a class, then we want either: 116 // fieldone: valueone 117 // fieldtwo: valuetwo 118 // fieldthree: valuethree 119 // or: 120 // fieldone fieldtwo fieldthree 121 // valueone valuetwo valuethree 122 // maybe the first is better? 123 // next, if we call with a list, then we want something like: 124 // valueone 125 // valuetwo 126 // valuethree 127 // what if it is a list of classes? then something like: 128 // fieldone fieldtwo fieldthree 129 // itemonev1 itemonev2 itemonev3 130 // itemtwov1 itemtwov2 itemtwov3 131 // 132 // if we call with a class with a list in, then something like: 133 // fieldone: valueone 134 // fieldtwo: valuetwo 135 // fieldthree: valuethree 136 // listfield: [button here to see details] 137 // or maybe: 138 // fieldone: valueone 139 // fieldtwo: valuetwo 140 // fieldthree: valuethree 141 // listfield: 142 // valueone 143 // valuetwo 144 // valuethree 145 // or: 146 // fieldone: valueone 147 // fieldtwo: valuetwo 148 // fieldthree: valuethree 149 // listfield: valueone 150 // valuetwo 151 // valuethree 152 // first option: button to see details, is easiest 153 // 154 // what if list of classes, and the class contains a list? 155 // maybe as before for a list of classes, with the button added? 156 // 157 // ok, so detailed can be true or false: 158 // if true, then class fields will be listed vertically, and list in full 159 // if false, then lists and classes will just become a button? 160 // if true, and classasrowtable is true, then a class will be horizontal, 161 // as a table row, otherwise vertically 162 // 163 // so much for layout... what about behavior when canModify is true? 164 // - probably need a button 'apply', 'revert' for each dialog and sub-dialog, ie for classes, maybe also for lists? 165 // - apply and revert applies only to that dialog, not to modifications to children dialogs 166 // - for lists, need button for each row, to delete it, and a button to add a new row 167 // - for primitive types, nothing is applied until parent class or list applies it? 168 // - maybe 'apply' and 'revert' apply at the level of each custom class? objectToDisplayComponent( final Object object, final Annotation[] annotations, DisplayContext displayContext, final boolean canModify )169 public JComponent objectToDisplayComponent( final Object object, final Annotation[] annotations, DisplayContext displayContext, final boolean canModify ) { 170 try { 171 if( object == null ) { 172 return new JLabel("<null>"); 173 } 174 175 final Class<?> objectClass = object.getClass(); 176 if( objectClass == String.class ) { 177 if( canModify ) { 178 JTextField textField = new JTextField(); 179 textField.setText( "" + object ); 180 return textField; 181 } else { 182 JLabel textField = new JLabel(); 183 textField.setText( "" + object ); 184 return textField; 185 } 186 } 187 if( objectClass.equals( Float.class ) ) { 188 if( canModify ) { 189 JTextField textField = new JTextField(); 190 textField.setText( "" + object ); 191 return textField; 192 } else { 193 JLabel textField = new JLabel(); 194 textField.setText( "" + object ); 195 return textField; 196 } 197 } 198 if( objectClass.equals( Integer.class ) ) { 199 if( canModify ) { 200 JTextField textField = new JTextField(); 201 textField.setText( "" + object ); 202 return textField; 203 } else { 204 JLabel textField = new JLabel(); 205 textField.setText( "" + object ); 206 return textField; 207 } 208 } 209 if( objectClass.equals( Boolean.class ) ) { 210 JCheckBox checkbox = new JCheckBox(); 211 checkbox.setEnabled( canModify ); 212 checkbox.setSelected( (Boolean )object ); 213 return checkbox; 214 } 215 if( List.class.isAssignableFrom( objectClass ) ) { 216 Class<?> listItemClass = getListItemClass( annotations ); 217 if( listItemClass == null ) { 218 throw new RuntimeException("Missing ListTypeInfo annotation on list" ); 219 } 220 221 if( displayContext.equals( DisplayContext.brief ) 222 || displayContext.equals( DisplayContext.listrow ) ) { // just return a button 223 final JButton button = new JButton( "..." ); 224 button.addActionListener( new ActionListener() { 225 @Override 226 public void actionPerformed( ActionEvent e ) { 227 System.out.println("creating child dialog..."); 228 JDialog childDialog = createDialog( button, "List", true ); 229 JComponent childcomponents = objectToDisplayComponent(object, annotations, DisplayContext.detailed, canModify ); 230 childDialog.add( childcomponents ); 231 childDialog.setSize( 300, 300 ); 232 childDialog.setVisible( true ); 233 } 234 } ); 235 return button; 236 } 237 238 // otherwise show as a table 239 List objectAsList = (List)object; 240 GridLayout gridLayout = new GridLayout(0,1); 241 JPanel panel = new JPanel( gridLayout ); 242 243 JComponent headers = listToHeaders( listItemClass ); 244 if( headers != null ) { 245 addGridLayoutRow( gridLayout ); 246 panel.add( headers ); 247 } 248 249 for( Object item : objectAsList ) { 250 JComponent childcomponent = objectToDisplayComponent( item, 251 annotations, DisplayContext.listrow, canModify ); 252 addGridLayoutRow( gridLayout ); 253 panel.add( childcomponent ); 254 } 255 return panel; 256 } 257 // boolean foundCustomClassAttribute = false; 258 // for( Annotation annotation : annotations ) { 259 // if( CustomClass.class.isAssignableFrom( annotation.getClass() ) ) { 260 // foundCustomClassAttribute = true; 261 // } 262 // } 263 // if( foundCustomClassAttribute ) { 264 if( displayContext.equals( DisplayContext.brief ) ) { // just return a button 265 // JPanel panel = new JPanel( new GridLayout( 1, 1 )); 266 267 // final JLabel label = new JLabel( objectClass.getSimpleName() ); 268 269 final JButton button = new JButton( "..." ); 270 button.setSize( 50, 50 ); 271 272 button.addActionListener( new ActionListener() { 273 @Override 274 public void actionPerformed( ActionEvent e ) { 275 System.out.println("creating child dialog..."); 276 JDialog childDialog = createDialog( button, objectClass.getSimpleName(), true ); 277 JComponent childcomponents = objectToDisplayComponent(object, new Annotation[0], DisplayContext.detailed, canModify ); 278 childDialog.add( childcomponents ); 279 childDialog.setSize( 300, 300 ); 280 childDialog.setVisible( true ); 281 } 282 } ); 283 // panel.add( label ); 284 // panel.add( button ); 285 return button; 286 // return panel; 287 } 288 289 if( displayContext.equals( DisplayContext.listrow ) ) { // put each field into a column 290 GridLayout gridLayout = new GridLayout(1,0); 291 JPanel panel = new JPanel( gridLayout ); 292 for( Field field : objectClass.getDeclaredFields() ) { 293 JComponent childcomponent = objectToDisplayComponent( field.get( object ), field.getAnnotations(), DisplayContext.brief, canModify ); 294 gridLayout.setColumns( gridLayout.getColumns() + 1 ); 295 panel.add( childcomponent ); 296 } 297 return panel; 298 } else { 299 // then, we put each field into its own row 300 GridLayout gridLayout = new GridLayout(0,2); 301 JPanel panel = new JPanel( gridLayout ); 302 for( Field field : objectClass.getDeclaredFields() ) { 303 JLabel label = new JLabel( field.getName() ); 304 JComponent childcomponent = objectToDisplayComponent( field.get( object ), field.getAnnotations(), DisplayContext.brief, canModify ); 305 306 gridLayout.setRows( gridLayout.getRows() + 1 ); 307 308 panel.add( label ); 309 panel.add( childcomponent ); 310 } 311 return panel; 312 } 313 } catch( Exception e ) { 314 e.printStackTrace(); 315 throw new RuntimeException( e ); 316 } 317 318 // } 319 320 // throw new RuntimeException("UiReflectionhelper: unhandled type: " + objectClass.getSimpleName() ); 321 } 322 listToHeaders( Class<?> listClass )323 JComponent listToHeaders( Class<?> listClass ) { 324 if( listClass.isPrimitive() ) { 325 return null; 326 } 327 328 if( listClass.equals( String.class ) ) { 329 return null; 330 } 331 332 GridLayout gridLayout = new GridLayout( 1, 0 ); 333 JPanel panel = new JPanel( gridLayout ); 334 335 // assume custom class, for now 336 for( Field field : listClass.getDeclaredFields() ) { 337 if( isExcluded( field ) ) { 338 continue; 339 } 340 341 JLabel label = new JLabel( field.getName() ); 342 addGridLayoutColumn( gridLayout ); 343 panel.add( label ); 344 } 345 return panel; 346 } 347 addGridLayoutRow( GridLayout gridLayout )348 void addGridLayoutRow( GridLayout gridLayout ) { 349 gridLayout.setRows( gridLayout.getRows() + 1 ); 350 } 351 addGridLayoutColumn( GridLayout gridLayout )352 void addGridLayoutColumn( GridLayout gridLayout ) { 353 gridLayout.setColumns( gridLayout.getColumns() + 1 ); 354 } 355 isExcluded(Annotation[] annotations )356 boolean isExcluded(Annotation[] annotations ) { 357 for( Annotation annotation : annotations ) { 358 if( Exclude.class.isAssignableFrom( annotation.getClass() ) ) { 359 return true; 360 } 361 } 362 return false; 363 } 364 isExcluded( Field field )365 boolean isExcluded( Field field ) { 366 return isExcluded( field.getAnnotations() ); // not that efficient, but do we care? 367 } 368 getListItemClass( Annotation[] annotations )369 Class<?> getListItemClass( Annotation[] annotations ) { 370 for( Annotation annotation : annotations ) { 371 if( ListTypeInfo.class.isAssignableFrom( annotation.getClass() ) ) { 372 ListTypeInfo listTypeInfo = (ListTypeInfo)annotation; 373 return listTypeInfo.value(); 374 } 375 } 376 return null; 377 } 378 createDialog( Component lockedtoparentofthiscomponent, String title, boolean modal )379 JDialog createDialog( Component lockedtoparentofthiscomponent, String title, boolean modal ) { 380 if( JFrame.class.isAssignableFrom( lockedtoparentofthiscomponent.getClass() ) ) { 381 JDialog dialog = new JDialog( (JFrame)lockedtoparentofthiscomponent, 382 title, modal ); 383 return dialog; 384 } 385 if( JDialog.class.isAssignableFrom( lockedtoparentofthiscomponent.getClass() ) ) { 386 JDialog dialog = new JDialog( (JDialog)lockedtoparentofthiscomponent, 387 title, modal ); 388 return dialog; 389 } 390 if( lockedtoparentofthiscomponent == null ) { 391 throw new RuntimeException("UiReflectionHelper: couldn't find parent frame or dialog" ); 392 } 393 return createDialog( lockedtoparentofthiscomponent.getParent(), title, modal ); 394 } 395 396 // JComponent objectToTableHeaders( Object object, Annotation[] annotations ) { 397 // return null; // placeholder 398 // } 399 } 400