]> git.argeo.org Git - lgpl/argeo-commons.git/blob - DataModels.java
48b6b7fc75f3476caab9b7a9be218feca546e8cf
[lgpl/argeo-commons.git] / DataModels.java
1 package org.argeo.cms.internal.kernel;
2
3 import static org.argeo.node.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.cms.CmsException;
14 import org.argeo.node.DataModelNamespace;
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);
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());
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) {
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 processBundle(bundleWire.getProvider().getBundle());
81 }
82 for (BundleCapability bundleCapability : providedDataModels) {
83 String name = (String) bundleCapability.getAttributes().get(DataModelNamespace.NAME);
84 assert name != null;
85 if (!dataModels.containsKey(name)) {
86 DataModel dataModel = new DataModel(name, bundleCapability, requiredDataModels);
87 dataModels.put(dataModel.getName(), dataModel);
88 }
89 }
90 }
91
92 /** Return a negative depth if dataModel is required by ref, 0 otherwise. */
93 static int required(DataModel ref, DataModel dataModel, int depth) {
94 for (DataModel dm : ref.getRequired()) {
95 if (dm.equals(dataModel))// found here
96 return depth - 1;
97 int d = required(dm, dataModel, depth - 1);
98 if (d != 0)// found deeper
99 return d;
100 }
101 return 0;// not found
102 }
103
104 class DataModel {
105 private final String name;
106 private final boolean abstrct;
107 // private final boolean standalone;
108 private final String cnd;
109 private final List<DataModel> required;
110
111 private DataModel(String name, BundleCapability bundleCapability, List<BundleWire> requiredDataModels) {
112 assert CMS_DATA_MODEL_NAMESPACE.equals(bundleCapability.getNamespace());
113 this.name = name;
114 Map<String, Object> attrs = bundleCapability.getAttributes();
115 abstrct = KernelUtils.asBoolean((String) attrs.get(DataModelNamespace.ABSTRACT));
116 // standalone = KernelUtils.asBoolean((String)
117 // attrs.get(DataModelNamespace.CAPABILITY_STANDALONE_ATTRIBUTE));
118 cnd = (String) attrs.get(DataModelNamespace.CND);
119 List<DataModel> req = new ArrayList<>();
120 for (BundleWire wire : requiredDataModels) {
121 String requiredDataModelName = (String) wire.getCapability().getAttributes()
122 .get(DataModelNamespace.NAME);
123 assert requiredDataModelName != null;
124 DataModel requiredDataModel = dataModels.get(requiredDataModelName);
125 if (requiredDataModel == null)
126 throw new CmsException("No required data model " + requiredDataModelName);
127 req.add(requiredDataModel);
128 }
129 required = Collections.unmodifiableList(req);
130 }
131
132 public String getName() {
133 return name;
134 }
135
136 public boolean isAbstract() {
137 return abstrct;
138 }
139
140 // public boolean isStandalone() {
141 // return !isAbstract();
142 // }
143
144 public String getCnd() {
145 return cnd;
146 }
147
148 public List<DataModel> getRequired() {
149 return required;
150 }
151
152 // @Override
153 // public int compareTo(DataModel o) {
154 // if (equals(o))
155 // return 0;
156 // int res = required(this, o, 0);
157 // if (res != 0)
158 // return res;
159 // // the other way round
160 // res = required(o, this, 0);
161 // if (res != 0)
162 // return -res;
163 // return 0;
164 // }
165
166 @Override
167 public int hashCode() {
168 return name.hashCode();
169 }
170
171 @Override
172 public boolean equals(Object obj) {
173 if (obj instanceof DataModel)
174 return ((DataModel) obj).name.equals(name);
175 return false;
176 }
177
178 @Override
179 public String toString() {
180 return "Data model " + name;
181 }
182
183 }
184
185 }