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