content: /*
   * Copyright (C) 2010-2014 - Andreas Maier 
   * CONRAD is developed as an Open Source project under the GNU General Public License (GPL).
  */
  package edu.stanford.rsl.apps.gui;
  
  import java.awt.Color;
  import java.awt.Component;
  import java.awt.Dimension;
  import java.awt.Font;
  import java.awt.Graphics;
  import java.awt.Graphics2D;
  import java.awt.GridBagConstraints;
  import java.awt.Rectangle;
  import java.awt.datatransfer.DataFlavor;
  import java.awt.datatransfer.UnsupportedFlavorException;
  import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
  import java.awt.font.FontRenderContext;
  import java.awt.font.LineBreakMeasurer;
  import java.awt.font.TextLayout;
  import java.io.File;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.InputStreamReader;
  import java.lang.reflect.Field;
  import java.text.AttributedCharacterIterator;
  import java.text.AttributedString;
  import java.text.BreakIterator;
  import java.util.ArrayList;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Set;
  
  import javax.swing.DropMode;
  import javax.swing.JButton;
  import javax.swing.JFrame;
  import javax.swing.JPanel;
  import javax.swing.JScrollPane;
  import javax.swing.JTable;
  import javax.swing.ListSelectionModel;
  import javax.swing.TransferHandler;
  import javax.swing.table.DefaultTableCellRenderer;
  import javax.swing.table.DefaultTableModel;
  import javax.swing.table.TableCellRenderer;
  
  import com.thoughtworks.qdox.JavaDocBuilder;
  import com.thoughtworks.qdox.model.JavaClass;
  import com.thoughtworks.qdox.model.JavaField;
  
  import edu.stanford.rsl.conrad.utils.CONRAD;
  import edu.stanford.rsl.conrad.utils.Configuration;
  import edu.stanford.rsl.conrad.utils.RegKeys;
  import edu.stanford.rsl.conrad.utils.UserUtil;
  
  
  public class RegistryEditor extends JPanel implements ActionListener {
  
  	/**
  	 * 
  	 */
  	private static final long serialVersionUID = -8853718009928894577L;
  	private Configuration config;
  	private JButton add;
  	private JButton delete;
  	private JButton showKeys;
  	private JTable table;
  
  	public RegistryEditor(Configuration config){
  		super();
  		this.config = config;
  	}
  
  	public void initGUI(){
  		this.setLayout(ConfigurationFrame.createSubPaneLayout());
  		this.setBackground(Color.WHITE);
  		Set<String> keys = config.getRegistryKeys();
  
  		String [][] tableData = new String [keys.size()][2];
  		String [] label = {"Key", "Value"};
  
  		Iterator<String> iter = keys.iterator();
  		int i = 0;
  		while (iter.hasNext()){
  			tableData[i][0] = iter.next();
  			tableData[i][1] = config.getRegistryEntry(tableData[i][0]);
  			i++;
  		}
  
  		table = new JTable();
  		DefaultTableModel model = new DefaultTableModel(tableData, label);
  		table.setModel(model);
  		table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
  		table.setFillsViewportHeight(true);
  		table.setDropMode(DropMode.ON);
  		table.setPreferredScrollableViewportSize(new Dimension(495, 270));
  
  
  		table.setTransferHandler(new TransferHandler() {
  
  			/**
  			 * 
  			 */
  			private static final long serialVersionUID = 6224911732660417665L;
  
  			public boolean canImport(TransferSupport support) {
  				// for the demo, we'll only support drops (not clipboard paste)
  				if (!support.isDrop()) {
  					return false;
  				}
  
  				// we only import Strings and files
  				if (!(support.isDataFlavorSupported(DataFlavor.stringFlavor)||support.isDataFlavorSupported(DataFlavor.javaFileListFlavor))) {
  					return false;
  				}
  
  				return true;
  			}
  
  			@SuppressWarnings("unchecked")
  			public boolean importData(TransferSupport support) {
  				// if we can't handle the import, say so
  				if (!canImport(support)) {
  					return false;
  				}
  
  				// fetch the drop location
  				JTable.DropLocation dl = (JTable.DropLocation) support
  				.getDropLocation();
  
  				int row = dl.getRow();
  
  				// fetch the data and bail if this fails
  				String data;
  				try {
  					data = (String) support.getTransferable().getTransferData(
  							DataFlavor.stringFlavor);
  				} catch (UnsupportedFlavorException e) {
  					try {
  						java.util.List<File> l =
  							(java.util.List<File>) support.getTransferable().getTransferData(DataFlavor.javaFileListFlavor);
  						data = l.get(0).getAbsolutePath();
  
  					} catch (UnsupportedFlavorException e1) {
  						// TODO Auto-generated catch block
  						e1.printStackTrace();
  						return false;
  					} catch (IOException e1) {
  						// TODO Auto-generated catch block
  						e1.printStackTrace();
  						return false;
  					}			
  				} catch (IOException e) {
  					return false;
  				}
  				DefaultTableModel model = (DefaultTableModel) table.getModel();
  				model.setValueAt(data, row, 1);
  
  				Rectangle rect = table.getCellRect(row, 0, false);
  				if (rect != null) {
  					table.scrollRectToVisible(rect);
  				}
  
  				return true;
  			}
  		});
  
  		JScrollPane scroll = new JScrollPane(table);
  
  		add(scroll, ConfigurationFrame.createConstraints(1, 1, 3, 2, GridBagConstraints.NORTH, GridBagConstraints.BOTH, 0, 0, 0, 0));
  
  		add = new JButton("Add key");
  		add.addActionListener(this);
  		delete = new JButton("Remove key");
  		delete.addActionListener(this);
  		showKeys = new JButton("Show available keys");
  		showKeys.addActionListener(this);
  		add(add, ConfigurationFrame.createConstraints(1, 0, 1, 1, GridBagConstraints.WEST, GridBagConstraints.NONE, 5, 0, 5, 0));
  		add(showKeys, ConfigurationFrame.createConstraints(2, 0, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.NONE, 5, 0, 5, 0));
  		add(delete, ConfigurationFrame.createConstraints(3, 0, 1, 1, GridBagConstraints.EAST, GridBagConstraints.NONE, 5, 0, 5, 0));
  		setOpaque(true);
  	}
  
  
  	public static void main(String [] args){
  		Configuration.loadConfiguration();
  		JFrame frame =  new JFrame();
  		frame.add(new RegistryEditor(Configuration.getGlobalConfiguration()));
  		frame.setVisible(true);
  		frame.setSize(new Dimension(495, 270));
  		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  	}
  
  	JFrame keysDescription;
  	JTable descTable;
  
  	public class MultilineTableCell 
  	implements TableCellRenderer {
  		class CellArea extends DefaultTableCellRenderer {
  			/**
  			 * 
  			 */
  			private static final long serialVersionUID = 1L;
  			private String text;
  			protected int rowIndex;
  			protected int columnIndex;
  			protected JTable table;
  			protected Font font;
  			private int paragraphStart,paragraphEnd;
  			private LineBreakMeasurer lineMeasurer;
  
  			public CellArea(String s, JTable tab, int row, int column,boolean isSelected) {
  				text = s;
  				rowIndex = row;
  				columnIndex = column;
  				table = tab;
  				font = table.getFont();
  				if (isSelected) {
  					setForeground(table.getSelectionForeground());
  					setBackground(table.getSelectionBackground());
  				}
  			}
  			
  			public void paintComponent(Graphics gr) {
  				super.paintComponent(gr);
  				if ( text != null && !text.isEmpty() ) {
  					Graphics2D g = (Graphics2D) gr;
  					if (lineMeasurer == null) {
  						AttributedCharacterIterator paragraph = new AttributedString(text).getIterator();
  						paragraphStart = paragraph.getBeginIndex();
  						paragraphEnd = paragraph.getEndIndex();
  						FontRenderContext frc = g.getFontRenderContext();
  						lineMeasurer = new LineBreakMeasurer(paragraph,BreakIterator.getWordInstance(), frc);
  					}
  					float breakWidth = (float)table.getColumnModel().getColumn(columnIndex).getWidth();
  					float drawPosY = 0;
  					// Set position to the index of the first character in the paragraph.
  					lineMeasurer.setPosition(paragraphStart);
  					// Get lines until the entire paragraph has been displayed.
  					while (lineMeasurer.getPosition() < paragraphEnd) {
  						// Retrieve next layout. A cleverer program would also cache
  						// these layouts until the component is re-sized.
  						TextLayout layout = lineMeasurer.nextLayout(breakWidth);
  						// Compute pen x position. If the paragraph is right-to-left we
  						// will align the TextLayouts to the right edge of the panel.
  						// Note: this won't occur for the English text in this sample.
  						// Note: drawPosX is always where the LEFT of the text is placed.
  						float drawPosX = layout.isLeftToRight()
  						? 0 : breakWidth - layout.getAdvance();
  						// Move y-coordinate by the ascent of the layout.
  						drawPosY += layout.getAscent();
  						// Draw the TextLayout at (drawPosX, drawPosY).
  						layout.draw(g, drawPosX, drawPosY);
  						// Move y-coordinate in preparation for next layout.
  						drawPosY += layout.getDescent() + layout.getLeading();
  					}
  					table.setRowHeight(rowIndex,(int) drawPosY);
  				}
  			}
  		}
  		public Component getTableCellRendererComponent(
  				JTable table, Object value,boolean isSelected, boolean hasFocus, int row,int column
  		)
  		{
  			CellArea area = new CellArea(value.toString(),table,row,column,isSelected);
  			return area;
  		}   
  	}
  
  	/**
  	 * Example for getting comments from a java file using qdox.
  	 * This requires access to the actual java file in the list of ressources.
  	 * (Use source folder as output folder for class files in eclipse)
  	 */
  	private void generateKeyTableFromSources(){
  		// read java sources to parser from qdox
  		InputStream stream = RegKeys.class.getResourceAsStream("RegKeys.java");
  		InputStreamReader reader = new InputStreamReader(stream);
  		JavaDocBuilder builder = new JavaDocBuilder();
  		builder.addSource(reader);
  		
  		// generate table with line wrapping cells in colum 2
  		final int wordWrapColumnIndex = 1;
  		descTable = new JTable() {    
  			/**
  			 * 
  			 */
  			private static final long serialVersionUID = 7516482246121817003L;
  
  			public TableCellRenderer getCellRenderer(int row, int column) {
  				if (column == wordWrapColumnIndex ) {
  					return new MultilineTableCell();
  				}
  				else {
  					return super.getCellRenderer(row, column);
  				}
  			}
  		};
  		
  		// Use qdox parser to parse RegKeys sources
  		JavaClass cls = builder.getClassByName("edu.stanford.rsl.conrad.utils.RegKeys");
  		// get list of fields
  		JavaField[] fields = cls.getFields();
  
  		// build table model
  		String [][] tableData = new String [fields.length][2];
  		String [] label = {"Key", "Description"};
  		int i = 0;
  		for (JavaField field : fields){
  			tableData[i][0] = field.getName();
  			tableData[i][1] = field.getComment();
  			if (tableData[i][1] != null){
  				tableData[i][1] = field.getComment().replace("<BR>", "\n")	
  				.replace("<br>", "\n")
  				.replace("<b>", "\n")
  				.replace("</b>", "\n");
  			} else {
  				tableData[i][1] = "";
  			}
  			i++;
  		}
  
  		DefaultTableModel model = new DefaultTableModel(tableData, label);
  		descTable.setModel(model);
  	}
  	
  	private void generateKeyTableFromClassFiles(){
  		// get fields from class file
  		Field[] declaredFields = RegKeys.class.getDeclaredFields();
  		List<Field> staticFields = new ArrayList<Field>();
  		// add only static fields
  		for (Field field : declaredFields) {
  			if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
  				staticFields.add(field);
  			}
  		}
  		descTable = new JTable();
  		// build table model
  		String [][] tableData = new String [staticFields.size()][2];
  		String [] label = {"Key", "Description"};
  		int i = 0;
  		for (Field field : staticFields){
  			tableData[i][0] = field.getName();
  			tableData[i][1] = "Comment unavailable. (Use source folder as output folder for class files in eclipse or include sources in jar file.)";
  			i++;
  		}
  		DefaultTableModel model = new DefaultTableModel(tableData, label);
  		descTable.setModel(model);
  	}
  	
  	public void actionPerformed(ActionEvent e) {
  		if (e.getSource().equals(add)){
  			try {
  				String key = UserUtil.queryString("Enter key name:", "");
  				DefaultTableModel model = (DefaultTableModel) table.getModel();
  				boolean found = false;
  				for (int i = 0; i < model.getRowCount(); i++){
  					if (model.getValueAt(i, 0).equals(key)) found = true;
  				}
  				if (! found) {
  					model.addRow(new String [] {key, ""});
  					model.fireTableDataChanged();
  				}
  			} catch (Exception e1) {
  				// TODO Auto-generated catch block
  				e1.printStackTrace();
  			}	
  		}
  		if (e.getSource().equals(showKeys)){
  			try {
  				if (keysDescription ==null){
  					keysDescription = new JFrame("Available Keys");
  					keysDescription.setSize(640,480);
  					keysDescription.setBackground(Color.WHITE);
  					
  					try{
  						// Read key descriptions from source file
  						// getting comments requires access to the actual java files in the 
  						// resource path.
  						generateKeyTableFromSources();
  					} catch (Exception e2){
  						// Generate key list using introspection only
  						// will be executed if source is not found.
  						generateKeyTableFromClassFiles();
  					}
  					descTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
  					descTable.setFillsViewportHeight(true);
  					descTable.setPreferredScrollableViewportSize(new Dimension(640, 480));
  					keysDescription.add(new JScrollPane(descTable));
  					keysDescription.setVisible(true);
  					keysDescription.setLocation(CONRAD.getWindowTopCorner());
  					System.out.println("created");
  				} else {
  					if( keysDescription.isVisible()){
  						keysDescription.setVisible(false);
  						keysDescription = null;
  					} else {
  						keysDescription.setVisible(true);
  					}
  				}
  			} catch (Exception e1) {
  				// TODO Auto-generated catch block
  				e1.printStackTrace();
  			}	
  		}
  		if (e.getSource().equals(delete)){
  			if (table.getSelectedRowCount() == 1){
  				DefaultTableModel model = (DefaultTableModel) table.getModel();
  				int selectedRow = table.getSelectedRow();
  				model.removeRow(selectedRow);
  			} else {
  				// won't delete multiple keys at once.
  				table.getSelectionModel().clearSelection();
  			}
  		}
  
  	}
  
  	public void updateToConfiguration(){
  		config.resetRegistry();
  		DefaultTableModel model = (DefaultTableModel) table.getModel();
  		int lenght = model.getRowCount();
  		for (int i = 0; i < lenght; i++){
  			config.setRegistryEntry((String) model.getValueAt(i, 0), (String) model.getValueAt(i, 1));
  		}
  	}
  
  }
  
  
  