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