1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.project.management;
14 import java.util.function.Supplier;
15 import java.util.regex.Matcher;
16 import java.util.regex.Pattern;
18 import org.simantics.databoard.binding.error.BindingException;
19 import org.simantics.databoard.binding.error.RuntimeBindingException;
20 import org.simantics.db.ReadGraph;
21 import org.simantics.db.RequestProcessor;
22 import org.simantics.db.Resource;
23 import org.simantics.db.common.request.ResourceRead;
24 import org.simantics.db.common.utils.Transaction;
25 import org.simantics.db.exception.DatabaseException;
26 import org.simantics.graph.representation.TransferableGraph1;
27 import org.simantics.layer0.DatabaseManagementResource;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
32 * GraphBundle represents a bundle graph that may exist in memory
33 * in a OSGi Bundle Context, a P2 Bundle Pool, or in Simantics Database.
35 * The string representation of the version is in the following format:
36 * <id>/<major.minor.micro(.qualifier)>
38 * Here is what is said about osgi version numbers:
40 * Major - Differences in the major part indicate significant differences
41 * such that backward compability is not guaranteed.
43 * Minor - Changes in the minor part indicate that the newer version of the
44 * entity is backward compatible with the older version, but it
45 * includes additional functionality and/or API.
47 * Service - The service part indicates the presence of bug fixes and minor
48 * implementation (i.e., hidden) changes over previous versions.
50 * Qualifier - The qualifier is not interpreted by the system. Qualifiers are
51 * compared using standard string comparison. Qualifier is determined
52 * at build time by builder. It may be millisecond time or version
53 * control revision number. The value is monotonically increasing.
56 * The class is hash-equals-comparable.
58 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
60 public class GraphBundle implements Comparable<GraphBundle> {
62 private static final Logger LOGGER = LoggerFactory.getLogger(GraphBundle.class);
64 /** Versioned Id pattern */
65 static String ID_PATTERN_STRING = "[a-zA-Z_0-9\\-]+(?:\\.[a-zA-Z_0-9\\-]+)*";
66 static String VERSION_PATTERN_STRING = "(\\d+).(\\d+).(\\d+).([a-zA-Z_0-9\\-]+)";
67 static Pattern ID_PATTERN = Pattern.compile(ID_PATTERN_STRING);
68 static Pattern VERSION_PATTERN = Pattern.compile(VERSION_PATTERN_STRING);
69 static Pattern VERSIONED_ID_PATTERN = Pattern.compile("(" + ID_PATTERN_STRING + ")/" + VERSION_PATTERN_STRING + "");
71 /** User-friendly name */
74 /** If {@link #graph} is null then this may be defined to fetch the data on-demand */
75 Supplier<TransferableGraph1> graphSource;
78 TransferableGraph1 graph;
80 /** GraphBundle resource in database */
83 /** Graph hash code */
90 int major, minor, service;
95 /** Database install Info, optional */
98 /** Should this ontology be installed immutable **/
99 boolean immutable = true;
103 public GraphBundle(String name, TransferableGraph1 data, String versionedId)
104 throws RuntimeBindingException {
106 // Assert version id is correct
107 Matcher m = VERSIONED_ID_PATTERN.matcher(versionedId);
109 throw new IllegalArgumentException("Illegal VersionId \""+versionedId+"\", <id>/<major.minor.micro.qualifier> is expected.");
114 this.hashcode = hash(data);
115 this.id = m.group(1);
116 this.major = Integer.valueOf( m.group(2) );
117 this.minor = Integer.valueOf( m.group(3) );
118 if (m.group(4) != null) {
119 this.service = Integer.valueOf( m.group(4) );
121 this.qualifier = m.group(5);
122 } catch (BindingException e) {
124 throw new RuntimeBindingException(e);
128 public GraphBundle(String name, TransferableGraph1 data, String id, String version)
129 throws RuntimeBindingException {
130 Matcher m = ID_PATTERN.matcher(id);
132 throw new IllegalArgumentException("Illegal Id, got \""+id+"\"");
133 m = VERSION_PATTERN.matcher(version);
135 throw new IllegalArgumentException("Illegal Version, got \""+id+"\", <id>/<major.minor.micro.qualifier> is expected.");
139 this.hashcode = hash(data);
141 this.major = Integer.valueOf( m.group(1) );
142 this.minor = Integer.valueOf( m.group(2) );
143 this.service = Integer.valueOf( m.group(3) );
144 if (m.group(4) != null) {
145 this.qualifier = m.group(4);
147 } catch (BindingException e) {
149 throw new RuntimeBindingException(e);
153 public GraphBundle(String name, Supplier<TransferableGraph1> source, int hashCode, String id, String version) {
154 Matcher m = ID_PATTERN.matcher(id);
156 throw new IllegalArgumentException("Illegal Id, got \""+id+"\"");
157 m = VERSION_PATTERN.matcher(version);
159 throw new IllegalArgumentException("Illegal Version, got \""+id+"\", <id>/<major.minor.micro.qualifier> is expected.");
161 this.graphSource = source;
162 this.hashcode = hashCode;
164 this.major = Integer.valueOf( m.group(1) );
165 this.minor = Integer.valueOf( m.group(2) );
166 this.service = Integer.valueOf( m.group(3) );
167 if (m.group(4) != null) {
168 this.qualifier = m.group(4);
172 private int hash(TransferableGraph1 data) throws BindingException {
173 return data == null ? 0 : TransferableGraph1.BINDING.hashValue( data );
176 public String getName() {
181 public int compareTo(GraphBundle o) {
182 int cur = id.compareTo(o.id);
186 cur = major - o.major;
190 cur = minor - o.minor;
194 cur = service - o.service;
199 * This method excepts {@link Transaction#readGraph()} to return a non-null
200 * value, i.e. a database transaction must be in progress that has been
202 * {@link Transaction#startTransaction(RequestProcessor, boolean)}.
205 * @see #getGraph(RequestProcessor)
207 public TransferableGraph1 getGraph() {
209 if (graphSource != null) {
210 graph = graphSource.get();
213 ReadGraph g = Transaction.readGraph();
215 throw new IllegalStateException("No read transaction available");
218 } catch (DatabaseException e) {
219 LOGGER.error("Failed to read transferable graph from " + resource, e);
226 public TransferableGraph1 getGraph(RequestProcessor processor) {
229 graph = processor.syncRequest(new ResourceRead<TransferableGraph1>(resource) {
231 public TransferableGraph1 perform(ReadGraph graph) throws DatabaseException {
232 return readTg(graph);
235 } catch (DatabaseException e) {
236 LOGGER.error("Failed to read transferable graph from " + resource, e);
242 private TransferableGraph1 readTg(ReadGraph graph) throws DatabaseException {
243 DatabaseManagementResource DatabaseManagement = DatabaseManagementResource.getInstance(graph);
244 return graph.getRelatedValue(resource, DatabaseManagement.HasFile, TransferableGraph1.BINDING);
247 public int getHashcode() {
252 public int hashCode() {
253 return 31*id.hashCode() + 7*major + 3*minor + 11*service + (qualifier!=null?13*qualifier.hashCode():0) + hashcode;
257 public boolean equals(Object obj) {
258 if (obj instanceof GraphBundle == false) return false;
259 GraphBundle other = (GraphBundle) obj;
260 if (other.hashcode != hashcode) return false;
261 if (!other.id.equals(id)) return false;
262 if (other.major != major) return false;
263 if (other.minor != minor) return false;
264 if (other.service != service) return false;
265 if (!objectEquals(other.qualifier, qualifier )) return false;
269 static boolean objectEquals(Object o1, Object o2) {
270 if (o1 == o2) return true;
271 if (o1 == null && o2 == null) return true;
272 if (o1 == null || o2 == null) return false;
273 return o1.equals(o2);
276 public boolean getImmutable() {
280 public String getId() {
284 public int getMajor() {
288 public int getMinor() {
292 public int getService() {
296 public String getQualifier() {
300 public String getVersionedId() {
301 return id+"/"+major+"."+minor+"."+service+"."+qualifier;
305 public String toString() {
306 return name+", "+id+"/"+getVersionedId()+", hash="+hashcode;
309 public long[] getResourceArray() {
310 return resourceArray;
313 public void setResourceArray(long[] resourceArray) {
314 this.resourceArray = resourceArray;
317 public static void main(String[] args) {
318 Matcher m = VERSIONED_ID_PATTERN.matcher("org.simantics.layer0/1.1.1.qualifier");
320 System.out.println( m.groupCount() );
323 m = VERSIONED_ID_PATTERN.matcher("org.simantics.layer0/1.1.1");
325 System.out.println( m.groupCount() );
328 m = VERSIONED_ID_PATTERN.matcher("org.simantics.layer0/1.1.1.200810101010");
330 System.out.println( m.groupCount() );
333 m = VERSIONED_ID_PATTERN.matcher("org.simantics.layer0/1.1");
335 System.out.println( m.groupCount() );