org.wicketstuff.annotation.scan.AnnotatedMountScanner Maven / Gradle / Ivy
Show all versions of wicketstuff-annotation Show documentation
/*
* 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.wicketstuff.annotation.scan;
import java.util.List;
import org.apache.wicket.Application;
import org.apache.wicket.Page;
import org.apache.wicket.core.request.mapper.HomePageMapper;
import org.apache.wicket.request.IRequestMapper;
import org.apache.wicket.request.component.IRequestablePage;
import org.apache.wicket.core.request.mapper.MountedMapper;
import org.wicketstuff.annotation.mount.MountPath;
import org.wicketstuff.config.MatchingResources;
/**
* Looks for mount information by scanning for classes annotated with {@link MountPath}. You can
* specify a package to scan (e.g., "org.mycompany.wicket.pages"). Wildcards also work (e.g.,
* "org.mycompany.*.pages" or "org.mycompany.**.pages").
*
*
* You can also go more advanced, using any pattern supported by {@link MatchingResources}. For
* example, the first package example above is turned into
* "classpath*:/org/mycompany/wicket/pages/**/*.class".
*
*
* For each class that is annotated, an appropriate {@link IRequestMapper} implementing class is
* created using the information in the {@link MountPath} annotation and any supplemental
* annotations. Each instance is added to the list to return. Each item in the returned list can
* then be mounted.
*
*
* Typical usage is in your {@link Application#init()} method and utilizes the
* {@link AnnotatedMountList#mount} convenience method.
*
*
* protected void init()
* {
* new AnnotatedMountScanner().scanPackage("org.mycompany.wicket.pages").mount(this);
* }
*
*
*
* You could scan the entire classpath if you wanted by passing in null, but that might require more
* time to run than limiting it to known packages which have annotated classes.
*
*
* Page classes annotation usage is as follows:
*
*
* @MountPath
* private class HelloPage extends Page
* {
* }
*
* @MountPath("hello")
* private class HelloPage extends Page
* {
* }
*
* @MountPath(value = "dogs", alt = { "canines", "k9s" })
* private class DogsPage extends Page
* {
* }
*
*
*
* The first example will mount HelloPage to /HelloPage using the default mapper (as returned by
* {@link #getRequestMapper} which is {@link MountedMapper}.
*
*
* The second example will mount HelloPage to /hello using the default mapper (as returned by
* {@link #getRequestMapper} which is {@link MountedMapper}.
*
*
* The third example will mount DogsPage at "/dogs" (as the primary) and as "/canines" and "/k9s" as
* alternates using the {@link MountedMapper}.
*
* @author Doug Donohoe
* @author Ronald Tetsuo Miura
*/
public class AnnotatedMountScanner
{
/**
* Get the Spring search pattern given a package name or part of a package name
*
* @param packageName
* a package name
* @return a Spring search pattern for the given package
*/
public String getPatternForPackage(String packageName)
{
if (packageName == null)
packageName = "";
packageName = packageName.replace('.', '/');
if (!packageName.endsWith("/"))
{
packageName += '/';
}
return "classpath*:" + packageName + "**/*.class";
}
/**
* Scan given a package name or part of a package name and return list of classes with MountPath
* annotation.
*
* @return A List of classes annotated with @MountPath
*/
public List> getPackageMatches(String pattern)
{
return getPatternMatches(getPatternForPackage(pattern));
}
/**
* Scan given a Spring search pattern and return list of classes with MountPath annotation.
*
* @return A List of classes annotated with @MountPath
*/
public List> getPatternMatches(String pattern)
{
MatchingResources resources = new MatchingResources(pattern);
List> mounts = resources.getAnnotatedMatches(MountPath.class);
for (Class> mount : mounts)
{
if (!(Page.class.isAssignableFrom(mount)))
{
throw new RuntimeException("@MountPath annotated class should subclass Page: " +
mount);
}
}
return mounts;
}
/**
* Scan given package name or part of a package name
*
* @param packageName
* a package to scan (e.g., "org.mycompany.pages)
* @return An {@link AnnotatedMountList}
*/
public AnnotatedMountList scanPackage(String packageName)
{
return scanList(getPackageMatches(packageName));
}
/**
* Scan given a Spring search pattern.
*
* @param pattern
* @return An {@link AnnotatedMountList}
*/
public AnnotatedMountList scanPattern(String pattern)
{
return scanList(getPatternMatches(pattern));
}
/**
* Scan a list of classes which are annotated with MountPath
*
* @param mounts
* @return An {@link AnnotatedMountList}
*/
@SuppressWarnings({ "unchecked" })
protected AnnotatedMountList scanList(List> mounts)
{
AnnotatedMountList list = new AnnotatedMountList();
for (Class> mount : mounts)
{
Class extends Page> page = (Class extends Page>)mount;
scanClass(page, list);
}
return list;
}
/**
* Scan given a class that is a sublass of {@link Page}.
*
* @param pageClass
* {@link Page} subclass to scan
* @return An {@link AnnotatedMountList} containing the primary and alternate strategies created
* for the class.
*/
public AnnotatedMountList scanClass(Class extends Page> pageClass)
{
AnnotatedMountList list = new AnnotatedMountList();
scanClass(pageClass, list);
return list;
}
/**
* Magic of all this is done here.
*
* @param pageClass
* @param list
*/
private void scanClass(Class extends Page> pageClass, AnnotatedMountList list)
{
MountPath mountPath = pageClass.getAnnotation(MountPath.class);
if (mountPath == null)
return;
// alternates
for (String alt : mountPath.alt())
{
list.add(getRequestMapper(alt, pageClass));
}
String path = mountPath.value();
// default if no explicit path is provided
if ("".equals(path))
{
path = getDefaultMountPath(pageClass);
}
list.add(getRequestMapper(path, pageClass));
}
/**
* Returns the default mapper given a mount path and class.
*
* @param mountPath
* @param pageClass
* @return {@link MountedMapper}
*/
public IRequestMapper getRequestMapper(String mountPath,
Class extends IRequestablePage> pageClass)
{
if ("/".equals(mountPath)) {
return new HomePageMapper(pageClass);
}
return new MountedMapper(mountPath, pageClass);
}
/**
* Returns the default mount path for a given class (used if the path has not been specified in
* the @MountPath
annotation). By default, this method returns the
* pageClass.getSimpleName().
*
* @param pageClass
* @return the default mount path for pageClass
*/
public String getDefaultMountPath(Class extends IRequestablePage> pageClass)
{
return pageClass.getSimpleName();
}
}