1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.jetspeed.container.state.impl;
18
19 import java.io.UnsupportedEncodingException;
20 import java.util.ArrayList;
21 import java.util.Iterator;
22 import java.util.Map;
23
24 import javax.portlet.PortletMode;
25 import javax.portlet.WindowState;
26
27 import org.apache.commons.codec.binary.Base64;
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30 import org.apache.jetspeed.JetspeedActions;
31 import org.apache.jetspeed.PortalContext;
32 import org.apache.jetspeed.PortalReservedParameters;
33 import org.apache.jetspeed.container.window.PortletWindowAccessor;
34 import org.apache.pluto.om.window.PortletWindow;
35
36 /***
37 * JetspeedNavigationalStateCodec
38 *
39 * @author <a href="mailto:ate@apache.org">Ate Douma</a>
40 * @version $Id: JetspeedNavigationalStateCodec.java 554926 2007-07-10 13:12:26Z ate $
41 */
42 public class JetspeedNavigationalStateCodec implements NavigationalStateCodec
43 {
44 /*** Commons logging */
45 protected final static Log log = LogFactory.getLog(JetspeedNavigationalStateCodec.class);
46
47 protected static final char PARAMETER_SEPARATOR = '|';
48 protected static final char PARAMETER_ELEMENT_SEPARATOR = '=';
49 protected static final char RENDER_WINDOW_ID_KEY = 'a';
50 protected static final char ACTION_WINDOW_ID_KEY = 'b';
51 protected static final char MODE_KEY = 'c';
52 protected static final char STATE_KEY = 'd';
53 protected static final char PARAM_KEY = 'e';
54 protected static final char CLEAR_PARAMS_KEY = 'f';
55 protected static final char RESOURCE_WINDOW_ID_KEY = 'g';
56
57 protected static final String keytable = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
58 protected final PortletMode[] portletModes;
59 protected final WindowState[] windowStates;
60 private final PortletWindowAccessor windowAccessor;
61
62 public JetspeedNavigationalStateCodec(PortalContext portalContext, PortletWindowAccessor windowAccessor)
63 {
64 ArrayList list = new ArrayList();
65 this.windowAccessor = windowAccessor;
66
67
68
69
70 list.addAll(JetspeedActions.getStandardPortletModes());
71 list.addAll(JetspeedActions.getExtendedPortletModes());
72
73 portletModes = (PortletMode[])list.toArray(new PortletMode[list.size()]);
74 if (portletModes.length > keytable.length())
75 {
76 throw new UnsupportedOperationException("Too many supported PortletModes found. Can only handle max: "+keytable.length());
77 }
78
79 list.clear();
80
81
82
83
84 list.addAll(JetspeedActions.getStandardWindowStates());
85 list.addAll(JetspeedActions.getExtendedWindowStates());
86
87 windowStates = (WindowState[])list.toArray(new WindowState[list.size()]);
88 if (windowStates.length > keytable.length())
89 {
90 throw new UnsupportedOperationException("Too many supported WindowModes found. Can only handle max: "+keytable.length());
91 }
92 }
93
94 public PortletWindowRequestNavigationalStates decode(String parameters, String characterEncoding)
95 throws UnsupportedEncodingException
96 {
97 PortletWindowRequestNavigationalStates states = new PortletWindowRequestNavigationalStates(characterEncoding);
98 if ( parameters != null && parameters.length() > 0 )
99 {
100 String decodedParameters = decodeParameters(parameters, characterEncoding);
101
102 int position = 0;
103 StringBuffer buffer = new StringBuffer();
104
105 PortletWindowRequestNavigationalState currentState = null;
106 String parameter;
107 while ( (position = decodeArgument(position, decodedParameters, buffer, PARAMETER_SEPARATOR )) != -1 )
108 {
109 parameter = buffer.toString();
110 currentState = decodeParameter( windowAccessor, states, currentState, parameter);
111 }
112
113 if ( log.isDebugEnabled() )
114 {
115 logDecode(states, buffer);
116 if ( buffer.length() > 0 )
117 {
118 buffer.append("]");
119 log.debug("navstate decoded="+buffer.toString());
120 }
121 }
122 }
123 return states;
124 }
125
126 private void logDecode(PortletWindowRequestNavigationalStates states, StringBuffer buffer)
127 {
128 PortletWindowRequestNavigationalState currentState;
129 buffer.setLength(0);
130 String actionWindowId = states.getActionWindow() != null ? states.getActionWindow().getId().toString() : "";
131 Iterator iter = states.getWindowIdIterator();
132 while ( iter.hasNext() )
133 {
134 if ( buffer.length() == 0 )
135 {
136 buffer.append("[[");
137 }
138 else
139 {
140 buffer.append(",[");
141 }
142 currentState = states.getPortletWindowNavigationalState((String)iter.next());
143 buffer.append("window:"+currentState.getWindowId());
144
145 if ( currentState.getWindowId().equals(actionWindowId))
146 {
147 buffer.append(",action:true");
148 }
149 if (currentState.getPortletMode() != null)
150 {
151 buffer.append(",mode:"+currentState.getPortletMode());
152 }
153 if (currentState.getWindowState() != null )
154 {
155 buffer.append(",state:"+currentState.getWindowState());
156 }
157 if (!currentState.isClearParameters())
158 {
159 if (currentState.getParametersMap() != null)
160 {
161 buffer.append(",parameters:[");
162 boolean first = true;
163 Iterator parIter = currentState.getParametersMap().keySet().iterator();
164 while ( parIter.hasNext() )
165 {
166 if ( first )
167 {
168 first = false;
169 }
170 else
171 {
172 buffer.append(",");
173 }
174 String name = (String)parIter.next();
175 buffer.append(name+":[");
176 String[] values = (String[])currentState.getParametersMap().get(name);
177 for ( int i = 0; i < values.length; i++ )
178 {
179 if ( i > 0 )
180 {
181 buffer.append(",");
182 }
183 buffer.append(values[i]);
184 }
185 buffer.append("]");
186 }
187 }
188 }
189 buffer.append("]");
190 }
191 }
192
193 public String encode(PortletWindowRequestNavigationalStates states, PortletWindow window, PortletMode portletMode,
194 WindowState windowState, boolean navParamsStateFull, boolean renderParamsStateFull)
195 throws UnsupportedEncodingException
196 {
197 String windowId = window.getId().toString();
198 PortletWindowRequestNavigationalState currentState = states.getPortletWindowNavigationalState(windowId);
199 PortletWindowRequestNavigationalState targetState = new PortletWindowRequestNavigationalState(windowId);
200 targetState.setPortletMode(portletMode != null ? portletMode : currentState != null ? currentState.getPortletMode() : null);
201 targetState.setWindowState(windowState != null ? windowState : currentState != null ? currentState.getWindowState() : null);
202
203
204 if ( currentState != null && !renderParamsStateFull )
205 {
206
207 if ( currentState.getParametersMap() != null )
208 {
209 Iterator parametersIter = currentState.getParametersMap().entrySet().iterator();
210 Map.Entry entry;
211 while ( parametersIter.hasNext())
212 {
213 entry = (Map.Entry)parametersIter.next();
214 targetState.setParameters((String)entry.getKey(), (String[])entry.getValue());
215 }
216 }
217 }
218
219 return encode(states, windowId, targetState, false, false, navParamsStateFull, renderParamsStateFull);
220 }
221
222 public String encode(PortletWindowRequestNavigationalStates states, PortletWindow window, Map parameters,
223 PortletMode portletMode, WindowState windowState, boolean action, boolean navParamsStateFull,
224 boolean renderParamsStateFull)
225 throws UnsupportedEncodingException
226 {
227 String windowId = window.getId().toString();
228 PortletWindowRequestNavigationalState currentState = states.getPortletWindowNavigationalState(windowId);
229 PortletWindowRequestNavigationalState targetState = new PortletWindowRequestNavigationalState(windowId);
230 targetState.setPortletMode(portletMode != null ? portletMode : currentState != null ? currentState.getPortletMode() : null);
231 targetState.setWindowState(windowState != null ? windowState : currentState != null ? currentState.getWindowState() : null);
232
233 Iterator parametersIter = parameters.entrySet().iterator();
234
235 boolean resource = false;
236
237 Map.Entry entry;
238 String parameter;
239
240 while ( parametersIter.hasNext())
241 {
242 entry = (Map.Entry)parametersIter.next();
243 parameter = (String)entry.getKey();
244 if (!resource && !action && PortalReservedParameters.PORTLET_RESOURCE_URL_REQUEST_PARAMETER.equals(parameter))
245 {
246 resource = true;
247 navParamsStateFull = true;
248 renderParamsStateFull = true;
249 }
250 else
251 {
252 targetState.setParameters(parameter, (String[])entry.getValue());
253 }
254 }
255 if ( renderParamsStateFull && targetState.getParametersMap() == null )
256 {
257
258
259 targetState.setClearParameters(true);
260 }
261 return encode(states, windowId, targetState, action, resource, navParamsStateFull, renderParamsStateFull);
262 }
263
264 public String encode(PortletWindowRequestNavigationalStates states, boolean navParamsStateFull, boolean renderParamsStateFull)
265 throws UnsupportedEncodingException
266 {
267 return encode(states, null, null, false, false, navParamsStateFull, renderParamsStateFull);
268 }
269 protected String encode(PortletWindowRequestNavigationalStates states, String targetWindowId,
270 PortletWindowRequestNavigationalState targetState, boolean action, boolean resource, boolean navParamsStateFull,
271 boolean renderParamsStateFull)
272 throws UnsupportedEncodingException
273 {
274 StringBuffer buffer = new StringBuffer();
275 String encodedState;
276 boolean haveState = false;
277
278
279 if ( !navParamsStateFull || !renderParamsStateFull )
280 {
281 PortletWindowRequestNavigationalState pwfns;
282 String windowId;
283 Iterator iter = states.getWindowIdIterator();
284 while ( iter.hasNext() )
285 {
286 windowId = (String)iter.next();
287 pwfns = states.getPortletWindowNavigationalState(windowId);
288 if ( targetWindowId != null && windowId.equals(targetWindowId))
289 {
290
291 }
292 else
293 {
294 encodedState = encodePortletWindowNavigationalState(windowId, pwfns, false, false, navParamsStateFull,
295 renderParamsStateFull);
296 if ( encodedState.length() > 0 )
297 {
298 if ( !haveState )
299 {
300 haveState = true;
301 }
302 else
303 {
304 buffer.append(PARAMETER_SEPARATOR);
305 }
306 buffer.append(encodedState);
307 }
308 }
309 }
310 }
311 if (targetWindowId != null)
312 {
313 encodedState = encodePortletWindowNavigationalState(targetWindowId, targetState, action, resource, false, false);
314 if ( encodedState.length() > 0 )
315 {
316 if ( !haveState )
317 {
318 haveState = true;
319 }
320 else
321 {
322 buffer.append(PARAMETER_SEPARATOR);
323 }
324 buffer.append(encodedState);
325 }
326 }
327 String encodedNavState = null;
328 if ( haveState )
329 {
330 encodedNavState = encodeParameters(buffer.toString(), states.getCharacterEncoding());
331 }
332 return encodedNavState;
333 }
334
335 protected String encodePortletWindowNavigationalState(String windowId, PortletWindowRequestNavigationalState state,
336 boolean action, boolean resource, boolean navParamsStateFull, boolean renderParamsStateFull)
337 {
338 StringBuffer buffer = new StringBuffer();
339 buffer.append(action ? ACTION_WINDOW_ID_KEY : resource? RESOURCE_WINDOW_ID_KEY: RENDER_WINDOW_ID_KEY);
340 buffer.append(windowId);
341 boolean encoded = action || resource;
342
343 if ( action || !navParamsStateFull )
344 {
345 if (state.getPortletMode() != null)
346 {
347 buffer.append(PARAMETER_SEPARATOR);
348 buffer.append(MODE_KEY);
349 buffer.append(encodePortletMode(state.getPortletMode()));
350 encoded = true;
351 }
352
353 if (state.getWindowState() != null)
354 {
355 buffer.append(PARAMETER_SEPARATOR);
356 buffer.append(STATE_KEY);
357 buffer.append(encodeWindowState(state.getWindowState()));
358 encoded = true;
359 }
360 }
361
362 if (state.getParametersMap() != null && (action || !renderParamsStateFull) )
363 {
364 Map.Entry entry;
365 String parameterName;
366 String[] parameterValues;
367
368 StringBuffer paramBuffer = new StringBuffer();
369 Iterator iter = state.getParametersMap().entrySet().iterator();
370 while ( iter.hasNext() )
371 {
372 encoded = true;
373 entry = (Map.Entry)iter.next();
374 parameterName = (String)entry.getKey();
375 parameterValues = (String[])entry.getValue();
376
377 buffer.append(PARAMETER_SEPARATOR);
378 buffer.append(PARAM_KEY);
379
380 paramBuffer.setLength(0);
381 paramBuffer.append(encodeArgument(parameterName, PARAMETER_ELEMENT_SEPARATOR));
382 paramBuffer.append(PARAMETER_ELEMENT_SEPARATOR);
383 paramBuffer.append(Integer.toHexString(parameterValues.length));
384 for ( int i = 0; i < parameterValues.length; i++ )
385 {
386 paramBuffer.append(PARAMETER_ELEMENT_SEPARATOR);
387 paramBuffer.append(encodeArgument(parameterValues[i], PARAMETER_ELEMENT_SEPARATOR));
388 }
389
390 buffer.append(encodeArgument(paramBuffer.toString(),PARAMETER_SEPARATOR));
391 }
392 }
393 else if ( state.isClearParameters() )
394 {
395
396
397
398
399 buffer.append(PARAMETER_SEPARATOR);
400 buffer.append(CLEAR_PARAMS_KEY);
401 encoded = true;
402 }
403 return encoded ? buffer.toString() : "";
404 }
405
406 protected PortletWindowRequestNavigationalState decodeParameter(PortletWindowAccessor accessor, PortletWindowRequestNavigationalStates states, PortletWindowRequestNavigationalState currentState, String parameter)
407 {
408 char parameterType = parameter.charAt(0);
409 if (parameterType == RENDER_WINDOW_ID_KEY || parameterType == ACTION_WINDOW_ID_KEY || parameterType == RESOURCE_WINDOW_ID_KEY )
410 {
411 String windowId = parameter.substring(1);
412 currentState = states.getPortletWindowNavigationalState(windowId);
413 if ( currentState == null )
414 {
415 PortletWindow window = accessor.getPortletWindow(windowId);
416 if ( window == null )
417 {
418 window = accessor.createPortletWindow(windowId);
419 }
420 currentState = new PortletWindowRequestNavigationalState(windowId);
421 states.addPortletWindowNavigationalState(windowId, currentState);
422 if ( parameterType == ACTION_WINDOW_ID_KEY )
423 {
424 states.setActionWindow(window);
425 }
426 else if (parameterType == RESOURCE_WINDOW_ID_KEY )
427 {
428 states.setResourceWindow(window);
429 }
430 }
431 }
432 else if ( currentState != null )
433 {
434 switch ( parameterType )
435 {
436 case MODE_KEY:
437 {
438 PortletMode portletMode = decodePortletMode(parameter.charAt(1));
439 if ( portletMode != null )
440 {
441 currentState.setPortletMode(portletMode);
442 }
443 break;
444 }
445 case STATE_KEY:
446 {
447 WindowState windowState = decodeWindowState(parameter.charAt(1));
448 if ( windowState != null )
449 {
450 currentState.setWindowState(windowState);
451 if (windowState.equals(WindowState.MAXIMIZED) || windowState.equals(JetspeedActions.SOLO_STATE))
452 {
453 PortletWindow window = accessor.getPortletWindow(currentState.getWindowId());
454 if ( window == null )
455 {
456 window = accessor.createPortletWindow(currentState.getWindowId());
457 }
458 states.setMaximizedWindow(window);
459 }
460 }
461 break;
462 }
463 case PARAM_KEY:
464 {
465 int position = 1;
466 StringBuffer buffer = new StringBuffer();
467 String parameterName = null;
468 int parameterValueCount = -1;
469 String parameterValues[] = null;
470 int parameterValueIndex = -1;
471 while ( (position = decodeArgument(position, parameter, buffer, PARAMETER_ELEMENT_SEPARATOR)) != -1 )
472 {
473 if ( parameterName == null )
474 {
475 parameterName = buffer.toString();
476 parameterValueCount = -1;
477 }
478 else if ( parameterValueCount == -1 )
479 {
480 parameterValueCount = Integer.parseInt(buffer.toString(), 16);
481 parameterValues = new String[parameterValueCount];
482 parameterValueIndex = 0;
483 }
484 else
485 {
486 parameterValues[parameterValueIndex++] = buffer.toString();
487 parameterValueCount--;
488 if ( parameterValueCount == 0 )
489 {
490 currentState.setParameters(parameterName, parameterValues);
491 break;
492 }
493 }
494 }
495 break;
496 }
497 case CLEAR_PARAMS_KEY:
498 {
499 currentState.setClearParameters(true);
500 }
501 }
502 }
503 return currentState;
504
505 }
506
507 protected PortletMode decodePortletMode(char mode)
508 {
509 PortletMode portletMode = null;
510 int index = keytable.indexOf(mode);
511 if (index > -1 && index < portletModes.length)
512 {
513 portletMode = portletModes[index];
514 }
515 return portletMode;
516 }
517
518 protected char encodePortletMode(PortletMode portletMode)
519 {
520 for ( int i = 0; i < portletModes.length; i++ )
521 {
522 if (portletModes[i].equals(portletMode))
523 return keytable.charAt(i);
524 }
525 throw new IllegalArgumentException("Unsupported PortletMode: "+portletMode);
526 }
527
528 protected WindowState decodeWindowState(char state)
529 {
530 WindowState windowState = null;
531 int index = keytable.indexOf(state);
532 if (index > -1 && index < windowStates.length)
533 {
534 windowState = windowStates[index];
535 }
536 return windowState;
537 }
538
539 protected char encodeWindowState(WindowState windowState)
540 {
541 for ( int i = 0; i < windowStates.length; i++ )
542 {
543 if (windowStates[i].equals(windowState))
544 return keytable.charAt(i);
545 }
546 throw new IllegalArgumentException("Unsupported WindowState: "+windowState);
547 }
548
549 /***
550 * Decodes a Base64 encoded string.
551 *
552 * Because the encoded string is used in an URL
553 * the two '/' and '=' which has some significance in an URL
554 * are encoded on top of the Base64 encoding and are first translated back before decoding.
555 *
556 * @param value
557 * @param characterEncoding String containing the name of the chararacter encoding
558 * @return decoded string
559 */
560 protected String decodeParameters(String value, String characterEncoding)
561 throws UnsupportedEncodingException
562 {
563 value = value.replace('-','/').replace('_','=');
564 if ( characterEncoding != null )
565 {
566 return new String(Base64.decodeBase64(value.getBytes(characterEncoding)), characterEncoding);
567 }
568 else
569 {
570 return new String(Base64.decodeBase64(value.getBytes()));
571 }
572 }
573
574 /***
575 * Encodes a string with Base64.
576 *
577 * Because the encoded string is used in an URL
578 * the two '/' and '=' which has some significance in an URL
579 * are encoded on top of/after the Base64 encoding
580 *
581 * @param value
582 * @return encoded string
583 */
584 protected String encodeParameters(String value, String characterEncoding)
585 throws UnsupportedEncodingException
586 {
587 if ( characterEncoding != null )
588 {
589 value = new String(Base64.encodeBase64(value.getBytes(characterEncoding)));
590 }
591 else
592 {
593 value = new String(Base64.encodeBase64(value.getBytes()));
594 }
595 return value.replace('/','-').replace('=','_');
596 }
597
598 protected String encodeArgument( String argument, char escape )
599 {
600 int length = argument.length();
601 StringBuffer buffer = new StringBuffer(length);
602 buffer.setLength(0);
603 char c;
604 for ( int i = 0; i < length; i++ )
605 {
606 c = argument.charAt(i);
607 buffer.append(c);
608 if ( c == escape )
609 {
610 buffer.append(c);
611 }
612 }
613 return buffer.toString();
614 }
615
616 protected int decodeArgument(int position, String arguments, StringBuffer buffer, char escape)
617 {
618 int maxLength = arguments.length();
619 buffer.setLength(0);
620 char c;
621 for ( ; position < maxLength; position++ )
622 {
623 c = arguments.charAt(position);
624 if ( c != escape )
625 {
626 buffer.append(c);
627 }
628 else
629 {
630 if ( c == escape && position < maxLength-1 && arguments.charAt(position+1) == escape )
631 {
632 buffer.append(c);
633 position++;
634 }
635 else
636 {
637 position++;
638 break;
639 }
640 }
641 }
642 return buffer.length() > 0 ? position : -1;
643 }
644 }