001/* 002 * (C) Copyright 2006-2008 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * bstefanescu 018 * 019 * $Id$ 020 */ 021 022package org.nuxeo.runtime.contribution.impl; 023 024import java.util.ArrayList; 025import java.util.Collections; 026import java.util.HashMap; 027import java.util.List; 028import java.util.Map; 029 030import org.nuxeo.runtime.contribution.Contribution; 031import org.nuxeo.runtime.contribution.ContributionRegistry; 032 033/** 034 * The parent provider is read only. It is never modified by the registry. It serves only to resolve dependencies. This 035 * allows greater flexibility in managing dependencies. This registry may have a parent registry that can be used only 036 * read only. 037 * 038 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 039 */ 040// TODO need to implement a visibility (PRIVATE, PROTECTED, PUBLIC etc) 041// on contributions when extending other registries 042public abstract class AbstractContributionRegistry<K, T> implements ContributionRegistry<K, T> { 043 044 protected final Map<Object, Contribution<K, T>> registry; 045 046 protected final AbstractContributionRegistry<K, T> parent; 047 048 protected final List<AbstractContributionRegistry<K, T>> listeners; 049 050 protected AbstractContributionRegistry() { 051 this(null); 052 } 053 054 protected AbstractContributionRegistry(AbstractContributionRegistry<K, T> parent) { 055 registry = new HashMap<Object, Contribution<K, T>>(); 056 this.parent = parent; 057 listeners = new ArrayList<AbstractContributionRegistry<K, T>>(); 058 // subclasses may call importParentContributions(); after initializing the registry 059 // this will import all resolved contributions from the parent 060 } 061 062 public ContributionRegistry<K, T> getParent() { 063 return parent; 064 } 065 066 protected synchronized void importParentContributions() { 067 AbstractContributionRegistry<K, T> pParent = parent; 068 List<AbstractContributionRegistry<K, T>> parents = new ArrayList<AbstractContributionRegistry<K, T>>(); 069 while (pParent != null) { 070 parents.add(pParent); 071 pParent = pParent.parent; 072 } 073 Collections.reverse(parents); 074 for (AbstractContributionRegistry<K, T> p : parents) { 075 p.listeners.add(this); 076 for (Contribution<K, T> contrib : p.registry.values().toArray(new Contribution[p.registry.size()])) { 077 if (contrib.isResolved()) { 078 installContribution(contrib.getId(), contrib.getValue()); 079 } 080 } 081 p = p.parent; 082 } 083 } 084 085 public synchronized Contribution<K, T> getContribution(K primaryKey) { 086 Contribution<K, T> contrib = registry.get(primaryKey); 087 if (contrib == null && parent != null) { 088 contrib = parent.getContribution(primaryKey); 089 } 090 return contrib; 091 } 092 093 public T getObject(K key) { 094 Contribution<K, T> contrib = getContribution(key); 095 if (contrib != null) { 096 if (contrib.isResolved()) { 097 return contrib.getValue(); 098 } 099 } 100 return null; 101 } 102 103 public synchronized void removeContribution(K key) { 104 Contribution<K, T> contrib = registry.get(key); 105 if (contrib != null) { 106 contrib.unregister(); 107 } 108 // TODO if all dependents are unregistered remove contribution from 109 // registry 110 } 111 112 public void removeFragment(K key, T fragment) { 113 Contribution<K, T> contrib = registry.get(key); 114 if (contrib != null) { 115 contrib.removeFragment(fragment); 116 } 117 } 118 119 public synchronized Contribution<K, T> addFragment(K key, T fragment, K... superKeys) { 120 Contribution<K, T> contrib = registry.get(key); 121 if (contrib == null) { 122 contrib = new ContributionImpl<K, T>(this, key); 123 registry.put(key, contrib); 124 } 125 contrib.addFragment(fragment, superKeys); 126 return contrib; 127 } 128 129 public synchronized Contribution<K, T> getOrCreateDependency(K key) { 130 Contribution<K, T> contrib = getContribution(key); 131 if (contrib == null) { 132 contrib = new ContributionImpl<K, T>(this, key); 133 registry.put(key, contrib); 134 // do not register so that this contribution will be a phantom 135 } 136 return contrib; 137 } 138 139 public void fireUnresolved(Contribution<K, T> contrib, T value) { 140 K key = contrib.getId(); 141 uninstallContribution(key, value); 142 if (!listeners.isEmpty()) { 143 for (AbstractContributionRegistry<K, T> reg : listeners) { 144 reg.uninstallContribution(key, value); 145 } 146 } 147 } 148 149 public void fireResolved(Contribution<K, T> contrib) { 150 K key = contrib.getId(); 151 T value = contrib.getValue(); 152 if (value == null) { 153 throw new IllegalStateException("contribution is null"); 154 } 155 installContribution(key, value); 156 if (!listeners.isEmpty()) { 157 for (AbstractContributionRegistry<K, T> reg : listeners) { 158 reg.installContribution(key, value); 159 } 160 } 161 } 162 163 public void fireUpdated(T oldValue, Contribution<K, T> contrib) { 164 T value = contrib.getValue(); 165 if (value == null) { 166 throw new IllegalStateException("contribution is null"); 167 } 168 updateContribution(contrib.getId(), value, oldValue); 169 } 170 171 public void dispose() { 172 registry.clear(); 173 } 174 175 protected abstract T clone(T object); 176 177 /** 178 * Applies fragment over the given object. 179 * 180 * @param object 181 * @param fragment 182 */ 183 protected void applyFragment(T object, T fragment) { 184 // do nothing 185 } 186 187 protected void applySuperFragment(T object, T superFragment) { 188 // do nothing 189 } 190 191 protected abstract void installContribution(K key, T object); 192 193 protected abstract void uninstallContribution(K key, T object); 194 195 protected boolean isMainFragment(T object) { 196 return true; 197 } 198 199 protected void updateContribution(K key, T object, T oldValue) { 200 uninstallContribution(key, oldValue); 201 installContribution(key, object); 202 } 203 204}