001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *        http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 */
019
020package org.apache.isis.core.progmodel.facets.object.title.annotation;
021
022import java.lang.reflect.Method;
023import java.util.List;
024
025import com.google.common.base.Function;
026import com.google.common.base.Objects;
027import com.google.common.base.Strings;
028
029import org.slf4j.Logger;
030import org.slf4j.LoggerFactory;
031
032import org.apache.isis.applib.annotation.Title;
033import org.apache.isis.applib.profiles.Localization;
034import org.apache.isis.core.commons.lang.StringExtensions;
035import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
036import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
037import org.apache.isis.core.metamodel.adapter.util.AdapterInvokeUtils;
038import org.apache.isis.core.metamodel.facetapi.FacetHolder;
039import org.apache.isis.core.metamodel.facets.object.title.TitleFacetAbstract;
040
041public class TitleFacetViaTitleAnnotation extends TitleFacetAbstract {
042
043    private static final Logger LOG = LoggerFactory.getLogger(TitleFacetViaTitleAnnotation.class);
044    private final List<TitleComponent> components;
045    private final AdapterManager adapterManager;
046
047    public static class TitleComponent {
048        public static final Function<? super Method, ? extends TitleComponent> FROM_METHOD = new Function<Method, TitleComponent>() {
049
050            @Override
051            public TitleComponent apply(final Method input) {
052                return TitleComponent.of(input);
053            }
054        };
055
056        private final String prepend;
057        private final String append;
058        private final Method method;
059        private final int abbreviateTo;
060
061        private TitleComponent(final String prepend, final String append, final Method method, final int abbreviateTo) {
062            super();
063            this.prepend = prepend;
064            this.append = append;
065            this.method = method;
066            this.abbreviateTo = abbreviateTo;
067        }
068
069        public String getPrepend() {
070            return prepend;
071        }
072
073        public String getAppend() {
074            return append;
075        }
076
077        public Method getMethod() {
078            return method;
079        }
080
081        public static TitleComponent of(final Method method) {
082            final Title annotation = method.getAnnotation(Title.class);
083            final String prepend = annotation != null ? annotation.prepend() : " ";
084            final String append = annotation != null ? annotation.append() : "";
085            final int abbreviateTo = annotation != null ? annotation.abbreviatedTo() : Integer.MAX_VALUE;
086            return new TitleComponent(prepend, append, method, abbreviateTo);
087        }
088    }
089
090    public TitleFacetViaTitleAnnotation(final List<TitleComponent> components, final FacetHolder holder, final AdapterManager adapterManager) {
091        super(holder);
092        this.components = components;
093        this.adapterManager = adapterManager;
094    }
095
096    @Override
097    public String title(final ObjectAdapter targetAdapter, final Localization localization) {
098        return title(null, targetAdapter, localization);
099    }
100
101    private String titleOf(final ObjectAdapter adapter) {
102        if (adapter == null) {
103            return null;
104        }
105        return adapter.titleString(null);
106    }
107
108    public List<TitleComponent> getComponents() {
109        return components;
110    }
111
112    private static String abbreviated(final String str, final int maxLength) {
113        return str.length() < maxLength ? str : str.substring(0, maxLength - 3) + "...";
114    }
115
116    @Override
117    public String title(ObjectAdapter contextAdapter, ObjectAdapter targetAdapter, Localization localization) {
118        final StringBuilder stringBuilder = new StringBuilder();
119
120        try {
121            for (final TitleComponent component : this.components) {
122                final Object titlePart = AdapterInvokeUtils.invoke(component.getMethod(), targetAdapter);
123                if (titlePart == null) {
124                    continue;
125                } 
126                // ignore context, if provided
127                final ObjectAdapter titlePartAdapter = adapterManager.adapterFor(titlePart);
128                if(Objects.equal(contextAdapter, titlePartAdapter)) {
129                    continue;
130                }
131                String title = titleOf(titlePartAdapter);
132                if (Strings.isNullOrEmpty(title)) {
133                    // ... use the toString() otherwise
134                    // (mostly for benefit of testing...)
135                    title = titlePart.toString().trim();
136                }
137                if(Strings.isNullOrEmpty(title)) {
138                    continue;
139                }
140                stringBuilder.append(component.getPrepend());
141                stringBuilder.append(abbreviated(title, component.abbreviateTo));
142                stringBuilder.append(component.getAppend());
143            }
144
145            return stringBuilder.toString().trim();
146        } catch (final RuntimeException ex) {
147            LOG.warn("Title failure", ex);
148            return "Failed Title";
149        }
150    }
151}