package de.tud.kom.socom.web.client; import java.util.HashMap; import java.util.Map; import com.google.gwt.event.logical.shared.ValueChangeEvent; import de.tud.kom.socom.web.client.AppController.Presenters; import de.tud.kom.socom.web.client.util.exceptions.InvalidCodeStateException; import de.tud.kom.socom.web.client.util.exceptions.ParsingException; /** encapsulated handling of #xyz/abc/whatever tokens of History. Accepts leading / as well: #/xyz/abc/ * * @author jkonert * */ public final class HistoryToken { public static final String SEPERATOR = "/"; private static final int INDEX_GAME_PART = 0; private static final int INDEX_APP_PART = 1; private static final int INDEX_APP_PART_MODULE = 2; private static final int INDEX_APP_PART_MODULE_ACTION = 3; private static final int MAX_INDEX_SUPPORTED = 100; private static final String KEYVALUEPAIRS_SEPERATOR = "|"; private static final String KEYVALUE_SEPERATOR = "="; private String[] parts; public HistoryToken(String game, Presenters presenter, String... parts) { if (parts == null) parts = new String[0]; // for easier coding... this.parts = new String[parts.length+2]; setGamePart(game, true); setPresenter(presenter, false); for (int i =0; i<parts.length; i++) { setPart(i+1, parts[i], false); } } private HistoryToken (String[] parts) { this.parts = parts; } // private HistoryToken() // { // parts = new String[0]; // } /** Will not check the consistency of parameters but directly split and set the parts and return * * @param tokenstring * @return */ public static HistoryToken fromUrlHistoryTokenTrusted(String tokenstring) { String[] parts = null; if (tokenstring == null || tokenstring.equals("")) parts = new String[0]; while (tokenstring.startsWith(SEPERATOR)) { tokenstring = tokenstring.substring(1); } while (tokenstring.endsWith(SEPERATOR)) { tokenstring = tokenstring.substring(0, tokenstring.length()-1); } parts = tokenstring.split(SEPERATOR); return new HistoryToken(parts); } /** * @throws ParsingException in case some parameters are not properly set * @param tokenstring * @return */ public static HistoryToken fromUrlHistoryToken(String tokenstring) { HistoryToken tmp = HistoryToken.fromUrlHistoryTokenTrusted(tokenstring); tmp.setPresenter(tmp.getPresenter(Presenters.content), false); // only as a check // more checks to come... return tmp; } /** * @return the HistoryString similar to pattern #Game/Presenter/Module/Action/Parameter1/Parameter2. */ public String toHistoryTokenString() { StringBuilder st = new StringBuilder(); StringBuilder st2 = new StringBuilder(); for (String p: parts) { if (p == null) // buffer params in case only nulls follow...then do not add { st2.append(p).append(SEPERATOR); continue; } else if (st2.length() > 0) // append buffered params first then add the current one { st.append(st2); st2.delete(0, st2.length()); } st.append(p).append(SEPERATOR); // add current parameter } if (st.length() > 0) st.deleteCharAt(st.length()-1); if (st.length() > 0 && !String.valueOf(st.charAt(0)).equals(SEPERATOR)) st.insert(0, SEPERATOR); // add a leading "/" return st.toString(); } /** * returns the Module part of #Game/Presenter/Module/Action/Parameter1/Parameter2/Parameter3 * @return */ public String getGamePart() { return getPart(INDEX_GAME_PART); } public void setGamePart(String game, boolean clearChildParts) { setPart(INDEX_GAME_PART, game, clearChildParts); } /** * returns the Presenter part of #Game/Presenter/Module/Action/Parameter1/Parameter2/Parameter3 or NULL * @return */ public Presenters getPresenter() { return Presenters.findAppPart(getPart(INDEX_APP_PART), null); } /** * returns the Presenter part of #Game/Presenter/Module/Action/Parameter1/Parameter2/Parameter3 or the given default in case non found * @return */ public Presenters getPresenter(Presenters defaultValue) { return Presenters.findAppPart(getPart(INDEX_APP_PART), defaultValue); } public void setPresenter(Presenters part, boolean clearChildParts) { if (part == null) throw new UnsupportedOperationException("Presenter should not be null"); setPart(INDEX_APP_PART,part.name(), clearChildParts); } /** * returns the Module part of #Game/Presenter/Module/Action/Parameter1/Parameter2/Parameter3 * @return */ public String getPresenterModule() { return getPart(INDEX_APP_PART_MODULE); } public void setPresenterModule(String module, boolean clearChildParts) { setPart(INDEX_APP_PART_MODULE, module, clearChildParts); } /** * returns the Action part of #Game/Presenter/Module/Action/Parameter1/Parameter2/Parameter3 * @return */ public String getPresenterModuleAction() { return getPart(INDEX_APP_PART_MODULE_ACTION); } public void setPresenterModuleAction(String action, boolean clearChildParts) { setPart(INDEX_APP_PART_MODULE_ACTION, action, clearChildParts); } /** returns an array of all further parameters after #Presenter/Module/Action/ * * @return empty String[] or String[] containing all parameters after Action... */ public String[] getPresenterModuleActionParameters() { if (parts.length >= INDEX_APP_PART_MODULE_ACTION+1) { String[] params = new String[parts.length-INDEX_APP_PART_MODULE_ACTION]; for (int i=0; i < params.length; i++) { params[i] = parts[i+INDEX_APP_PART_MODULE_ACTION]; } return params; } else return new String[0]; } /** returns athe requested parameter after #Presenter/Module/Action/ like e.g. #Game/Presenter/Module/Action/Parameter0/Parameter1/Parameter2 * @throws UnsupportedOperationException if index is < 0. * @param index index of parameter to return 0....n * @return an empty string or the parameter in URL after the Action (starting with Parameter 0) */ public String getPresenterModuleActionParameter(int index) { if (index < 0) throw new InvalidCodeStateException("Index cannot be below zero ("+index+")"); if (parts.length >= INDEX_APP_PART_MODULE_ACTION+index+1) { return parts[INDEX_APP_PART_MODULE_ACTION+index]; } else return ""; } public void setPresenterModuleActionParameter(int index, String value, boolean clearChildParts) { if (index < 0) throw new InvalidCodeStateException("Index cannot be below zero ("+index+")"); setPart(INDEX_APP_PART_MODULE_ACTION+index,value,clearChildParts); } /** returns ONE of the parameters of the Presenter/Module/Action parameters as key=value|key2=value2|.. parsed Map<String,String> of key->value pairs. * Keys may not be null for this. Values may not contain any of the seperator chars. * @throws ParsingException in case no parsing is possible. If only ONE value without any "=" is found the exception is thrown as well. * @param index index of parameter to return as a map 0....n * @return an empty map in case the parameter is empty, otherwise am key/value map of the parsed parameter. values can be null */ public Map<String,String> getPresenterModuleActionParameterKeyValuePairs(int index) { String part = getPart(index); HashMap<String, String> map = new HashMap<String, String>(); if (part.length() == 0) return map; String[] keyvaluepairs = part.split(KEYVALUEPAIRS_SEPERATOR); for (String kv:keyvaluepairs) { String[] keyvalue = kv.split(KEYVALUE_SEPERATOR); if (keyvalue.length != 2) throw new ParsingException("Key-Value_Pairs did not match the pattern <key>"+KEYVALUE_SEPERATOR+"<value>"+KEYVALUEPAIRS_SEPERATOR+"<key2>"+KEYVALUE_SEPERATOR+"<value2>"); if (keyvalue[0] == null || keyvalue[0] == "") throw new ParsingException("KEY in Key-Value-Pair is missing ("+kv+")"); if (map.containsKey(keyvalue[0])) throw new ParsingException("KEY is more than once given; not unique ("+kv+")"); map.put(keyvalue[0], keyvalue[1]); } return map; } public void setPresenterModuleActionParameterKeyValueMap(int index, Map<String, String> keyValuePairs, boolean clearChildParts) { if (keyValuePairs == null ||keyValuePairs.size() == 0) { setPart(index, keyValuePairs == null?null:"", clearChildParts); return; } StringBuilder sb = new StringBuilder(); for (String key: keyValuePairs.keySet()) { if (key.contains(KEYVALUE_SEPERATOR) || key.contains(KEYVALUEPAIRS_SEPERATOR)) throw new ParsingException("Key ("+key+") may not contain the chars for seperating Key-Value-Pairs ("+KEYVALUE_SEPERATOR+" "+KEYVALUEPAIRS_SEPERATOR+")"); String value = keyValuePairs.get(key); if (value != null && (value.contains(KEYVALUE_SEPERATOR) || value.contains(KEYVALUEPAIRS_SEPERATOR))) throw new ParsingException("Value ("+value+") may not contain the chars for seperating Key-Value-Pairs ("+KEYVALUE_SEPERATOR+" "+KEYVALUEPAIRS_SEPERATOR+")"); sb.append(key).append(KEYVALUE_SEPERATOR).append(value).append(KEYVALUEPAIRS_SEPERATOR); } if (sb.length()>0) sb.deleteCharAt(sb.length()-1); setPart(index, sb.toString(), clearChildParts); } /** direct access to Token-Parts without semantics. * @throws UnsupportedOperationException if index is < 0. * @param index 0...n where 0 means the Presenter #Game/Presenter/Module/Action/Parameter0/Parameter1/Parameter2 * @return the requested part or an empty String */ public String getTokenPart(int index) { return getPart(index); } /** * direct access to Token-Parts setting without semantics * @param index 0...n where 0 means the Presenter in #Game/Presenter/Module/Action/Parameter0/Parameter1/Parameter2 * @param value * @param clearChildParts */ public void setTokenPart(int index, String value, boolean clearChildParts) { if (index < 0) throw new InvalidCodeStateException("Index cannot be below zero ("+index+")"); setPart(index,value,clearChildParts); } public HistoryToken clone() { return HistoryToken.fromUrlHistoryTokenTrusted(this.toHistoryTokenString()); } private void setPart(int index, String value, boolean clearChildParts) { if (index > MAX_INDEX_SUPPORTED) throw new InvalidCodeStateException("index is to big ("+index+"). Supports maximum index of "+MAX_INDEX_SUPPORTED); if (value != null && value.contains(SEPERATOR)) throw new ParsingException("value may not contain Seperator characters ("+SEPERATOR+")"); if (parts.length <= index) { // resize array String[] tmp = new String[index+1]; for (int i=0; i < parts.length; i++) { tmp[i] = parts[i]; } parts = tmp; } parts[index] = value; if (clearChildParts) { for (int i=index+1; i < parts.length; i++) { parts[i] = null; } } } private String getPart(int index) { if (index < 0) throw new InvalidCodeStateException("Index cannot be below zero ("+index+")"); if (parts.length <= index) return ""; String p = parts[index]; return (p == null) ? "" : p; } public static HistoryToken fromValueChangeEvent( ValueChangeEvent<String> event) { return HistoryToken.fromUrlHistoryToken(event.getValue()); } }