Sort symbol item contributions alphanumerically, not lexicographically
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / symbolcontribution / BasicSymbolProviderFactory.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
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
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.diagram.symbolcontribution;
13
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.Collections;
17 import java.util.Comparator;
18
19 import org.simantics.databoard.Bindings;
20 import org.simantics.db.ReadGraph;
21 import org.simantics.db.Resource;
22 import org.simantics.db.common.request.BinaryRead;
23 import org.simantics.db.common.request.ObjectsWithSupertype;
24 import org.simantics.db.common.utils.NameUtils;
25 import org.simantics.db.exception.DatabaseException;
26 import org.simantics.diagram.stubs.DiagramResource;
27 import org.simantics.diagram.symbollibrary.ISymbolGroup;
28 import org.simantics.diagram.symbollibrary.ISymbolItem;
29 import org.simantics.layer0.Layer0;
30 import org.simantics.utils.strings.AlphanumComparator;
31
32 /**
33  * A basic SymbolProviderFactory implementation for the graph database. It is
34  * capable of loading an ISymbolProvider from BasicSymbolContribution instances
35  * like the following:
36  * 
37  * <pre>
38  * _ : DIA.BasicSymbolContribution
39  *     DIA.BasicSymbolContributionHasSymbolLibrary
40  *         MySymbolLibrary1
41  *         MySymbolLibrary2
42  * </pre>
43  * 
44  * @author Tuukka Lehtonen
45  */
46 public class BasicSymbolProviderFactory implements SymbolProviderFactory {
47                 
48     Resource contribution;
49     Resource diagram;
50
51     public BasicSymbolProviderFactory(Resource contribution, Resource diagram) {
52         this.contribution = contribution;
53         this.diagram = diagram;
54     }
55
56         @Override
57         public int hashCode() {
58                 final int prime = 31;
59                 int result = 1;
60                 result = prime * result + ((contribution == null) ? 0 : contribution.hashCode());
61                 result = prime * result + ((diagram == null) ? 0 : diagram.hashCode());
62                 return result;
63         }
64
65         @Override
66         public boolean equals(Object obj) {
67                 if (this == obj)
68                         return true;
69                 if (obj == null)
70                         return false;
71                 if (getClass() != obj.getClass())
72                         return false;
73                 BasicSymbolProviderFactory other = (BasicSymbolProviderFactory) obj;
74                 if (contribution == null) {
75                         if (other.contribution != null)
76                                 return false;
77                 } else if (!contribution.equals(other.contribution))
78                         return false;
79                 if (diagram == null) {
80                         if (other.diagram != null)
81                                 return false;
82                 } else if (!diagram.equals(other.diagram))
83                         return false;
84                 return true;
85         }
86     
87     @Override
88     public ISymbolProvider create(ReadGraph g) throws DatabaseException {
89         return g.syncRequest(new LoadRequest(contribution, diagram));
90     }
91
92     /*
93      * Note: this cannot be ResourceRead since it must never be
94      * classified as immutable because of possible dynamic filters
95      */
96     static class LoadRequest extends BinaryRead<Resource, Resource, ISymbolProvider> {
97         public LoadRequest(Resource contribution, Resource diagram) {
98             super(contribution, diagram);
99         }
100         @Override
101         public ISymbolProvider perform(ReadGraph graph) throws DatabaseException {
102             Layer0 l0 = Layer0.getInstance(graph);
103             DiagramResource dr = DiagramResource.getInstance(graph);
104
105             if (!SymbolProviderFactories.accept(graph, dr, parameter, parameter2))
106                 return new SymbolProvider(Collections.<ISymbolGroup>emptyList());
107
108             Collection<Resource> libraries = graph.getObjects(parameter, dr.BasicSymbolContributionHasSymbolLibrary);
109             Collection<ISymbolGroup> groups = new ArrayList<ISymbolGroup>(libraries.size());
110
111             for (Resource library : libraries) {
112                 if (SymbolProviderFactories.accept(graph, dr, library, parameter2)) {
113                     groups.add(createGroup(graph, library, l0.DependsOn, parameter2));
114                 }
115             }
116
117             return new SymbolProvider(groups);
118             
119         }
120     }
121
122     static class SymbolProvider extends AbstractSymbolProvider {
123         public SymbolProvider(Collection<ISymbolGroup> groups) {
124             super();
125             setSymbolGroup(groups);
126             lockGroups();
127         }
128     }
129
130     static SymbolGroup createGroup(ReadGraph graph, Resource library, Resource relation, Resource diagram) throws DatabaseException {
131         Layer0 l0 = Layer0.getInstance(graph);
132         DiagramResource dr = DiagramResource.getInstance(graph);
133
134         String name = safeName(graph, library);
135         String description = graph.getPossibleRelatedValue2(library, l0.HasDescription, Bindings.STRING);
136         if (description == null)
137             description = name;
138         ModifiableSymbolGroup group = new ModifiableSymbolGroup(library, name, description);
139
140         ArrayList<ISymbolItem> items = new ArrayList<ISymbolItem>();
141         for (Resource item : graph.syncRequest(new ObjectsWithSupertype(library, relation, dr.Element))) {
142             if (!SymbolProviderFactories.accept(graph, dr, item, diagram)) {
143                 continue;
144             }
145
146             String itemName = safeName(graph, item);
147             String itemDescription = graph.getPossibleRelatedValue2(item, l0.HasDescription, Bindings.STRING);
148             if (itemDescription == null || itemDescription.isEmpty())
149                 itemDescription = itemName;
150             items.add( new ElementSymbolItem(item, itemName, itemDescription, group) );
151         }
152
153         Collections.sort(items, new Comparator<ISymbolItem>() {
154             @Override
155             public int compare(ISymbolItem o1, ISymbolItem o2) {
156                 return AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(o1.getName(), o2.getName());
157             }
158         });
159
160         group.setItems(items.toArray(new ISymbolItem[items.size()]));
161
162         return group;
163     }
164
165     private static String safeName(ReadGraph graph, Resource r) throws DatabaseException {
166         Layer0 L0 = Layer0.getInstance(graph);
167         String name = graph.getPossibleRelatedValue2(r, L0.HasLabel, Bindings.STRING);
168         if (name == null || name.isEmpty())
169             name = graph.getPossibleRelatedValue(r, L0.HasName, Bindings.STRING);
170         if (name == null)
171             name = NameUtils.getSafeName(graph, r);
172         return name;
173     }
174
175 }