Preparing for Rails 2.3.9

As much as i wish to upgrade my Rails 2.3.x application Daily Fratze to the newest tag of the Rails 2.3.x branch, i cannot.

First there was the epic fail of release 2.3.6, that broke all HTML Helpers and forced the Rails XSS protection upon us. This release was immediately followed by 2.3.7 and 2.3.8. With my tests, this version was still enforcing Rails XSS and breaking helpers like “h”.

Rails 2.3.9, released last week, puts en end to this.

I just was about to upgrade, when i read this error: Textarea input silently truncated in 2.3.8!. The input of a textarea is truncated if the text entered consists of two lines or more with one of them quoted as the Rack middleware messes with the input.

I can confirm that this behavior still applies to 2.3.9.

It’s a shame, that all the talk is about Rails 3 with bugs like this in an older branch. I understand that this is a Rack problem but as it is already fixed in newer Rack versions, i cannot understand that the Rails team doesn’t bump the required Rack version respectively has no tests for problems like these.

So i’m hoping that Rails 2.3.10 sees the light of day anytime soon.

Anyway, as Rails 2.3.9 suddenly uses a new interpolation syntax for the translation files (“Hello {{name}}” becomes ” Hello %{name}”), here is a one-liner to update i18n files to the new syntax:

find . -iname "*.yml" -exec sed 's/{{\([^\{\}]*\)}}/%{\1}/g' -i {} \;

If you like some less escapism use xargs

find . -iname "*.yml"  | xargs sed 's/{{\([^{}]*\)}}/%{\1}/g' -i;

Commands need to be executed inside your locale directory.

| Comments (2) »

06-Sep-10


How to get UIDefaults in Java

If you’re loocking for Javas UIDefaults, use the UIManager class. This snippet gives you all installed UIDefaults:

UIDefaults defaults = UIManager.getDefaults();		 
for(Enumeration e = defaults.keys(); e.hasMoreElements(); ){
    String key = e.nextElement().toString();
    System.out.println(key + " = " + defaults.get(key));	         
}

| Comments (0) »

06-Sep-10


Find all non “.java” files but not in .svn directories

I’m migrating some old projects to Maven and i need to move all resources out of the source tree.

find . -type d -name '\.svn' -prune -o -type f -not -iname "*.java" -print
  • Prune every directory named ‘.svn’
  • Or
  • Type is file and the name ends not with “*.java”

I tried to create the necessary directories within the same command but after about half an hour xargs trial and error, i’ll do it manually, the list of files is small enough 😉

| Comments (0) »

19-Aug-10



J2SE: Cut/Copy/Paste Helper

You wouldn’t think that having a standard edit menü with Cut, Copy and Paste buttons would be much of a problem in the J2SE world, especially regarding the fact that most standard Swing components have TransferHandlers that support the 3 operations with the standard keyboard shortcuts.

First try was to user TransferHandler.getCopyAction() etc. and create menuitems from the actions. The actions will actually be called but they use the source of the action event to determine the component whose contents should be transfered.

After some searching i found this tutorial. The idea is to have a global property change handler on the global KeyboardFocusManager that tracks the current component that has the focus. The handler than updates the 3 actions with the new target.

I have changed the code a little bit. I removed the methods for adding special client parameters to determine whether cut/copy/paste should be enabled completely. Instead i check whether the component has a TransferHandler installed and if so, whether that handler supports the current action. By doing so my actions have the same behavior like the ones that are the swing components have.

The following code can be used to create menuitems or buttons that mimic the default cut/copy/paste actions on Swing components:

import java.awt.Component;
import java.awt.KeyboardFocusManager;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
 
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
import javax.swing.TransferHandler;
 
public final class CutCopyPasteHelper {
	private static final FocusedAction CUT_INSTANCE;
	private static final FocusedAction COPY_INSTANCE;
	private static final FocusedAction PASTE_INSTANCE;
 
	private static final Clipboard CLIPBOARD;
 
	private static final List<FocusedAction> FOCUSED_ACTIONS = new ArrayList<FocusedAction>();
 
	static {
		Clipboard clipboard;
		try {
			clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
		} catch (SecurityException e) {
			// Don't have access to the clipboard, create a new one
			clipboard = new Clipboard("Sandboxed Clipboard");
		}
		CLIPBOARD = clipboard;
 
		FOCUSED_ACTIONS.add(CUT_INSTANCE = new CutAction());
		FOCUSED_ACTIONS.add(COPY_INSTANCE = new CopyAction());
		FOCUSED_ACTIONS.add(PASTE_INSTANCE = new PasteAction());		
		KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener(new PropertyChangeHandler());
	}
 
	private static Clipboard getClipboard() {
		return CLIPBOARD;
	}
 
	/**
	 * Returns an action to perform a cut operation.
	 *
	 * @return the cut action
	 */
	public static Action getCutAction() {
		return CUT_INSTANCE;
	}
 
	/**
	 * Returns an action to perform a copy operation.
	 *
	 * @return the copy action
	 */
	public static Action getCopyAction() {
		return COPY_INSTANCE;
	}
 
	/**
	 * Returns an action to perform a paste operation.
	 *
	 * @return the paste action
	 */
	public static Action getPasteAction() {
		return PASTE_INSTANCE;
	}
 
	private static void updateActions(Component focusedComponent) {
		for(FocusedAction action : FOCUSED_ACTIONS)						
			action.update(focusedComponent);					
	}
 
	private static final class PropertyChangeHandler implements PropertyChangeListener {
		public void propertyChange(PropertyChangeEvent e) {
			if (e.getPropertyName() == "permanentFocusOwner") {
				updateActions((Component)e.getNewValue());
			}
		}
	}
 
	private CutCopyPasteHelper() {
	}
 
 
	private static abstract class FocusedAction extends AbstractAction {        
		private static final long serialVersionUID = 9134168335741527777L;		
		protected JComponent focusedComponent;
 
		public FocusedAction(String name) {
			super(name);
		}		
 
		protected void update(Component permanentFocusOwner) {
			this.focusedComponent = (permanentFocusOwner instanceof JComponent) ? (JComponent)permanentFocusOwner : null; 			
			this.checkTarget();
		}
 
		public JComponent getFocusedComponent() {
			return focusedComponent;
		}		
 
		public boolean actionSupported(int actionMap, int action) {	
			return (actionMap & action) != 0;
		}
 
		abstract protected void checkTarget();	
	}
 
	protected static void export(final int exportAction, final JComponent component) {
		final Clipboard clipboard = getClipboard();		
		component.getTransferHandler().exportToClipboard(component, clipboard, exportAction);
	}
 
	private static final class CopyAction extends FocusedAction {		
		private static final long serialVersionUID = 1765740238724009255L;
 
		public CopyAction() {
			super("Kopieren");	
			super.setEnabled(false);
			super.putValue(ACTION_COMMAND_KEY, "a6f9f030-c408-4531-857f-dd3aad7b43f6");
			super.putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_DOWN_MASK));
		}
 
		@Override
		public void actionPerformed(ActionEvent e) {			
			export(TransferHandler.COPY, focusedComponent);
		}
 
		@Override
		public void checkTarget() {
			super.setEnabled(focusedComponent != null && focusedComponent.getTransferHandler()!= null && actionSupported(focusedComponent.getTransferHandler().getSourceActions(focusedComponent), TransferHandler.COPY));
		}		
	}
 
	private static final class CutAction extends FocusedAction {		
		private static final long serialVersionUID = 1765740238724009255L;
 
		public CutAction() {
			super("Ausschneiden");	
			super.setEnabled(false);
			super.putValue(ACTION_COMMAND_KEY, "aa33f95a-c741-407a-a0ca-0c3aedd33c4d");
			super.putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_DOWN_MASK));
		}
 
		@Override
		public void actionPerformed(ActionEvent e) {			
			export(TransferHandler.MOVE, focusedComponent);
		}
 
		@Override
		public void checkTarget() {
			super.setEnabled(focusedComponent != null && focusedComponent.getTransferHandler()!= null && actionSupported(focusedComponent.getTransferHandler().getSourceActions(focusedComponent), TransferHandler.MOVE));
		}		
	}
 
 
	private static final class PasteAction extends FocusedAction {		
		private static final long serialVersionUID = -2117467682323608417L;
 
		public PasteAction() {
			super("Einfügen");	
			super.setEnabled(false);
			super.putValue(ACTION_COMMAND_KEY, "2676413d-8b9e-4d64-b59a-800db6747d80");
			super.putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK));
		}
 
		public void checkTarget() {			
			super.setEnabled(focusedComponent != null && focusedComponent.getTransferHandler() != null && focusedComponent.getTransferHandler().canImport(focusedComponent, getClipboard().getAvailableDataFlavors()));			
		}
 
		public void actionPerformed(ActionEvent e) {
			Clipboard clipboard = getClipboard();
			JComponent target = getFocusedComponent();
			target.getTransferHandler().importData(target, clipboard.getContents(null));
		}		
	}
}

What’s missing: The actions aren’t localized and they use Ctrl+? on every system. One could use the default Swing bundles to determine the correct modifier.

| Comments (2) »

09-Jul-10