]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/internal/kernel/DataModels.java
Clarify naming.
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / internal / kernel / DataModels.java
1 package org.argeo.cms.internal.kernel;
2
3 import static org.argeo.api.DataModelNamespace.CMS_DATA_MODEL_NAMESPACE;
4
5 import java.util.ArrayList;
6 import java.util.Collections;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.TreeMap;
10
11 import org.apache.commons.logging.Log;
12 import org.apache.commons.logging.LogFactory;
13 import org.argeo.api.DataModelNamespace;
14 import org.argeo.cms.CmsException;
15 import org.osgi.framework.Bundle;
16 import org.osgi.framework.BundleContext;
17 import org.osgi.framework.BundleEvent;
18 import org.osgi.framework.BundleListener;
19 import org.osgi.framework.wiring.BundleCapability;
20 import org.osgi.framework.wiring.BundleWire;
21 import org.osgi.framework.wiring.BundleWiring;
22
23 class DataModels implements BundleListener {
24 private final static Log log = LogFactory.getLog(DataModels.class);
25
26 private Map<String, DataModel> dataModels = new TreeMap<>();
27
28 public DataModels(BundleContext bc) {
29 for (Bundle bundle : bc.getBundles())
30 processBundle(bundle, null);
31 bc.addBundleListener(this);
32 }
33
34 public List<DataModel> getNonAbstractDataModels() {
35 List<DataModel> res = new ArrayList<>();
36 for (String name : dataModels.keySet()) {
37 DataModel dataModel = dataModels.get(name);
38 if (!dataModel.isAbstract())
39 res.add(dataModel);
40 }
41 // TODO reorder?
42 return res;
43 }
44
45 @Override
46 public void bundleChanged(BundleEvent event) {
47 if (event.getType() == Bundle.RESOLVED) {
48 processBundle(event.getBundle(), null);
49 } else if (event.getType() == Bundle.UNINSTALLED) {
50 BundleWiring wiring = event.getBundle().adapt(BundleWiring.class);
51 List<BundleCapability> providedDataModels = wiring.getCapabilities(CMS_DATA_MODEL_NAMESPACE);
52 if (providedDataModels.size() == 0)
53 return;
54 for (BundleCapability bundleCapability : providedDataModels) {
55 dataModels.remove(bundleCapability.getAttributes().get(DataModelNamespace.NAME));
56 }
57 }
58
59 }
60
61 protected void processBundle(Bundle bundle, List<Bundle> scannedBundles) {
62 if (scannedBundles != null && scannedBundles.contains(bundle))
63 throw new IllegalStateException("Cycle in CMS data model requirements for " + bundle);
64 BundleWiring wiring = bundle.adapt(BundleWiring.class);
65 if (wiring == null) {
66 int bundleState = bundle.getState();
67 if (bundleState != Bundle.INSTALLED && bundleState != Bundle.UNINSTALLED) {// ignore unresolved bundles
68 log.warn("Bundle " + bundle.getSymbolicName() + " #" + bundle.getBundleId() + " ("
69 + bundle.getLocation() + ") cannot be adapted to a wiring");
70 } else {
71 if (log.isTraceEnabled())
72 log.warn("Bundle " + bundle.getSymbolicName() + " is not resolved.");
73 }
74 return;
75 }
76 List<BundleCapability> providedDataModels = wiring.getCapabilities(CMS_DATA_MODEL_NAMESPACE);
77 if (providedDataModels.size() == 0)
78 return;
79 List<BundleWire> requiredDataModels = wiring.getRequiredWires(CMS_DATA_MODEL_NAMESPACE);
80 // process requirements first
81 for (BundleWire bundleWire : requiredDataModels) {
82 List<Bundle> nextScannedBundles = new ArrayList<>();
83 if (scannedBundles != null)
84 nextScannedBundles.addAll(scannedBundles);
85 nextScannedBundles.add(bundle);
86 Bundle providerBundle = bundleWire.getProvider().getBundle();
87 processBundle(providerBundle, nextScannedBundles);
88 }
89 for (BundleCapability bundleCapability : providedDataModels) {
90 String name = (String) bundleCapability.getAttributes().get(DataModelNamespace.NAME);
91 assert name != null;
92 if (!dataModels.containsKey(name)) {
93 DataModel dataModel = new DataModel(name, bundleCapability, requiredDataModels);
94 dataModels.put(dataModel.getName(), dataModel);
95 }
96 }
97 }
98
99 /** Return a negative depth if dataModel is required by ref, 0 otherwise. */
100 static int required(DataModel ref, DataModel dataModel, int depth) {
101 for (DataModel dm : ref.getRequired()) {
102 if (dm.equals(dataModel))// found here
103 return depth - 1;
104 int d = required(dm, dataModel, depth - 1);
105 if (d != 0)// found deeper
106 return d;
107 }
108 return 0;// not found
109 }
110
111 class DataModel {
112 private final String name;
113 private final boolean abstrct;
114 // private final boolean standalone;
115 private final String cnd;
116 private final List<DataModel> required;
117
118 private DataModel(String name, BundleCapability bundleCapability, List<BundleWire> requiredDataModels) {
119 assert CMS_DATA_MODEL_NAMESPACE.equals(bundleCapability.getNamespace());
120 this.name = name;
121 Map<String, Object> attrs = bundleCapability.getAttributes();
122 abstrct = KernelUtils.asBoolean((String) attrs.get(DataModelNamespace.ABSTRACT));
123 // standalone = KernelUtils.asBoolean((String)
124 // attrs.get(DataModelNamespace.CAPABILITY_STANDALONE_ATTRIBUTE));
125 cnd = (String) attrs.get(DataModelNamespace.CND);
126 List<DataModel> req = new ArrayList<>();
127 for (BundleWire wire : requiredDataModels) {
128 String requiredDataModelName = (String) wire.getCapability().getAttributes()
129 .get(DataModelNamespace.NAME);
130 assert requiredDataModelName != null;
131 DataModel requiredDataModel = dataModels.get(requiredDataModelName);
132 if (requiredDataModel == null)
133 throw new CmsException("No required data model " + requiredDataModelName);
134 req.add(requiredDataModel);
135 }
136 required = Collections.unmodifiableList(req);
137 }
138
139 public String getName() {
140 return name;
141 }
142
143 public boolean isAbstract() {
144 return abstrct;
145 }
146
147 // public boolean isStandalone() {
148 // return !isAbstract();
149 // }
150
151 public String getCnd() {
152 return cnd;
153 }
154
155 public List<DataModel> getRequired() {
156 return required;
157 }
158
159 // @Override
160 // public int compareTo(DataModel o) {
161 // if (equals(o))
162 // return 0;
163 // int res = required(this, o, 0);
164 // if (res != 0)
165 // return res;
166 // // the other way round
167 // res = required(o, this, 0);
168 // if (res != 0)
169 // return -res;
170 // return 0;
171 // }
172
173 @Override
174 public int hashCode() {
175 return name.hashCode();
176 }
177
178 @Override
179 public boolean equals(Object obj) {
180 if (obj instanceof DataModel)
181 return ((DataModel) obj).name.equals(name);
182 return false;
183 }
184
185 @Override
186 public String toString() {
187 return "Data model " + name;
188 }
189
190 }
191
192 }