/*
* 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.viewer.wicket.model.models;
import java.io.Serializable;
import java.util.List;
import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
import org.apache.isis.core.metamodel.adapter.oid.Oid;
import org.apache.isis.core.metamodel.adapter.oid.OidMarshaller;
import org.apache.isis.core.metamodel.adapter.oid.RootOid;
import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
import org.apache.isis.core.metamodel.spec.feature.Contributed;
import org.apache.isis.core.metamodel.spec.feature.ObjectAssociation;
import org.apache.isis.viewer.wicket.model.mementos.PageParameterNames;
public class BookmarkTreeNode implements Serializable {
private static final long serialVersionUID = 1L;
private static final OidMarshaller OID_MARSHALLER = OidMarshaller.INSTANCE;
private final List<BookmarkTreeNode> children = Lists.newArrayList();
private final int depth;
private final RootOid oidNoVer;
private final String oidNoVerStr;
private final PageType pageType;
private String title;
private PageParameters pageParameters;
public static BookmarkTreeNode newRoot(
BookmarkableModel<?> bookmarkableModel) {
return new BookmarkTreeNode(bookmarkableModel, 0);
}
private BookmarkTreeNode(
final BookmarkableModel<?> bookmarkableModel,
final int depth) {
pageParameters = bookmarkableModel.getPageParameters();
RootOid oid = oidFrom(pageParameters);
this.oidNoVerStr = OID_MARSHALLER.marshalNoVersion(oid);
this.oidNoVer = OID_MARSHALLER.unmarshal(oidNoVerStr, RootOid.class);
// replace oid with the noVer equivalent.
PageParameterNames.OBJECT_OID.removeFrom(pageParameters);
PageParameterNames.OBJECT_OID.addStringTo(pageParameters, getOidNoVerStr());
this.title = bookmarkableModel.getTitle();
this.pageType = bookmarkableModel instanceof EntityModel ? PageType.ENTITY : PageType.ACTION_PROMPT;
this.depth = depth;
}
public RootOid getOidNoVer() {
return oidNoVer;
}
public String getOidNoVerStr() {
return oidNoVerStr;
}
public String getTitle() {
return title;
}
private void setTitle(String title) {
this.title = title;
}
public PageType getPageType() {
return pageType;
}
public List<BookmarkTreeNode> getChildren() {
return children;
}
public BookmarkTreeNode addChild(BookmarkableModel<?> childModel) {
final BookmarkTreeNode childNode = new BookmarkTreeNode(childModel, depth+1);
children.add(childNode);
return childNode;
}
/**
* Whether or not the provided {@link BookmarkableModel} matches that contained
* within this node, or any of its children.
*
* <p>
* If it does, then the matched node's title is updated to that of the provided
* {@link BookmarkableModel}.
*
* <p>
* The {@link PageParameters} (used for matching) is
* {@link BookmarkableModel#getPageParameters() obtained} from the {@link BookmarkableModel}.
*
* @return - whether the provided candidate is found or was added to this node's tree.
*/
public boolean matches(BookmarkableModel<?> candidateBookmarkableModel) {
if(candidateBookmarkableModel instanceof EntityModel) {
if(this.pageType != PageType.ENTITY) {
return false;
}
return matchAndUpdateTitleFor((EntityModel) candidateBookmarkableModel);
} else if(candidateBookmarkableModel instanceof ActionModel) {
if(this.pageType != PageType.ACTION_PROMPT) {
return false;
}
return matchFor((ActionModel) candidateBookmarkableModel);
} else {
return false;
}
}
/**
* Whether or not the provided {@link EntityModel} matches that contained
* within this node, or any of its children.
*
* <p>
* If it does match, then the matched node's title is updated to that of the provided
* {@link EntityModel}.
*
* @return - whether the provided candidate is found or was added to this node's tree.
*/
private boolean matchAndUpdateTitleFor(final EntityModel candidateEntityModel) {
// match only on the oid string
final String candidateOidStr = oidStrFrom(candidateEntityModel);
boolean inGraph = Objects.equal(this.oidNoVerStr, candidateOidStr);
if(inGraph) {
this.setTitle(candidateEntityModel.getTitle());
}
// and also match recursively down to all children and grandchildren.
if(candidateEntityModel.hasAsChildPolicy()) {
for(BookmarkTreeNode childNode: this.getChildren()) {
inGraph = childNode.matches(candidateEntityModel) || inGraph; // evaluate each
}
if(!inGraph) {
inGraph = addToGraphIfParented(candidateEntityModel);
}
}
return inGraph;
}
/**
* Whether or not the provided {@link ActionModel} matches that contained
* within this node (taking into account the action's arguments).
*
* If it does match, then the matched node's title is updated to that of the provided
* {@link ActionModel}.
* <p>
*
* @return - whether the provided candidate is found or was added to this node's tree.
*/
private boolean matchFor(final ActionModel candidateActionModel) {
// check if target object of the action is the same (the oid str)
final String candidateOidStr = oidStrFrom(candidateActionModel);
if(!Objects.equal(this.oidNoVerStr, candidateOidStr)) {
return false;
}
// check if args same
List<String> thisArgs = PageParameterNames.ACTION_ARGS.getListFrom(pageParameters);
PageParameters candidatePageParameters = candidateActionModel.getPageParameters();
List<String> candidateArgs = PageParameterNames.ACTION_ARGS.getListFrom(candidatePageParameters);
if(!Objects.equal(thisArgs, candidateArgs)) {
return false;
}
// ok, a match
return true;
}
private boolean addToGraphIfParented(BookmarkableModel<?> candidateBookmarkableModel) {
boolean whetherAdded = false;
// TODO: this ought to be move into a responsibility of BookmarkableModel, perhaps, rather than downcasting
if(candidateBookmarkableModel instanceof EntityModel) {
EntityModel entityModel = (EntityModel) candidateBookmarkableModel;
final ObjectAdapter candidateAdapter = entityModel.getObject();
final List<ObjectAssociation> properties = candidateAdapter.getSpecification().getAssociations(Contributed.EXCLUDED, ObjectAssociation.Filters.REFERENCE_PROPERTIES);
for (ObjectAssociation objectAssoc : properties) {
final ObjectAdapter possibleParentAdapter = objectAssoc.get(candidateAdapter, InteractionInitiatedBy.USER);
if(possibleParentAdapter == null) {
continue;
}
final Oid possibleParentOid = possibleParentAdapter.getOid();
if(possibleParentOid == null) {
continue;
}
final String possibleParentOidStr = possibleParentOid.enStringNoVersion();
if(Objects.equal(this.oidNoVerStr, possibleParentOidStr)) {
this.addChild(candidateBookmarkableModel);
whetherAdded = true;
}
}
}
return whetherAdded;
}
public void appendGraphTo(List<BookmarkTreeNode> list) {
list.add(this);
for (BookmarkTreeNode childNode : children) {
childNode.appendGraphTo(list);
}
}
public int getDepth() {
return depth;
}
// //////////////////////////////////////
public PageParameters getPageParameters() {
return pageParameters;
}
// //////////////////////////////////////
public static RootOid oidFrom(final PageParameters pageParameters) {
String oidStr = PageParameterNames.OBJECT_OID.getStringFrom(pageParameters);
if(oidStr == null) {
return null;
}
try {
return OID_MARSHALLER.unmarshal(oidStr, RootOid.class);
} catch(Exception ex) {
return null;
}
}
public static String oidStrFrom(BookmarkableModel<?> candidateBookmarkableModel) {
final RootOid oid = oidFrom(candidateBookmarkableModel.getPageParameters());
return oid != null? OID_MARSHALLER.marshalNoVersion(oid): null;
}
}