/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.isis.applib.util; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * Title buffer is a utility class to help produce titles for objects without * having to add lots of guard code. It provides two basic method: one to * concatenate a title to the buffer; another to append a title with a joiner * string, taking care adding in necessary spaces. The benefits of using this * class is that null references are safely ignored (rather than appearing as * 'null'), and joiners (a space by default) are only added when needed. */ public class TitleBuffer { private static final String SPACE = " "; public static final Class[] NO_PARAMETER_TYPES = new Class[0]; public static final Object[] NO_ARGUMENTS = new Object[0]; /** * Determines if the specified object's title is empty (or null). * * <p> * Note: this method only obtains the title using either <tt>title()</tt> or <tt>toString()</tt>; it doesn't * honour other mechanisms for specifying the title, such as {@link org.apache.isis.applib.annotation.Title} * annotation. If that functionality is required, first call * {@link org.apache.isis.applib.DomainObjectContainer#titleOf(Object)} on the object and pass in the resultant * string. * </p> */ public static boolean isEmpty(final Object object) { final String title = titleFor(object); return isEmpty(title); } /** * Reflectively run the <tt>String title()</tt> method if it exists, else * fall back to the <tt>toString()</tt> method. */ private static String titleFor(final Object object) { if (object == null) { return null; } if(object instanceof String) { return object.toString(); } try { Method method = object.getClass().getMethod("title", NO_PARAMETER_TYPES); return (String) method.invoke(object, NO_ARGUMENTS); } catch (final SecurityException | IllegalArgumentException | IllegalAccessException | InvocationTargetException e) { throw new TitleBufferException(e); } catch (final NoSuchMethodException e) { return object.toString(); } } /** * Determines if the specified text is empty. Will return true if either: * the specified reference is null; or if the reference is an empty string. */ public static boolean isEmpty(final String text) { return text == null || text.equals(""); } private final StringBuilder title; /** * Creates a new, empty, {@link org.apache.isis.applib.util.TitleBuffer}. */ public TitleBuffer() { title = new StringBuilder(); } /** * Creates a new {@link org.apache.isis.applib.util.TitleBuffer}, containing the title of the specified object. * * <p> * Note: this method only obtains the title using either <tt>title()</tt> or <tt>toString()</tt>; it doesn't * honour other mechanisms for specifying the title, such as {@link org.apache.isis.applib.annotation.Title} * annotation. If that functionality is required, first call * {@link org.apache.isis.applib.DomainObjectContainer#titleOf(Object)} on the object and pass in the resultant * string. * </p> */ public TitleBuffer(final Object object) { this(); concat(object); } /** * Creates a new title object, containing the title of the specified object. * * <p> * Note: this method only obtains the title using either <tt>title()</tt> or <tt>toString()</tt>; it doesn't * honour other mechanisms for specifying the title, such as {@link org.apache.isis.applib.annotation.Title} * annotation. If that functionality is required, first call * {@link org.apache.isis.applib.DomainObjectContainer#titleOf(Object)} on the object and pass in the resultant * string. * </p> */ public TitleBuffer(final Object object, final String defaultTitle) { this(); String title = titleFor(object); if (isEmpty(title)) { concat(defaultTitle); } else { concat(title); } } /** * Creates a new title object, containing the specified text. */ public TitleBuffer(final String text) { this(); concat(text); } /** * */ public TitleBuffer append(final int number) { append(String.valueOf(number)); return this; } /** * Append the title of the specified object to this {@link org.apache.isis.applib.util.TitleBuffer}. * * <p> * Note: this method only obtains the title using either <tt>title()</tt> or <tt>toString()</tt>; it doesn't * honour other mechanisms for specifying the title, such as {@link org.apache.isis.applib.annotation.Title} * annotation. If that functionality is required, first call * {@link org.apache.isis.applib.DomainObjectContainer#titleOf(Object)} on the object and pass in the resultant * string. * </p> */ public TitleBuffer append(final Object object) { String title = titleFor(object); if (!isEmpty(title)) { appendWithSpace(title); } return this; } /** * Appends the title of the specified object, or the specified text if the * objects title is null or empty. Prepends a space if there is already some * text in this title object. * * <p> * Note: this method only obtains the title using either <tt>title()</tt> or <tt>toString()</tt>; it doesn't * honour other mechanisms for specifying the title, such as {@link org.apache.isis.applib.annotation.Title} * annotation. If that functionality is required, first call * {@link org.apache.isis.applib.DomainObjectContainer#titleOf(Object)} on the object and pass in the resultant * string. * </p> * * @param object * the object whose title is to be appended to this title. * @param defaultValue * a textual value to be used if the object's title is null or * empty. * @return a reference to the called object (itself). */ public TitleBuffer append(final Object object, final String defaultValue) { String title = titleFor(object); if (!isEmpty(title)) { appendWithSpace(title); } else { appendWithSpace(defaultValue); } return this; } /** * Appends a space (if there is already some text in this title object) and * then the specified text. * * @return a reference to the called object (itself). */ public TitleBuffer append(final String text) { if (!isEmpty(text)) { appendWithSpace(text); } return this; } /** * Appends the joining string and the title of the specified object. If the object is empty then nothing * will be appended. * * <p> * Note: this method only obtains the title using either <tt>title()</tt> or <tt>toString()</tt>; it doesn't * honour other mechanisms for specifying the title, such as {@link org.apache.isis.applib.annotation.Title} * annotation. If that functionality is required, first call * {@link org.apache.isis.applib.DomainObjectContainer#titleOf(Object)} on the object and pass in the resultant * string. * </p> * * @see #isEmpty(Object) */ public TitleBuffer append(final String joiner, final Object object) { String title = titleFor(object); if (!isEmpty(title)) { appendJoiner(joiner); appendWithSpace(title); } return this; } /** * Append the <code>joiner</code> text, a space, and the title of the * specified object to the text of this {@link org.apache.isis.applib.util.TitleBuffer}. If the title of * the specified object is null then use the <code>defaultValue</code> text. * If both the objects title and the default value are null or equate to a * zero-length string then no text will be appended ; not even the joiner * text. * * <p> * Note: this method only obtains the title using either <tt>title()</tt> or <tt>toString()</tt>; it doesn't * honour other mechanisms for specifying the title, such as {@link org.apache.isis.applib.annotation.Title} * annotation. If that functionality is required, first call * {@link org.apache.isis.applib.DomainObjectContainer#titleOf(Object)} on the object and pass in the resultant * string. * </p> * * @param joiner * text to append before the title * @param object * object whose title needs to be appended * @param defaultTitle * the text to use if the the object's title is null. * @return a reference to the called object (itself). */ public TitleBuffer append(final String joiner, final Object object, final String defaultTitle) { appendJoiner(joiner); String title = titleFor(object); if (!isEmpty(title)) { appendWithSpace(title); } else { appendWithSpace(defaultTitle); } return this; } /** * Appends the joiner text, a space, and the text to the text of this * {@link org.apache.isis.applib.util.TitleBuffer}. If no text yet exists in the object then the joiner * text and space are omitted. * * @return a reference to the called object (itself). */ public TitleBuffer append(final String joiner, final String text) { if (!isEmpty(text)) { appendJoiner(joiner); appendWithSpace(text); } return this; } private void appendJoiner(final String joiner) { if (title.length() > 0) { title.append(joiner); } } /** * Append a space to the text of this TitleString object if, and only if, * there is some existing text i.e., a space is only added to existing text * and will not create a text entry consisting of only one space. * * @return a reference to the called object (itself). */ public TitleBuffer appendSpace() { if (title.length() > 0) { title.append(SPACE); } return this; } private void appendWithSpace(final Object object) { appendSpace(); title.append(titleFor(object)); } /** * Concatenate the the title value (the result of calling an objects label() * method) to this TitleString object. If the value is null the no text is * added. * * @param object * the ObjectAdapter to get a title from * @return a reference to the called object (itself). */ public final TitleBuffer concat(final Object object) { concat(object, ""); return this; } /** * Concatenate the title of the object value or the specified default value if the title is equal to null or * is empty, to this {@link org.apache.isis.applib.util.TitleBuffer}. * * <p> * Note: this method only obtains the title using either <tt>title()</tt> or <tt>toString()</tt>; it doesn't * honour other mechanisms for specifying the title, such as {@link org.apache.isis.applib.annotation.Title} * annotation. If that functionality is required, first call * {@link org.apache.isis.applib.DomainObjectContainer#titleOf(Object)} on the object and pass in the resultant * string. * </p> * * @param object * the object to get a title from * @param defaultValue * the default text to use when the object is null/empty * * @return a reference to the called object (itself). */ public final TitleBuffer concat(final Object object, final String defaultValue) { String title = titleFor(object); if (isEmpty(title)) { this.title.append(defaultValue); } else { this.title.append(title); } return this; } /** * Concatenate the specified text on to the end of the text of this * {@link org.apache.isis.applib.util.TitleBuffer}. * * @param text * text to append * @return a reference to the called object (itself). */ public final TitleBuffer concat(final String text) { title.append(text); return this; } /** * Concatenate the joiner text and the text to the text of this {@link org.apache.isis.applib.util.TitleBuffer} * object. If no text yet exists in the object then the joiner text is * omitted. * * @return a reference to the called object (itself). */ public TitleBuffer concat(final String joiner, final String text) { if (!isEmpty(text)) { appendJoiner(joiner); title.append(text); } return this; } /** * Concatenate the joiner text and the title of the object to the text of * this {@link org.apache.isis.applib.util.TitleBuffer}. If no object yet exists in the object then the * joiner text is omitted. * * <p> * Note: this method only obtains the title using either <tt>title()</tt> or <tt>toString()</tt>; it doesn't * honour other mechanisms for specifying the title, such as {@link org.apache.isis.applib.annotation.Title} * annotation. If that functionality is required, first call * {@link org.apache.isis.applib.DomainObjectContainer#titleOf(Object)} on the object and pass in the resultant * string. * </p> * * @return a reference to the called object (itself). */ public final TitleBuffer concat(final String joiner, final Object object) { String title = titleFor(object); if (!isEmpty(title)) { appendJoiner(joiner); concat(title, ""); } return this; } /** * Concatenate the joiner text and the title of the object to the text of * this {@link org.apache.isis.applib.util.TitleBuffer} object. If no object yet exists in the object then * defaultValue is used instead. * * <p> * Note: this method only obtains the title using either <tt>title()</tt> or <tt>toString()</tt>; it doesn't * honour other mechanisms for specifying the title, such as {@link org.apache.isis.applib.annotation.Title} * annotation. If that functionality is required, first call * {@link org.apache.isis.applib.DomainObjectContainer#titleOf(Object)} on the object and pass in the resultant * string. * </p> * @return a reference to the called object (itself). */ public final TitleBuffer concat(final String joiner, final Object object, final String defaultValue) { String title = titleFor(object); if (isEmpty(title)) { appendJoiner(joiner); this.title.append(defaultValue); } else { appendJoiner(joiner); this.title.append(title); } return this; } /** * Returns a String that represents the value of this object. */ @Override public String toString() { return title.toString(); } /** * Truncates this title so it has a maximum number of words. Spaces are used * to determine words, thus two spaces in a title will cause two words to be * mistakenly identified. * * @param noWords * the number of words to show * @return a reference to the called object (itself). */ public TitleBuffer truncate(final int noWords) { if (noWords < 1) { throw new IllegalArgumentException("Truncation must be to one or more words"); } int pos = 0; int spaces = 0; while (pos < title.length() && spaces < noWords) { if (title.charAt(pos) == ' ') { spaces++; } pos++; } if (pos < title.length()) { title.setLength(pos - 1); // string.delete(pos - 1, // string.length()); title.append("..."); } return this; } }