/* * * * Copyright (c) 2016. David Sowerby * * * * Licensed 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 uk.q3c.krail.core.navigate.sitemap; import com.google.common.base.Splitter; import com.google.inject.Inject; import org.apache.commons.lang3.StringUtils; import org.reflections.Reflections; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.q3c.krail.core.i18n.I18NKey; import uk.q3c.krail.core.view.KrailView; import javax.annotation.Nonnull; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import static com.google.common.base.Preconditions.checkNotNull; public class DefaultAnnotationSitemapLoader extends SitemapLoaderBase implements AnnotationSitemapLoader { private static Logger log = LoggerFactory.getLogger(DefaultAnnotationSitemapLoader.class); private Map<String, AnnotationSitemapEntry> sources; @Inject protected DefaultAnnotationSitemapLoader() { super(); } @Override public Map<String, AnnotationSitemapEntry> getSources() { return sources; } /** * Scans for {@link View} annotations, starting from the reflectionRoot (the key for the AnnotationEntry). Annotations cannot hold enum parameters, so * the enum name has to be converted from the labelKeyName parameter of the {@link View} annotation. If a class has the {@link View} annotation, but does * not implement {@link KrailView}, then it is ignored. * <p/> * <br> * Also scans for the {@link RedirectFrom} annotation, and populates the {@link MasterSitemap} redirects with the appropriate entries. If a class is * annotated with {@link RedirectFrom}, but does not implement {@link KrailView}, then the annotation is ignored. * */ @SuppressWarnings("unchecked") @Override public boolean load(@Nonnull MasterSitemap sitemap) { checkNotNull(sitemap); clearCounts(); if (sources != null) { for (Entry<String, AnnotationSitemapEntry> entry : sources.entrySet()) { String source = entry.getKey(); log.debug("scanning {} for View annotations", entry.getKey()); Reflections reflections = new Reflections(entry.getKey()); // find the View annotations Set<Class<?>> typesWithView = reflections.getTypesAnnotatedWith(View.class); log.debug("{} KrailViews with View annotation found", typesWithView.size()); // find the RedirectFrom annotations Set<Class<?>> typesWithRedirectFrom = reflections.getTypesAnnotatedWith(RedirectFrom.class); log.debug("{} KrailViews with RedirectFrom annotation found", typesWithRedirectFrom.size()); // process the View annotations for (Class<?> clazz : typesWithView) { Class<? extends KrailView> viewClass = null; if (KrailView.class.isAssignableFrom(clazz)) { viewClass = (Class<? extends KrailView>) clazz; View annotation = viewClass.getAnnotation(View.class); NodeRecord nodeRecord = new NodeRecord(annotation.uri()); nodeRecord.setViewClass(viewClass); nodeRecord.setPageAccessControl(annotation.pageAccessControl()); nodeRecord.setPositionIndex(annotation.positionIndex()); if (StringUtils.isNotEmpty(annotation.roles())) { Splitter splitter = Splitter.on(",") .trimResults(); Iterable<String> roles = splitter.split(annotation.roles()); for (String role : roles) { nodeRecord.addRole(role); } } I18NKey keySample = entry.getValue() .getLabelSample(); String keyName = annotation.labelKeyName(); try { I18NKey key = keyFromName(keyName, keySample); nodeRecord.setLabelKey(key); } catch (IllegalArgumentException iae) { addError(source, AnnotationSitemapLoader.LABEL_NOT_VALID, clazz, keyName, keySample.getClass()); } sitemap.append(nodeRecord); } } // process the RedirectFrom annotations for (Class<?> clazz : typesWithRedirectFrom) { Class<? extends KrailView> viewClass = null; if (KrailView.class.isAssignableFrom(clazz)) { viewClass = (Class<? extends KrailView>) clazz; RedirectFrom redirectAnnotation = viewClass.getAnnotation(RedirectFrom.class); View viewAnnotation = viewClass.getAnnotation(View.class); if (viewAnnotation == null) { // report this addWarning(source, REDIRECT_FROM_IGNORED, clazz); } else { String[] sourcePages = redirectAnnotation.sourcePages(); String targetPage = viewAnnotation.uri(); for (String sourcePage : sourcePages) { sitemap.addRedirect(sourcePage, targetPage); } } } } } for (String source : sources.keySet()) { addInfo("Scanned for annotations", "Package name: " + source); } return true; } else { log.info("No Annotations Sitemap sources to load"); return false; } } /** * Returns an {@link I18NKey} enum constant from {@code labelKeyName} using the class from {@code sampleKey}. * * * @return an {@link I18NKey} enum constant from {@code labelKeyName} using the class from {@code sampleKey}. * * * @throws IllegalArgumentException * if <code>labelKeyClass</code> does not contain a constant of <code>labelKeyName</code> */ private I18NKey keyFromName(String labelKeyName, I18NKey sampleKey) { Enum<?> labelKey = Enum.valueOf(((Enum) sampleKey).getDeclaringClass(), labelKeyName); return (I18NKey) labelKey; } @Inject(optional = true) protected void setAnnotations(Map<String, AnnotationSitemapEntry> sources) { this.sources = sources; } }