001/**
002 *  Licensed to the Apache Software Foundation (ASF) under one or more
003 *  contributor license agreements.  See the NOTICE file distributed with
004 *  this work for additional information regarding copyright ownership.
005 *  The ASF licenses this file to You under the Apache License, Version 2.0
006 *  (the "License"); you may not use this file except in compliance with
007 *  the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 *  Unless required by applicable law or agreed to in writing, software
012 *  distributed under the License is distributed on an "AS IS" BASIS,
013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 *  See the License for the specific language governing permissions and
015 *  limitations under the License.
016 */
017package org.apache.isis.core.metamodel.specloader.specimpl;
018
019import java.util.List;
020
021import org.apache.isis.applib.Identifier;
022import org.apache.isis.applib.annotation.Render;
023import org.apache.isis.applib.annotation.When;
024import org.apache.isis.applib.annotation.Where;
025import org.apache.isis.applib.filter.Filter;
026import org.apache.isis.core.commons.authentication.AuthenticationSession;
027import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
028import org.apache.isis.core.metamodel.consent.Consent;
029import org.apache.isis.core.metamodel.consent.InteractionInvocationMethod;
030import org.apache.isis.core.metamodel.facetapi.Facet;
031import org.apache.isis.core.metamodel.facetapi.FacetHolder;
032import org.apache.isis.core.metamodel.facetapi.FacetHolderImpl;
033import org.apache.isis.core.metamodel.facetapi.FacetUtil;
034import org.apache.isis.core.metamodel.facetapi.MultiTypedFacet;
035import org.apache.isis.core.metamodel.facets.members.resolve.RenderFacet;
036import org.apache.isis.core.metamodel.facets.members.resolve.RenderFacetAbstract;
037import org.apache.isis.core.metamodel.facets.notpersisted.NotPersistedFacet;
038import org.apache.isis.core.metamodel.facets.notpersisted.NotPersistedFacetAbstract;
039import org.apache.isis.core.metamodel.facets.typeof.TypeOfFacet;
040import org.apache.isis.core.metamodel.facets.typeof.TypeOfFacetAbstract;
041import org.apache.isis.core.metamodel.interactions.InteractionUtils;
042import org.apache.isis.core.metamodel.interactions.UsabilityContext;
043import org.apache.isis.core.metamodel.interactions.VisibilityContext;
044import org.apache.isis.core.metamodel.spec.ObjectSpecification;
045import org.apache.isis.core.metamodel.spec.SpecificationLoader;
046import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
047import org.apache.isis.core.metamodel.spec.feature.ObjectMemberContext;
048import org.apache.isis.core.progmodel.facets.members.disabled.DisabledFacet;
049import org.apache.isis.core.progmodel.facets.members.disabled.DisabledFacetImpl;
050import org.apache.isis.core.progmodel.facets.members.resolve.RenderFacetAnnotation;
051
052public class OneToManyAssociationContributee extends OneToManyAssociationImpl implements ContributeeMember {
053
054    private final ObjectAdapter serviceAdapter;
055    private final ObjectAction serviceAction;
056    
057
058    /**
059     * Hold facets rather than delegate to the contributed action (different types might
060     * use layout metadata to position the contributee in different ways)
061     */
062    private final FacetHolder facetHolder = new FacetHolderImpl();
063    
064    private final Identifier identifier;
065
066    private static ObjectSpecification typeOfSpec(final ObjectActionImpl objectAction, ObjectMemberContext objectMemberContext) {
067        final TypeOfFacet actionTypeOfFacet = objectAction.getFacet(TypeOfFacet.class);
068        SpecificationLoader specificationLookup = objectMemberContext.getSpecificationLookup();
069        // TODO: a bit of a hack; ought really to set up a fallback TypeOfFacetDefault which ensures that there is always
070        // a TypeOfFacet for any contributee associations created from contributed actions.
071        Class<? extends Object> cls = actionTypeOfFacet != null? actionTypeOfFacet.value(): Object.class;
072        return specificationLookup.loadSpecification(cls);
073    }
074    
075    public OneToManyAssociationContributee(
076            final ObjectAdapter serviceAdapter, 
077            final ObjectActionImpl serviceAction,
078            final ObjectSpecification contributeeType,
079            final ObjectMemberContext objectMemberContext) {
080        super(serviceAction.getFacetedMethod(), typeOfSpec(serviceAction, objectMemberContext), objectMemberContext);
081        this.serviceAdapter = serviceAdapter;
082        this.serviceAction = serviceAction;
083
084        // copy over facets from contributed to own.
085        FacetUtil.copyFacets(serviceAction.getFacetedMethod(), facetHolder);
086        
087        final NotPersistedFacet notPersistedFacet = new NotPersistedFacetAbstract(this) {};
088        final DisabledFacet disabledFacet = disabledFacet();
089        final TypeOfFacet typeOfFacet = new TypeOfFacetAbstract(getSpecification().getCorrespondingClass(), this, objectMemberContext.getSpecificationLookup()) {}; 
090        
091        FacetUtil.addFacet(notPersistedFacet);
092        FacetUtil.addFacet(disabledFacet);
093        FacetUtil.addFacet(typeOfFacet);
094        
095        // calculate the identifier
096        final Identifier contributorIdentifier = serviceAction.getFacetedMethod().getIdentifier();
097        final String memberName = contributorIdentifier.getMemberName();
098        List<String> memberParameterNames = contributorIdentifier.getMemberParameterNames();
099        
100        identifier = Identifier.actionIdentifier(contributeeType.getCorrespondingClass().getName(), memberName, memberParameterNames);
101    }
102
103    private DisabledFacet disabledFacet() {
104        final DisabledFacet originalFacet = facetHolder.getFacet(DisabledFacet.class);
105        if( originalFacet != null && 
106            originalFacet.when() == When.ALWAYS && 
107            originalFacet.where() == Where.ANYWHERE) {
108            return originalFacet;
109        }
110        // ensure that the contributed association is always disabled
111        return new DisabledFacetImpl(When.ALWAYS, Where.ANYWHERE, "Contributed collection", this);
112    }
113
114    @Override
115    public ObjectAdapter get(final ObjectAdapter ownerAdapter) {
116        return serviceAction.execute(serviceAdapter, new ObjectAdapter[]{ownerAdapter});
117    }
118
119    @Override
120    public Identifier getIdentifier() {
121        return identifier;
122    }
123    
124    @Override
125    public Consent isVisible(final AuthenticationSession session, final ObjectAdapter contributee, Where where) {
126        final VisibilityContext<?> ic = serviceAction.createVisibleInteractionContext(session, InteractionInvocationMethod.BY_USER, serviceAdapter, where);
127        ic.putContributee(0, contributee); // by definition, the contributee will be the first arg of the service action
128        return InteractionUtils.isVisibleResult(this, ic).createConsent();
129    }
130
131    @Override
132    public Consent isUsable(final AuthenticationSession session, final ObjectAdapter contributee, Where where) {
133        final UsabilityContext<?> ic = serviceAction.createUsableInteractionContext(session, InteractionInvocationMethod.BY_USER, serviceAdapter, where);
134        ic.putContributee(0, contributee); // by definition, the contributee will be the first arg of the service action
135        return InteractionUtils.isUsableResult(this, ic).createConsent();
136    }
137
138    
139    // //////////////////////////////////////
140    // FacetHolder
141    // //////////////////////////////////////
142    
143    @Override
144    public Class<? extends Facet>[] getFacetTypes() {
145        return facetHolder.getFacetTypes();
146    }
147
148    @Override
149    public <T extends Facet> T getFacet(Class<T> cls) {
150        return facetHolder.getFacet(cls);
151    }
152    
153    @Override
154    public boolean containsFacet(Class<? extends Facet> facetType) {
155        return facetHolder.containsFacet(facetType);
156    }
157
158    @Override
159    public boolean containsDoOpFacet(java.lang.Class<? extends Facet> facetType) {
160        return facetHolder.containsDoOpFacet(facetType);
161    }
162
163    @Override
164    public List<Facet> getFacets(Filter<Facet> filter) {
165        return facetHolder.getFacets(filter);
166    }
167
168    @Override
169    public void addFacet(Facet facet) {
170        facetHolder.addFacet(facet);
171    }
172
173    @Override
174    public void addFacet(MultiTypedFacet facet) {
175        facetHolder.addFacet(facet);
176    }
177    
178    @Override
179    public void removeFacet(Facet facet) {
180        facetHolder.removeFacet(facet);
181    }
182
183    @Override
184    public void removeFacet(Class<? extends Facet> facetType) {
185        facetHolder.removeFacet(facetType);
186    }
187
188}