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 importContribution(contrib); 078 } 079 p = p.parent; 080 } 081 } 082 083 protected void importContribution(Contribution<K,T> contrib) { 084 if (!contrib.isResolved()) { 085 return; 086 } 087 for (Contribution<K, T> dep : contrib.getDependencies()) { 088 importContribution(dep); 089 } 090 installContribution(contrib.getId(), contrib.getValue()); 091 } 092 093 @Override 094 public synchronized Contribution<K, T> getContribution(K primaryKey) { 095 Contribution<K, T> contrib = registry.get(primaryKey); 096 if (contrib == null && parent != null) { 097 contrib = parent.getContribution(primaryKey); 098 } 099 return contrib; 100 } 101 102 public T getObject(K key) { 103 Contribution<K, T> contrib = getContribution(key); 104 if (contrib != null) { 105 if (contrib.isResolved()) { 106 return contrib.getValue(); 107 } 108 } 109 return null; 110 } 111 112 public synchronized void removeContribution(K key) { 113 Contribution<K, T> contrib = registry.get(key); 114 if (contrib != null) { 115 contrib.unregister(); 116 } 117 // TODO if all dependents are unregistered remove contribution from 118 // registry 119 } 120 121 public void removeFragment(K key, T fragment) { 122 Contribution<K, T> contrib = registry.get(key); 123 if (contrib != null) { 124 contrib.removeFragment(fragment); 125 } 126 } 127 128 public synchronized Contribution<K, T> addFragment(K key, T fragment, K... superKeys) { 129 Contribution<K, T> contrib = registry.get(key); 130 if (contrib == null) { 131 contrib = new ContributionImpl<K, T>(this, key); 132 registry.put(key, contrib); 133 } 134 contrib.addFragment(fragment, superKeys); 135 return contrib; 136 } 137 138 public synchronized Contribution<K, T> getOrCreateDependency(K key) { 139 Contribution<K, T> contrib = getContribution(key); 140 if (contrib == null) { 141 contrib = new ContributionImpl<K, T>(this, key); 142 registry.put(key, contrib); 143 // do not register so that this contribution will be a phantom 144 } 145 return contrib; 146 } 147 148 public void fireUnresolved(Contribution<K, T> contrib, T value) { 149 K key = contrib.getId(); 150 uninstallContribution(key, value); 151 if (!listeners.isEmpty()) { 152 for (AbstractContributionRegistry<K, T> reg : listeners) { 153 reg.uninstallContribution(key, value); 154 } 155 } 156 } 157 158 public void fireResolved(Contribution<K, T> contrib) { 159 K key = contrib.getId(); 160 T value = contrib.getValue(); 161 if (value == null) { 162 throw new IllegalStateException("contribution is null"); 163 } 164 installContribution(key, value); 165 if (!listeners.isEmpty()) { 166 for (AbstractContributionRegistry<K, T> reg : listeners) { 167 reg.installContribution(key, value); 168 } 169 } 170 } 171 172 public void fireUpdated(T oldValue, Contribution<K, T> contrib) { 173 T value = contrib.getValue(); 174 if (value == null) { 175 throw new IllegalStateException("contribution is null"); 176 } 177 updateContribution(contrib.getId(), value, oldValue); 178 } 179 180 public void dispose() { 181 registry.clear(); 182 } 183 184 protected abstract T clone(T object); 185 186 /** 187 * Applies fragment over the given object. 188 * 189 * @param object 190 * @param fragment 191 */ 192 protected void applyFragment(T object, T fragment) { 193 // do nothing 194 } 195 196 protected void applySuperFragment(T object, T superFragment) { 197 // do nothing 198 } 199 200 protected abstract void installContribution(K key, T object); 201 202 protected abstract void uninstallContribution(K key, T object); 203 204 protected boolean isMainFragment(T object) { 205 return true; 206 } 207 208 protected void updateContribution(K key, T object, T oldValue) { 209 uninstallContribution(key, oldValue); 210 installContribution(key, object); 211 } 212 213}