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