package fr.openwide.core.wicket.more.link.descriptor.impl;
import java.util.Collection;
import org.apache.wicket.Component;
import org.apache.wicket.Page;
import org.apache.wicket.RestartResponseAtInterceptPageException;
import org.apache.wicket.RestartResponseException;
import org.apache.wicket.Session;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.core.request.handler.PageProvider;
import org.apache.wicket.core.request.handler.RenderPageRequestHandler;
import org.apache.wicket.model.IModel;
import org.apache.wicket.request.Url;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.flow.RedirectToUrlException;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import fr.openwide.core.wicket.more.link.descriptor.AbstractDynamicBookmarkableLink;
import fr.openwide.core.wicket.more.link.descriptor.LinkInvalidTargetRuntimeException;
import fr.openwide.core.wicket.more.link.descriptor.generator.ILinkGenerator;
import fr.openwide.core.wicket.more.link.descriptor.generator.IPageLinkGenerator;
import fr.openwide.core.wicket.more.link.descriptor.parameter.injector.LinkParameterInjectionRuntimeException;
import fr.openwide.core.wicket.more.link.descriptor.parameter.validator.LinkParameterValidationRuntimeException;
import fr.openwide.core.wicket.more.markup.html.template.model.NavigationMenuItem;
import fr.openwide.core.wicket.more.util.model.Models;
public class CorePageInstanceLinkGenerator implements IPageLinkGenerator {
private static final long serialVersionUID = -1568375367568280290L;
private static final String ANCHOR_ROOT = "#";
private static final GetNameFromClassModelFunction GET_NAME_FROM_CLASS_MODEL_FUNCTION = new GetNameFromClassModelFunction();
private final IModel<? extends Page> pageInstanceModel;
private boolean bypassPermissions = false;
private final Collection<IModel<? extends Class<? extends Page>>> expectedPageClassModels;
public CorePageInstanceLinkGenerator(IModel<? extends Page> pageInstanceModel, Collection<IModel<? extends Class<? extends Page>>> expectedPageClassModels) {
this.pageInstanceModel = pageInstanceModel;
this.expectedPageClassModels = expectedPageClassModels;
}
@Override
public IPageLinkGenerator wrap(Component component) {
Collection<IModel<? extends Class<? extends Page>>> wrapedPageClassModels = Lists.newArrayList();
for (IModel<? extends Class<? extends Page>> expectedPageClassModel : expectedPageClassModels) {
wrapedPageClassModels.add(Models.wrap(expectedPageClassModel, component));
}
return new CorePageInstanceLinkGenerator(
Models.wrap(pageInstanceModel, component),
wrapedPageClassModels
);
}
protected Class<? extends Page> getValidExpectedPageClass(Page pageInstance) {
if (expectedPageClassModels.isEmpty()) {
return pageInstance.getClass();
}
for (IModel<? extends Class<? extends Page>> expectedPageClassModel : expectedPageClassModels) {
Class<? extends Page> expectedPageClass = expectedPageClassModel.getObject();
if (expectedPageClass != null && expectedPageClass.isAssignableFrom(pageInstance.getClass())) {
return expectedPageClass;
}
}
return null;
}
protected Page getValidPageInstance() throws LinkInvalidTargetRuntimeException {
Page pageInstance = pageInstanceModel.getObject();
if (pageInstance == null) {
throw new LinkInvalidTargetRuntimeException("The target page instance was null");
}
Class<? extends Page> validPageClass = getValidExpectedPageClass(pageInstance);
if (validPageClass == null) {
throw new LinkInvalidTargetRuntimeException("The target page instance '" + pageInstance + "' had unexpected type :"
+ " got " + pageInstance.getClass().getName() + ", "
+ "expected one of " + Joiner.on(", ").join(Collections2.transform(expectedPageClassModels, GET_NAME_FROM_CLASS_MODEL_FUNCTION)));
}
if (! bypassPermissions) {
if (!Session.get().getAuthorizationStrategy().isActionAuthorized(pageInstance, Page.RENDER)) {
throw new LinkInvalidTargetRuntimeException("The rendering of the target page instance '" + pageInstance
+ "' was not authorized.");
}
}
return pageInstance;
}
@Override
public boolean isAccessible() {
Page pageInstance = pageInstanceModel.getObject();
if (pageInstance == null) {
return false;
}
Class<? extends Page> validPageClass = getValidExpectedPageClass(pageInstance);
if (validPageClass == null) {
return false;
}
return Session.get().getAuthorizationStrategy().isActionAuthorized(pageInstance, Page.RENDER);
}
@Override
public ILinkGenerator chain(ILinkGenerator other) {
if (other instanceof IPageLinkGenerator) {
return chain((IPageLinkGenerator) other);
}
return new ChainedLinkGeneratorImpl(ImmutableList.of(this, other));
}
@Override
public IPageLinkGenerator chain(IPageLinkGenerator other) {
return new ChainedPageLinkGeneratorImpl(ImmutableList.of(this, other));
}
@Override
public String url() throws LinkInvalidTargetRuntimeException {
return url(RequestCycle.get());
}
@Override
public String url(RequestCycle requestCycle) throws LinkInvalidTargetRuntimeException {
return requestCycle.urlFor(
new RenderPageRequestHandler(new PageProvider(getValidPageInstance()))
).toString();
}
@Override
public String fullUrl() throws LinkInvalidTargetRuntimeException {
return fullUrl(RequestCycle.get());
}
@Override
public String fullUrl(RequestCycle requestCycle) throws LinkInvalidTargetRuntimeException {
return requestCycle.getUrlRenderer().renderFullUrl(Url.parse(url(requestCycle)));
}
@Override
public AbstractDynamicBookmarkableLink link(String wicketId) {
return new DynamicBookmarkablePageInstanceLink(wicketId, this);
}
@Override
public AbstractDynamicBookmarkableLink link(String wicketId, String anchor) {
AbstractDynamicBookmarkableLink link = link(wicketId);
link.add(new AttributeAppender("href", ANCHOR_ROOT + anchor));
return link;
}
@Override
public void setResponsePage() throws LinkInvalidTargetRuntimeException, LinkParameterValidationRuntimeException, LinkParameterInjectionRuntimeException {
RequestCycle.get().setResponsePage(getValidPageInstance());
}
@Override
public RestartResponseException newRestartResponseException() throws LinkInvalidTargetRuntimeException {
return new RestartResponseException(getValidPageInstance());
}
@Override
public RestartResponseAtInterceptPageException newRestartResponseAtInterceptPageException()
throws LinkInvalidTargetRuntimeException {
return new RestartResponseAtInterceptPageException(getValidPageInstance());
}
@Override
public RedirectToUrlException newRedirectToUrlException() throws LinkInvalidTargetRuntimeException {
return new RedirectToUrlException(fullUrl());
}
@Override
public RedirectToUrlException newRedirectToUrlException(String anchor) throws LinkInvalidTargetRuntimeException {
return new RedirectToUrlException(fullUrl() + ANCHOR_ROOT + anchor);
}
@Override
public NavigationMenuItem navigationMenuItem(IModel<String> labelModel) throws LinkInvalidTargetRuntimeException {
return navigationMenuItem(labelModel, Lists.<NavigationMenuItem>newArrayListWithExpectedSize(0));
}
@Override
public NavigationMenuItem navigationMenuItem(IModel<String> labelModel, Collection<NavigationMenuItem> subMenuItems)
throws LinkInvalidTargetRuntimeException {
return new NavigationMenuItem(labelModel, this, subMenuItems);
}
@Override
public boolean isActive(Class<? extends Page> selectedPage) {
throw new IllegalStateException("We may not call isActive on a page instance link.");
}
@Override
public void detach() {
pageInstanceModel.detach();
for (IModel<? extends Class<? extends Page>> expectedPageClassModel : expectedPageClassModels) {
expectedPageClassModel.detach();
}
}
private static class GetNameFromClassModelFunction implements Function<IModel<? extends Class<?>>, String> {
@Override
public String apply(IModel<? extends Class<?>> input) {
if (input.getObject() != null) {
return input.getObject().getName();
}
return null;
}
}
@Override
public PageProvider newPageProvider() {
return new PageProvider(getValidPageInstance());
}
/**
* @see IPageLinkGenerator#bypassPermissions()
*/
@Deprecated
@Override
public IPageLinkGenerator bypassPermissions() {
bypassPermissions = true;
return this;
}
}