001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.wicket.extensions.markup.html.repeater.data.sort;
018:
019: import org.apache.wicket.AttributeModifier;
020: import org.apache.wicket.Component;
021: import org.apache.wicket.IClusterable;
022: import org.apache.wicket.markup.html.link.Link;
023: import org.apache.wicket.model.Model;
024: import org.apache.wicket.util.lang.Objects;
025: import org.apache.wicket.version.undo.Change;
026:
027: /**
028: * A component that represents a sort header. When the link is clicked it will
029: * toggle the state of a sortable property within the sort state object.
030: *
031: * @author Phil Kulak
032: * @author Igor Vaynberg (ivaynberg)
033: */
034: public class OrderByLink extends Link {
035: private static final long serialVersionUID = 1L;
036:
037: /** sortable property */
038: private String property;
039:
040: /** locator for sort state object */
041: private ISortStateLocator stateLocator;
042:
043: /**
044: * Constructor.
045: *
046: * @param id
047: * the component id of the link
048: * @param property
049: * the name of the sortable property this link represents. this
050: * value will be used as parameter for sort state object methods.
051: * sort state object will be located via the stateLocator
052: * argument.
053: * @param stateLocator
054: * locator used to locate sort state object that this will use to
055: * read/write state of sorted properties
056: */
057: public OrderByLink(String id, String property,
058: ISortStateLocator stateLocator) {
059: this (id, property, stateLocator, DefaultCssProvider
060: .getInstance());
061: }
062:
063: /**
064: * Constructor.
065: *
066: * @param id
067: * the component id of the link
068: * @param property
069: * the name of the sortable property this link represents. this
070: * value will be used as parameter for sort state object methods.
071: * sort state object will be located via the stateLocator
072: * argument.
073: * @param stateLocator
074: * locator used to locate sort state object that this will use to
075: * read/write state of sorted properties
076: * @param cssProvider
077: * CSS provider that will be used generate the value of class
078: * attribute for this link
079: *
080: * @see OrderByLink.ICssProvider
081: *
082: */
083: public OrderByLink(String id, String property,
084: ISortStateLocator stateLocator, ICssProvider cssProvider) {
085: super (id);
086:
087: if (cssProvider == null) {
088: throw new IllegalArgumentException(
089: "argument [cssProvider] cannot be null");
090: }
091:
092: if (property == null) {
093: throw new IllegalArgumentException(
094: "argument [sortProperty] cannot be null");
095: }
096:
097: this .property = property;
098: this .stateLocator = stateLocator;
099: add(new CssModifier(this , cssProvider));
100: }
101:
102: /**
103: * @see org.apache.wicket.markup.html.link.Link
104: */
105: public final void onClick() {
106: sort();
107: onSortChanged();
108: }
109:
110: /**
111: * This method is a hook for subclasses to perform an action after sort has
112: * changed
113: */
114: protected void onSortChanged() {
115: // noop
116: }
117:
118: /**
119: * Re-sort data provider according to this link
120: *
121: * @return this
122: */
123: public final OrderByLink sort() {
124: if (isVersioned()) {
125: // version the old state
126: Change change = new SortStateChange();
127: addStateChange(change);
128: }
129:
130: ISortState state = stateLocator.getSortState();
131:
132: int oldDir = state.getPropertySortOrder(property);
133:
134: int newDir = ISortState.ASCENDING;
135:
136: if (oldDir == ISortState.ASCENDING) {
137: newDir = ISortState.DESCENDING;
138: }
139:
140: state.setPropertySortOrder(property, newDir);
141:
142: return this ;
143: }
144:
145: private final class SortStateChange extends Change {
146: private static final long serialVersionUID = 1L;
147:
148: private final ISortState old = (ISortState) Objects
149: .cloneModel(stateLocator.getSortState());
150:
151: /**
152: * @see org.apache.wicket.version.undo.Change#undo()
153: */
154: public void undo() {
155: stateLocator.setSortState(old);
156: }
157:
158: /**
159: * @see java.lang.Object#toString()
160: */
161: public String toString() {
162: return "[StateOrderChange old=" + old.toString() + "]";
163: }
164: }
165:
166: /**
167: * Uses the specified ICssProvider to add css class attributes to the link.
168: *
169: * @author Igor Vaynberg ( ivaynberg )
170: *
171: */
172: public static class CssModifier extends AttributeModifier {
173: private static final long serialVersionUID = 1L;
174:
175: /**
176: * @param link
177: * the link this modifier is being added to
178: * @param provider
179: * implementation of ICssProvider
180: */
181: public CssModifier(final OrderByLink link,
182: final ICssProvider provider) {
183: super ("class", true, new Model() {
184: private static final long serialVersionUID = 1L;
185:
186: public Object getObject() {
187:
188: final ISortState sortState = link.stateLocator
189: .getSortState();
190: return provider.getClassAttributeValue(sortState,
191: link.property);
192: }
193:
194: public void setObject(Object object) {
195: throw new UnsupportedOperationException();
196: }
197:
198: });
199: }
200:
201: /**
202: * @see org.apache.wicket.AttributeModifier#isEnabled(Component)
203: */
204: public boolean isEnabled(Component component) {
205: return getReplaceModel().getObject() != null;
206: }
207: };
208:
209: /**
210: * Interface used to generate values of css class attribute for the anchor
211: * tag If the generated value is null class attribute will not be added
212: *
213: * @author igor
214: */
215: public static interface ICssProvider extends IClusterable {
216: /**
217: * @param state
218: * current sort state
219: * @param property
220: * sort property represented by the {@link OrderByLink}
221: * @return the value of the "class" attribute for the given sort
222: * state/sort property combination
223: */
224: public String getClassAttributeValue(ISortState state,
225: String property);
226: }
227:
228: /**
229: * Easily constructible implementation of ICSSProvider
230: *
231: * @author Igor Vaynberg (ivaynberg)
232: *
233: */
234: public static class CssProvider implements ICssProvider {
235: private static final long serialVersionUID = 1L;
236:
237: private String ascending;
238:
239: private String descending;
240:
241: private String none;
242:
243: /**
244: * @param ascending
245: * css class when sorting is ascending
246: * @param descending
247: * css class when sorting is descending
248: * @param none
249: * css class when not sorted
250: */
251: public CssProvider(String ascending, String descending,
252: String none) {
253: this .ascending = ascending;
254: this .descending = descending;
255: this .none = none;
256: }
257:
258: /**
259: * @see org.apache.wicket.extensions.markup.html.repeater.data.sort.OrderByLink.ICssProvider#getClassAttributeValue(org.apache.wicket.extensions.markup.html.repeater.data.sort.ISortState,
260: * java.lang.String)
261: */
262: public String getClassAttributeValue(ISortState state,
263: String property) {
264: int dir = state.getPropertySortOrder(property);
265: if (dir == ISortState.ASCENDING) {
266: return ascending;
267: } else if (dir == ISortState.DESCENDING) {
268: return descending;
269: } else {
270: return none;
271: }
272: }
273: }
274:
275: /**
276: * Convineince implementation of ICssProvider that always returns a null and
277: * so never adds a class attribute
278: *
279: * @author Igor Vaynberg ( ivaynberg )
280: */
281: public static class VoidCssProvider extends CssProvider {
282: private static final long serialVersionUID = 1L;
283:
284: private static ICssProvider instance = new VoidCssProvider();
285:
286: /**
287: * @return singleton instance
288: */
289: public static ICssProvider getInstance() {
290: return instance;
291: }
292:
293: private VoidCssProvider() {
294: super ("", "", "");
295: }
296: }
297:
298: /**
299: * Default implementation of ICssProvider
300: *
301: * @author Igor Vaynberg ( ivaynberg )
302: */
303: public static class DefaultCssProvider extends CssProvider {
304: private static final long serialVersionUID = 1L;
305:
306: private static DefaultCssProvider instance = new DefaultCssProvider();
307:
308: private DefaultCssProvider() {
309: super ("wicket_orderUp", "wicket_orderDown",
310: "wicket_orderNone");
311: }
312:
313: /**
314: * @return singleton instance
315: */
316: public static DefaultCssProvider getInstance() {
317: return instance;
318: }
319: }
320:
321: }
|