001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.jsp.util;
029
030import org.opencms.file.CmsObject;
031import org.opencms.file.CmsResource;
032import org.opencms.file.types.CmsResourceTypeFolderSubSitemap;
033import org.opencms.flex.CmsFlexController;
034import org.opencms.i18n.CmsLocaleManager;
035import org.opencms.i18n.CmsMessages;
036import org.opencms.json.JSONObject;
037import org.opencms.jsp.CmsJspResourceWrapper;
038import org.opencms.main.CmsException;
039import org.opencms.main.CmsLog;
040import org.opencms.main.OpenCms;
041import org.opencms.module.CmsModule;
042import org.opencms.util.CmsHtml2TextConverter;
043import org.opencms.util.CmsHtmlConverter;
044import org.opencms.util.CmsRequestUtil;
045import org.opencms.util.CmsStringUtil;
046import org.opencms.util.CmsUUID;
047
048import java.text.DateFormat;
049import java.text.ParseException;
050import java.util.ArrayList;
051import java.util.Collection;
052import java.util.Collections;
053import java.util.Date;
054import java.util.List;
055import java.util.Locale;
056import java.util.Map;
057
058import javax.servlet.ServletRequest;
059import javax.servlet.http.HttpServletRequest;
060import javax.servlet.jsp.PageContext;
061
062import org.apache.commons.beanutils.PropertyUtils;
063import org.apache.commons.logging.Log;
064
065import com.google.common.collect.Maps;
066
067/**
068 * Provides utility methods to be used as functions from a JSP with the EL.<p>
069 *
070 * @since 7.0.2
071 *
072 * @see CmsJspContentAccessBean
073 */
074public final class CmsJspElFunctions {
075
076    /** Logger instance for this class. */
077    private static final Log LOG = CmsLog.getLog(CmsJspElFunctions.class);
078
079    /**
080     * Hide the public constructor.<p>
081     */
082    private CmsJspElFunctions() {
083
084        // NOOP
085    }
086
087    /**
088     * Extends the given list by adding the provided object.<p>
089     *
090     * @param list the list to extend
091     * @param value the value to add to the list
092     */
093    public static void addToList(List<Object> list, Object value) {
094
095        list.add(value);
096    }
097
098    /**
099     * Returns an OpenCms user context created from an Object.<p>
100     *
101     * <ul>
102     * <li>If the input is already a {@link CmsObject}, it is casted and returned unchanged.
103     * <li>If the input is a {@link ServletRequest}, the OpenCms user context is read from the request context.
104     * <li>If the input is a {@link PageContext}, the OpenCms user context is read from the request of the page context.
105     * <li>Otherwise the input is converted to a String which should be a user name, and creation of a OpenCms
106     * user context with this name is attempted. Please note that this will only work if the user name is
107     * either the "Guest" user or the "Export" user.
108     * <li>If no valid OpenCms user context could be created with all of the above, then a new user context for
109     * the "Guest" user is created.
110     * </ul>
111     *
112     * @param input the input to create an OpenCms user context from
113     *
114     * @return an OpenCms user context created from an Object
115     */
116    public static CmsObject convertCmsObject(Object input) {
117
118        CmsObject result;
119        if (input instanceof CmsObject) {
120            result = (CmsObject)input;
121        } else if (input instanceof ServletRequest) {
122            result = CmsFlexController.getCmsObject((ServletRequest)input);
123        } else if (input instanceof PageContext) {
124            result = CmsFlexController.getCmsObject(((PageContext)input).getRequest());
125        } else {
126            try {
127                // try to use the given name as user name
128                result = OpenCms.initCmsObject(String.valueOf(input));
129                // try to set the right site root
130                ServletRequest req = convertRequest(input);
131                if (req instanceof HttpServletRequest) {
132                    result.getRequestContext().setSiteRoot(
133                        OpenCms.getSiteManager().matchRequest((HttpServletRequest)req).getSiteRoot());
134                }
135            } catch (CmsException e) {
136                LOG.warn(e.getLocalizedMessage(), e);
137                result = null;
138            }
139        }
140        if (result == null) {
141            try {
142                result = OpenCms.initCmsObject(OpenCms.getDefaultUsers().getUserGuest());
143                // try to set the right site root
144                ServletRequest req = convertRequest(input);
145                if (req instanceof HttpServletRequest) {
146                    result.getRequestContext().setSiteRoot(
147                        OpenCms.getSiteManager().matchRequest((HttpServletRequest)req).getSiteRoot());
148                }
149            } catch (CmsException e1) {
150                // this should never fail since we can always create a "Guest" user
151            }
152        }
153        return result;
154    }
155
156    /**
157     * Returns a Date created from an Object.<p>
158     *
159     * <ul>
160     * <li>The Object is first checked if it is a {@link Date} already, if so it is casted and returned unchanged.
161     * <li>If not, the input is checked if it is a {@link Long}, and if so the Date is created from the Long value.
162     * <li>If it's not a Date and not a Long, the Object is transformed to a String and then it's tried
163     * to parse a Long out of the String.
164     * <li>If this fails, it is tried to parse as a Date using the
165     * default date formatting.
166     * <li>If this also fails, a new Date is returned that has been initialized with 0.<p>
167     * </ul>
168     *
169     * @param input the Object to create a Date from
170     *
171     * @return a Date created from the given Object
172     */
173    public static Date convertDate(Object input) {
174
175        Date result;
176        if (input instanceof Date) {
177            result = (Date)input;
178        } else if (input instanceof Long) {
179            result = new Date(((Long)input).longValue());
180        } else {
181            String str = String.valueOf(input);
182            try {
183                // treat the input as a String
184                long l = Long.parseLong(str);
185                result = new Date(l);
186            } catch (NumberFormatException e) {
187                try {
188                    // try to parse String as a Date
189                    result = DateFormat.getDateInstance().parse(str);
190                } catch (ParseException e1) {
191                    result = null;
192                }
193                if (result == null) {
194                    // use default date if parsing fails
195                    result = new Date(0);
196                }
197            }
198        }
199        return result;
200    }
201
202    /**
203     * Returns a Double created from an Object.<p>
204     *
205     * <ul>
206     * <li>If the input already is a {@link java.lang.Number}, this number is returned as Double.
207     * <li>If the input is of type {@link A_CmsJspValueWrapper}, the wrapper is converted to a Double using {@link A_CmsJspValueWrapper#getToDouble()}.
208     * <li>If the input is a String, it is converted to a Double.
209     * <li>Otherwise <code>null</code> is returned.
210     * </li>
211     *
212     * @param input the Object to create a Double from
213     *
214     * @return a Double created from the given Object
215     */
216    public static Double convertDouble(Object input) {
217
218        Double result = null;
219        if (input instanceof Double) {
220            result = (Double)input;
221        } else if (input instanceof Number) {
222            result = Double.valueOf(((Number)input).doubleValue());
223        } else if (input instanceof A_CmsJspValueWrapper) {
224            result = ((A_CmsJspValueWrapper)input).getToDouble();
225        } else {
226            if (input != null) {
227                String str = String.valueOf(input);
228                // support both "10,0" and "10.0" comma style
229                str = str.replace(',', '.');
230                try {
231                    result = Double.valueOf(str);
232                } catch (NumberFormatException e) {
233                    LOG.info(e.getLocalizedMessage());
234                }
235            }
236        }
237        return result;
238    }
239
240    /**
241     * Returns a list of attribute values specified by the attribute name of the items of the given list.<p>
242     *
243     * @param input the list of objects to obtain the attribute values from
244     * @param attributeName the name of the attribute to obtain
245     * @return a list of attributes specified by the attribute name of the items of the given list
246     */
247    public static List<Object> convertList(List<Object> input, String attributeName) {
248
249        List<Object> result = new ArrayList<Object>(input.size());
250
251        for (Object item : input) {
252            try {
253                result.add(PropertyUtils.getProperty(item, attributeName));
254            } catch (Exception e) {
255                // specified attribute is not implemented, return empty list
256                return Collections.emptyList();
257            }
258        }
259
260        return result;
261    }
262
263    /**
264     * Returns a Locale created from an Object.<p>
265     *
266     * <ul>
267     * <li>The Object is first checked if it is a {@link Locale} already, if so it is casted and returned.
268     * <li>If not, the input is transformed to a String and then a Locale lookup with this String is done.
269     * <li>If the locale lookup fails, the OpenCms default locale is returned.
270     * </ul>
271     *
272     * @param input the Object to create a Locale from
273     *
274     * @return a Locale created from the given Object
275     */
276    public static Locale convertLocale(Object input) {
277
278        Locale locale;
279        if (input instanceof Locale) {
280            locale = (Locale)input;
281        } else {
282            locale = CmsLocaleManager.getLocale(String.valueOf(input));
283        }
284        return locale;
285    }
286
287    /**
288     * Returns a resource created from an Object.<p>
289     *
290     * <ul>
291     * <li>If the input is already a {@link CmsResource}, it is casted to the resource and returned unchanged.
292     * <li>If the input is a String, the given OpenCms context is used to read a resource with this name from the VFS.
293     * <li>If the input is a {@link CmsUUID}, the given OpenCms context is used to read a resource with
294     * this UUID from the VFS.
295     * <li>Otherwise the input is converted to a String, and then the given OpenCms context is used to read
296     * a resource with this name from the VFS.
297     * </ul>
298     *
299     * @param cms the current OpenCms user context
300     * @param input the input to create a resource from
301     *
302     * @return a resource created from the given Object
303     *
304     * @throws CmsException in case of errors accessing the OpenCms VFS for reading the resource
305     */
306    public static CmsResource convertRawResource(CmsObject cms, Object input) throws CmsException {
307
308        CmsResource result;
309        if (input instanceof CmsResource) {
310            // input is already a resource
311            result = (CmsResource)input;
312        } else if (input instanceof String) {
313            if (CmsUUID.isValidUUID((String)input)) {
314                // input is a UUID as String
315                result = cms.readResource(CmsUUID.valueOf((String)input));
316            } else {
317                // input is a path as String
318                result = cms.readResource(cms.getRequestContext().removeSiteRoot((String)input));
319            }
320        } else if (input instanceof CmsUUID) {
321            // input is a UUID
322            result = cms.readResource((CmsUUID)input);
323        } else {
324            // input seems not really to make sense, try to use it like a String
325            result = cms.readResource(String.valueOf(input));
326        }
327        return result;
328    }
329
330    /**
331     * Tries to convert the given input object into a request.<p>
332     *
333     * This is only possible if the input object is already a request
334     * or if it is a page context.<p>
335     *
336     * If everything else, this method returns <code>null</code>.<p>
337     *
338     * @param input the input object to convert to a request
339     *
340     * @return a request object, or <code>null</code>
341     */
342    public static ServletRequest convertRequest(Object input) {
343
344        ServletRequest req = null;
345        if (input instanceof ServletRequest) {
346            req = (ServletRequest)input;
347        } else if (input instanceof PageContext) {
348            req = ((PageContext)input).getRequest();
349        }
350        return req;
351    }
352
353    /**
354     * Returns a resource wrapper created from the input.
355     *
356     * The wrapped result of {@link #convertRawResource(CmsObject, Object)} is returned.
357     *
358     * @param cms the current OpenCms user context
359     * @param input the input to create a resource from
360     *
361     * @return a resource wrapper created from the given Object
362     *
363     * @throws CmsException in case of errors accessing the OpenCms VFS for reading the resource
364     */
365    public static CmsJspResourceWrapper convertResource(CmsObject cms, Object input) throws CmsException {
366
367        CmsJspResourceWrapper result;
368        if (input instanceof CmsResource) {
369            result = CmsJspResourceWrapper.wrap(cms, (CmsResource)input);
370        } else {
371            result = CmsJspResourceWrapper.wrap(cms, convertRawResource(cms, input));
372        }
373        return result;
374    }
375
376    /**
377     * Returns a list of resource wrappers created from the input list of resources.
378     *
379     * @param cms the current OpenCms user context
380     * @param list the list to create the resource wrapper list from
381     *
382     * @return the list of wrapped resources.
383     */
384    public static List<CmsJspResourceWrapper> convertResourceList(CmsObject cms, List<CmsResource> list) {
385
386        List<CmsJspResourceWrapper> result = new ArrayList<CmsJspResourceWrapper>(list.size());
387        for (CmsResource res : list) {
388            result.add(CmsJspResourceWrapper.wrap(cms, res));
389        }
390        return result;
391    }
392
393    /**
394     * Returns a CmsUUID created from an Object.<p>
395     *
396     * <ul>
397     * <li>The Object is first checked if it is a {@link CmsUUID} already, if so it is casted and returned.
398     * <li>If not, the input is transformed to a byte[] and a new {@link CmsUUID} is created with this byte[].
399     * <li>Otherwise the input is casted to a String and a new {@link CmsUUID} is created with this String.
400     * </ul>
401     *
402     * @param input the Object to create a CmsUUID from
403     *
404     * @return a CmsUUID created from the given Object
405     */
406    public static CmsUUID convertUUID(Object input) {
407
408        CmsUUID uuid;
409        if (input instanceof CmsUUID) {
410            uuid = (CmsUUID)input;
411        } else if (input instanceof byte[]) {
412            uuid = new CmsUUID((byte[])input);
413        } else {
414            uuid = new CmsUUID(String.valueOf(input));
415        }
416        return uuid;
417    }
418
419    /**
420     * Returns a newly created, empty List object.<p>
421     *
422     * There is no way to create an empty list using standard JSTL methods,
423     * hence this function.<p>
424     *
425     * @return a newly created, empty List object
426     */
427    public static List<Object> createList() {
428
429        return new ArrayList<Object>();
430    }
431
432    /**
433     * Returns the current OpenCms user context from the given page context.<p>
434     *
435     * @param input the input to create a CmsObject from
436     *
437     * @return the current OpenCms user context from the given page context
438     */
439    public static CmsObject getCmsObject(Object input) {
440
441        return convertCmsObject(input);
442    }
443
444    /**
445     * Returns the size of the given list.<p>
446     *
447     * @param input the list of objects to obtain the size from
448     * @return the size of the given list
449     */
450    public static Integer getListSize(Collection<Object> input) {
451
452        if (input != null) {
453            return Integer.valueOf(input.size());
454        }
455        // input was null
456        return Integer.valueOf(0);
457    }
458
459    /**
460     * Returns a parameter value from the module parameters.<p>
461     *
462     * @param name the name of the module
463     * @param key the parameter to return the value for
464     * @return the parameter value from the module parameters, or <code>null</code> if the parameter is not set
465     */
466    public static String getModuleParam(String name, String key) {
467
468        CmsModule module = OpenCms.getModuleManager().getModule(name);
469        if (module != null) {
470            return module.getParameter(key);
471        }
472        return null;
473    }
474
475    /**
476     * Returns the current request URI.<p>
477     *
478     * For OpenCms 10.5, this is the same as using <code>${cms.requestContext.uri}</code> on a JSP.<p>
479     *
480     * @param input the request convertible object to get the navigation URI from
481     *
482     * @return the current navigation URI
483     *
484     * @deprecated On a JSP use <code>${cms.requestContext.uri}</code> instead.
485     */
486    @Deprecated
487    public static String getNavigationUri(Object input) {
488
489        ServletRequest req = convertRequest(input);
490        if (req == null) {
491            return null;
492        }
493        return getCmsObject(input).getRequestContext().getUri();
494    }
495
496    /**
497     * Returns the link without parameters from a String that is formatted for a GET request.<p>
498     *
499     * @param url the URL to remove the parameters from
500     * @return the link without parameters
501     */
502    public static String getRequestLink(String url) {
503
504        return CmsRequestUtil.getRequestLink(url);
505    }
506
507    /**
508     * Returns the value of a parameter from a String that is formatted for a GET request.<p>
509     *
510     * @param url the URL to get the parameter value from
511     * @param paramName the request parameter name
512     * @return the value of the parameter
513     */
514    public static String getRequestParam(String url, String paramName) {
515
516        Map<String, String[]> params = Collections.emptyMap();
517        if (CmsStringUtil.isNotEmpty(url)) {
518            int pos = url.indexOf(CmsRequestUtil.URL_DELIMITER);
519            if (pos >= 0) {
520                params = CmsRequestUtil.createParameterMap(url.substring(pos + 1));
521            }
522        }
523        String[] result = params.get(paramName);
524        if (result != null) {
525            return result[0];
526        }
527        return null;
528    }
529
530    /**
531     * Returns a JSP / EL VFS access bean.<p>
532     *
533     * @param input the Object to create a CmsObject from
534     *
535     * @return a JSP / EL VFS access bean
536     */
537    public static CmsJspVfsAccessBean getVfsAccessBean(Object input) {
538
539        return CmsJspVfsAccessBean.create(CmsJspElFunctions.convertCmsObject(input));
540    }
541
542    /**
543     * Returns whether the given resource is a sub sitemap folder.<p>
544     *
545     * @param resource the resource to check
546     *
547     * @return <code>true</code> if the given resource is a sub sitemap folder
548     */
549    public static boolean isSubSitemap(CmsResource resource) {
550
551        return (resource != null) && CmsResourceTypeFolderSubSitemap.isSubSitemap(resource);
552    }
553
554    /**
555     * Returns whether the given value is an instance of {@link A_CmsJspValueWrapper}.<p>
556     *
557     * @param value the value object to check
558     *
559     * @return <code>true</code> if the given value is an instance of {@link A_CmsJspValueWrapper}
560     */
561    public static boolean isWrapper(Object value) {
562
563        return (value instanceof A_CmsJspValueWrapper);
564    }
565
566    /**
567     * Parses the JSON String and returns the requested value.<p>
568     *
569     * @param maybeJsonString the JSON string
570     * @param key the key
571     *
572     * @return the json value string
573     */
574    public static String jsonGetString(Object maybeJsonString, Object key) {
575
576        try {
577            if (maybeJsonString == null) {
578                return null;
579            }
580            String jsonString = (String)maybeJsonString;
581            if (CmsStringUtil.isEmptyOrWhitespaceOnly(jsonString)) {
582                return null;
583            }
584            JSONObject json = new JSONObject(jsonString);
585            String keyString = (String)key;
586            return json.optString(keyString);
587        } catch (Exception e) {
588            return null;
589        }
590    }
591
592    /**
593     * Converts a string (which is assumed to contain a JSON object whose values are strings only) to a map, for use in JSPs.<p>
594     *
595     * If the input can't be interpreted as JSON, an empty map is returned.
596     *
597     * @param jsonString the JSON string
598     * @return the map with the keys/values from the JSON
599     */
600    public static Map<String, String> jsonToMap(String jsonString) {
601
602        Map<String, String> result = Maps.newHashMap();
603        if (jsonString != null) {
604            try {
605                JSONObject json = new JSONObject(jsonString);
606                for (String key : json.keySet()) {
607                    String value = json.optString(key);
608                    if (value != null) {
609                        result.put(key, value);
610                    }
611                }
612            } catch (Exception e) {
613                LOG.warn(e.getLocalizedMessage(), e);
614            }
615        }
616        return result;
617    }
618
619    /**
620     * Looks up the given key from the map that is passed as a String, and returns either the
621     * element found or the empty String.<p>
622     *
623     * The map String must have the form <code>"key1:value1|key2:value2"</code> etc.<p>
624     *
625     * @param key the key to look up
626     * @param map the map represented as a String
627     * @return the element found in the map with the given key, or the empty String
628     */
629    public static String lookup(String key, String map) {
630
631        return lookup(key, map, "");
632    }
633
634    /**
635     * Looks up the given key from the map that is passed as a String, and returns either the
636     * element found or the default value.<p>
637     *
638     * The map String must have the form <code>"key1:value1|key2:value2"</code> etc.<p>
639     *
640     * @param key the key to look up
641     * @param map the map represented as a String
642     * @param defaultValue the default value
643     * @return the element found in the map with the given key, or the default value
644     */
645    public static String lookup(String key, String map, String defaultValue) {
646
647        Map<String, String> values = CmsStringUtil.splitAsMap(map, "|", ":");
648        String result = values.get(key);
649        if (CmsStringUtil.isEmptyOrWhitespaceOnly(result)) {
650            return defaultValue;
651        }
652        return result;
653    }
654
655    /**
656     * Calculates the next largest integer for the given number parameter.<p>
657     *
658     * Note that the result is an Object of type {@link java.lang.Long},
659     * so in case the parameter can not be converted to a number, <code>null</code> is returned.<p>
660     *
661     * @param param an Object that will be converted to a number
662     *
663     * @return the next largest integer for the given number parameter
664     */
665    public static Long mathCeil(Object param) {
666
667        Long result = null;
668        if ((param instanceof Long) || (param instanceof Integer)) {
669            result = Long.valueOf(((Number)param).longValue());
670        } else {
671            Double d = convertDouble(param);
672            if (d != null) {
673                result = Long.valueOf((long)Math.ceil(d.doubleValue()));
674            }
675        }
676        return result;
677    }
678
679    /**
680     * Calculates the next smallest integer for the given number parameter.<p>
681     *
682     * Note that the result is an Object of type {@link java.lang.Long},
683     * so in case the parameter can not be converted to a number, <code>null</code> is returned.<p>
684     *
685     * @param param an Object that will be converted to a number
686     *
687     * @return the next smallest integer for the given number parameter
688     */
689    public static Long mathFloor(Object param) {
690
691        Long result = null;
692        if ((param instanceof Long) || (param instanceof Integer)) {
693            result = Long.valueOf(((Number)param).longValue());
694        } else {
695            Double d = convertDouble(param);
696            if (d != null) {
697                result = Long.valueOf((long)Math.floor(d.doubleValue()));
698            }
699        }
700        return result;
701    }
702
703    /**
704     * Calculates the next integer for the given number parameter by rounding.<p>
705     *
706     * Note that the result is an Object of type {@link java.lang.Long},
707     * so in case the parameter can not be converted to a number, <code>null</code> is returned.<p>
708     *
709     * @param param an Object that will be converted to a number
710     *
711     * @return the next integer for the given number parameter calculated by rounding
712     */
713    public static Long mathRound(Object param) {
714
715        Long result = null;
716        if ((param instanceof Long) || (param instanceof Integer)) {
717            result = Long.valueOf(((Number)param).longValue());
718        } else {
719            Double d = convertDouble(param);
720            if (d != null) {
721                result = Long.valueOf(Math.round(d.doubleValue()));
722            }
723        }
724        return result;
725    }
726
727    /**
728     * Repairs the given HTML input by adding potentially missing closing tags.<p>
729     *
730     * @param input the HTML input
731     *
732     * @return the repaired HTML or an empty string in case of errors
733     */
734    public static String repairHtml(String input) {
735
736        CmsHtmlConverter converter = new CmsHtmlConverter();
737        String result = converter.convertToStringSilent(input);
738        return result == null ? "" : result;
739    }
740
741    /**
742     * Strips all HTML markup from the given input.<p>
743     *
744     * <ul>
745     * <li>In case the input is an instance of {@link CmsJspContentAccessValueWrapper}, an optimized
746     * method is used for the HTML stripping.
747     * <li>Otherwise the input is converted to a String and this String is stripped.
748     * </ul>
749     *
750     * @param input the input to Strip from HTML
751     *
752     * @return the given input with all HTML stripped.
753     */
754    public static String stripHtml(Object input) {
755
756        if (input instanceof CmsJspContentAccessValueWrapper) {
757            CmsJspContentAccessValueWrapper wrapper = (CmsJspContentAccessValueWrapper)input;
758            if (wrapper.getExists()) {
759                return wrapper.getContentValue().getPlainText(wrapper.getCmsObject());
760            } else {
761                return "";
762            }
763        }
764        try {
765            return CmsHtml2TextConverter.html2text(String.valueOf(input), OpenCms.getSystemInfo().getDefaultEncoding());
766        } catch (Exception e) {
767            return CmsMessages.formatUnknownKey(e.getMessage());
768        }
769    }
770
771    /**
772     * Converts the given Object to a (Double) number, falling back to a default if this is not possible.<p>
773     *
774     * In case even the default can not be converted to a number, <code>-1.0</code> is returned.<p>
775     *
776     * @param input the Object to convert to a (Double) number
777     * @param def the fall back default value in case the conversion is not possible
778     *
779     * @return the given Object converted to a (Double) number
780     */
781    public static Double toNumber(Object input, Object def) {
782
783        Double result = convertDouble(input);
784        if (result == null) {
785            result = convertDouble(def);
786        }
787        if (result == null) {
788            result = new Double(-1.0);
789        }
790        return result;
791    }
792
793    /**
794     * Returns a substring of the source, which is at most length characters long.<p>
795     *
796     * If a char is cut, <code>" ..."</code> is appended to the result.<p>
797     *
798     * @param input the string to trim
799     * @param length the maximum length of the string to be returned
800     *
801     * @return a substring of the source, which is at most length characters long
802     *
803     * @see CmsStringUtil#trimToSize(String, int, String)
804     */
805    public static String trimToSize(String input, int length) {
806
807        return CmsStringUtil.trimToSize(input, length, " ...");
808    }
809
810    /**
811     * Validates a value against a regular expression.<p>
812     *
813     * @param value the value
814     * @param regex the regex
815     *
816     * @return <code>true</code> if the value satisfies the validation
817     */
818    public static boolean validateRegex(String value, String regex) {
819
820        return CmsStringUtil.validateRegex(value, regex, true);
821    }
822}