--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.project.management;\r
+\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.binding.error.BindingException;\r
+import org.simantics.databoard.binding.error.RuntimeBindingException;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.RequestProcessor;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.common.request.ResourceRead;\r
+import org.simantics.db.common.utils.Transaction;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.graph.representation.TransferableGraph1;\r
+import org.simantics.layer0.DatabaseManagementResource;\r
+\r
+/**\r
+ * GraphBundle represents a bundle graph that may exist in memory \r
+ * in a OSGi Bundle Context, a P2 Bundle Pool, or in Simantics Database.\r
+ * \r
+ * The string representation of the version is in the following format: \r
+ * <id>/<major.minor.micro(.qualifier)>\r
+ * \r
+ * Here is what is said about osgi version numbers:\r
+ * \r
+ * Major - Differences in the major part indicate significant differences \r
+ * such that backward compability is not guaranteed.\r
+ * \r
+ * Minor - Changes in the minor part indicate that the newer version of the\r
+ * entity is backward compatible with the older version, but it \r
+ * includes additional functionality and/or API.\r
+ * \r
+ * Service - The service part indicates the presence of bug fixes and minor\r
+ * implementation (i.e., hidden) changes over previous versions.\r
+ * \r
+ * Qualifier - The qualifier is not interpreted by the system. Qualifiers are\r
+ * compared using standard string comparison. Qualifier is determined\r
+ * at build time by builder. It may be millisecond time or version \r
+ * control revision number. The value is monotonically increasing. \r
+ * \r
+ * \r
+ * The class is hash-equals-comparable.\r
+ *\r
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>\r
+ */\r
+public class GraphBundle implements Comparable<GraphBundle> {\r
+\r
+ /** Versioned Id pattern */ \r
+ static String ID_PATTERN_STRING = "[a-zA-Z_0-9\\-]+(?:\\.[a-zA-Z_0-9\\-]+)*";\r
+ static String VERSION_PATTERN_STRING = "(\\d+).(\\d+).(\\d+).([a-zA-Z_0-9\\-]+)";\r
+ static Pattern ID_PATTERN = Pattern.compile(ID_PATTERN_STRING);\r
+ static Pattern VERSION_PATTERN = Pattern.compile(VERSION_PATTERN_STRING);\r
+ static Pattern VERSIONED_ID_PATTERN = Pattern.compile("(" + ID_PATTERN_STRING + ")/" + VERSION_PATTERN_STRING + "");\r
+ \r
+ /** User-friendly name */\r
+ String name;\r
+ \r
+ /** Actual graph */\r
+ TransferableGraph1 graph;\r
+ \r
+ /** GraphBundle resource in database */ \r
+ Resource resource;\r
+ \r
+ /** Graph hash code */\r
+ int hashcode;\r
+ \r
+ /** Id */\r
+ String id;\r
+ \r
+ // Version\r
+ int major, minor, service;\r
+ \r
+ // Optional qualifier\r
+ String qualifier;\r
+ \r
+ /** Database install Info, optional */\r
+ long[] resourceArray;\r
+\r
+ /** Should this ontology be installed immutable **/\r
+ boolean immutable = true;\r
+\r
+ GraphBundle() {}\r
+ \r
+ public GraphBundle(String name, TransferableGraph1 data, String versionedId) \r
+ throws RuntimeBindingException { \r
+ try { \r
+ // Assert version id is correct\r
+ Matcher m = VERSIONED_ID_PATTERN.matcher(versionedId); \r
+ if (!m.matches()) {\r
+ throw new IllegalArgumentException("Illegal VersionId \""+versionedId+"\", <id>/<major.minor.micro.qualifier> is expected.");\r
+ }\r
+ \r
+ Binding binding = Bindings.getBindingUnchecked( TransferableGraph1.class ); \r
+ \r
+ this.name = name;\r
+ this.graph = data; \r
+ this.hashcode = data != null ? binding.hashValue( data ) : 0;\r
+ this.id = m.group(1);\r
+ this.major = Integer.valueOf( m.group(2) );\r
+ this.minor = Integer.valueOf( m.group(3) );\r
+ if (m.group(4) != null) {\r
+ this.service = Integer.valueOf( m.group(4) );\r
+ }\r
+ this.qualifier = m.group(5);\r
+ } catch (BindingException e) {\r
+ // Unexpected\r
+ throw new RuntimeBindingException(e);\r
+ } \r
+ }\r
+ \r
+ public GraphBundle(String name, TransferableGraph1 data, String id, String version) \r
+ throws RuntimeBindingException { \r
+ Matcher m = ID_PATTERN.matcher(id);\r
+ if (!m.matches()) \r
+ throw new IllegalArgumentException("Illegal Id, got \""+id+"\"");\r
+ m = VERSION_PATTERN.matcher(version);\r
+ if (!m.matches()) \r
+ throw new IllegalArgumentException("Illegal Version, got \""+id+"\", <id>/<major.minor.micro.qualifier> is expected.");\r
+ try { \r
+ Binding binding = Bindings.getBindingUnchecked( TransferableGraph1.class ); \r
+ this.name = name;\r
+ this.graph = data; \r
+ this.hashcode = binding.hashValue( data );\r
+ this.id = id;\r
+ this.major = Integer.valueOf( m.group(1) );\r
+ this.minor = Integer.valueOf( m.group(2) );\r
+ this.service = Integer.valueOf( m.group(3) );\r
+ if (m.group(4) != null) {\r
+ this.qualifier = m.group(4);\r
+ }\r
+ } catch (BindingException e) {\r
+ // Unexpected\r
+ throw new RuntimeBindingException(e);\r
+ }\r
+ }\r
+ \r
+ public String getName() {\r
+ return name;\r
+ }\r
+\r
+ @Override\r
+ public int compareTo(GraphBundle o) {\r
+ int cur = id.compareTo(o.id);\r
+ if(cur != 0)\r
+ return cur;\r
+ \r
+ cur = major - o.major;\r
+ if(cur != 0)\r
+ return cur;\r
+ \r
+ cur = minor - o.minor;\r
+ if(cur != 0)\r
+ return cur;\r
+ \r
+ cur = service - o.service; \r
+ return cur;\r
+ }\r
+ \r
+ /**\r
+ * This method excepts {@link Transaction#readGraph()} to return a non-null\r
+ * value, i.e. a database transaction must be in progress that has been\r
+ * started with\r
+ * {@link Transaction#startTransaction(RequestProcessor, boolean)}.\r
+ * \r
+ * @return\r
+ * @see #getGraph(RequestProcessor)\r
+ */\r
+ public TransferableGraph1 getGraph() {\r
+ if (graph == null) {\r
+ ReadGraph g = Transaction.readGraph();\r
+ if (g == null)\r
+ throw new IllegalStateException("No read transaction available");\r
+ try {\r
+ Binding tg_binding = Bindings.getBindingUnchecked( TransferableGraph1.class );\r
+ DatabaseManagementResource DatabaseManagement = DatabaseManagementResource.getInstance(g);\r
+ graph = g.getRelatedValue(resource, DatabaseManagement.HasFile, tg_binding); \r
+ } catch (DatabaseException e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+ return graph;\r
+ }\r
+\r
+ public TransferableGraph1 getGraph(RequestProcessor processor) {\r
+ if (graph == null) {\r
+ try {\r
+ graph = processor.syncRequest(new ResourceRead<TransferableGraph1>(resource) {\r
+ @Override\r
+ public TransferableGraph1 perform(ReadGraph graph) throws DatabaseException {\r
+ Binding tg_binding = Bindings.getBindingUnchecked( TransferableGraph1.class );\r
+ DatabaseManagementResource DatabaseManagement = DatabaseManagementResource.getInstance(graph);\r
+ return graph.getRelatedValue(resource, DatabaseManagement.HasFile, tg_binding); \r
+ }\r
+ });\r
+ } catch (DatabaseException e) {\r
+ e.printStackTrace();\r
+ }\r
+ }\r
+ return graph;\r
+ }\r
+ \r
+ public int getHashcode() {\r
+ return hashcode;\r
+ }\r
+ \r
+ @Override\r
+ public int hashCode() {\r
+ return 31*id.hashCode() + 7*major + 3*minor + 11*service + (qualifier!=null?13*qualifier.hashCode():0) + hashcode;\r
+ }\r
+ \r
+ @Override\r
+ public boolean equals(Object obj) {\r
+ if (obj instanceof GraphBundle == false) return false;\r
+ GraphBundle other = (GraphBundle) obj;\r
+ if (other.hashcode != hashcode) return false;\r
+ if (!other.id.equals(id)) return false;\r
+ if (other.major != major) return false;\r
+ if (other.minor != minor) return false;\r
+ if (other.service != service) return false;\r
+ if (!objectEquals(other.qualifier, qualifier )) return false;\r
+ return true;\r
+ } \r
+ \r
+ static boolean objectEquals(Object o1, Object o2) {\r
+ if (o1 == o2) return true;\r
+ if (o1 == null && o2 == null) return true;\r
+ if (o1 == null || o2 == null) return false;\r
+ return o1.equals(o2);\r
+ } \r
+ \r
+ public boolean getImmutable() {\r
+ return immutable;\r
+ }\r
+ \r
+ public String getId() {\r
+ return id;\r
+ }\r
+ \r
+ public int getMajor() {\r
+ return major;\r
+ }\r
+ \r
+ public int getMinor() {\r
+ return minor;\r
+ }\r
+ \r
+ public int getService() {\r
+ return service;\r
+ }\r
+ \r
+ public String getQualifier() {\r
+ return qualifier;\r
+ }\r
+\r
+ public String getVersionedId() {\r
+ return id+"/"+major+"."+minor+"."+service+"."+qualifier;\r
+ }\r
+ \r
+ @Override\r
+ public String toString() {\r
+ return name+", "+id+"/"+getVersionedId()+", hash="+hashcode;\r
+ }\r
+\r
+ public long[] getResourceArray() {\r
+ return resourceArray;\r
+ }\r
+\r
+ public void setResourceArray(long[] resourceArray) {\r
+ this.resourceArray = resourceArray;\r
+ }\r
+ \r
+ public static void main(String[] args) {\r
+ Matcher m = VERSIONED_ID_PATTERN.matcher("org.simantics.layer0/1.1.1.qualifier");\r
+ if (m.matches()) {\r
+ System.out.println( m.groupCount() );\r
+ }\r
+ \r
+ m = VERSIONED_ID_PATTERN.matcher("org.simantics.layer0/1.1.1");\r
+ if (m.matches()) {\r
+ System.out.println( m.groupCount() );\r
+ }\r
+ \r
+ m = VERSIONED_ID_PATTERN.matcher("org.simantics.layer0/1.1.1.200810101010");\r
+ if (m.matches()) {\r
+ System.out.println( m.groupCount() );\r
+ }\r
+ \r
+ m = VERSIONED_ID_PATTERN.matcher("org.simantics.layer0/1.1");\r
+ if (m.matches()) {\r
+ System.out.println( m.groupCount() );\r
+ }\r
+ \r
+ }\r
+ \r
+}\r
+\r