aboutsummaryrefslogtreecommitdiffstats
path: root/winery/org.eclipse.winery.repository/src/main/java/org/eclipse/winery/repository/backend/BackendUtils.java
blob: eeddfba34cb5d015818c5500ec63a51747e9d7fd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
/*******************************************************************************
 * Copyright (c) 2012-2013,2015 University of Stuttgart.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and the Apache License 2.0 which both accompany this distribution,
 * and are available at http://www.eclipse.org/legal/epl-v10.html
 * and http://www.apache.org/licenses/LICENSE-2.0
 *
 * Contributors:
 *     Oliver Kopp - initial API and implementation
 *******************************************************************************/
package org.eclipse.winery.repository.backend;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.nio.file.attribute.FileTime;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.SortedSet;

import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.namespace.QName;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.xerces.impl.dv.XSSimpleType;
import org.apache.xerces.impl.xs.XSImplementationImpl;
import org.apache.xerces.xs.XSComplexTypeDefinition;
import org.apache.xerces.xs.XSElementDeclaration;
import org.apache.xerces.xs.XSImplementation;
import org.apache.xerces.xs.XSLoader;
import org.apache.xerces.xs.XSModel;
import org.apache.xerces.xs.XSModelGroup;
import org.apache.xerces.xs.XSObjectList;
import org.apache.xerces.xs.XSParticle;
import org.apache.xerces.xs.XSTerm;
import org.apache.xerces.xs.XSTypeDefinition;
import org.eclipse.winery.common.ModelUtilities;
import org.eclipse.winery.common.RepositoryFileReference;
import org.eclipse.winery.common.Util;
import org.eclipse.winery.common.ids.GenericId;
import org.eclipse.winery.common.ids.IdUtil;
import org.eclipse.winery.common.ids.Namespace;
import org.eclipse.winery.common.ids.definitions.EntityTypeId;
import org.eclipse.winery.common.ids.definitions.NodeTypeImplementationId;
import org.eclipse.winery.common.ids.definitions.TOSCAComponentId;
import org.eclipse.winery.common.ids.definitions.imports.GenericImportId;
import org.eclipse.winery.common.ids.elements.PlansId;
import org.eclipse.winery.common.ids.elements.TOSCAElementId;
import org.eclipse.winery.common.propertydefinitionkv.PropertyDefinitionKV;
import org.eclipse.winery.common.propertydefinitionkv.PropertyDefinitionKVList;
import org.eclipse.winery.common.propertydefinitionkv.WinerysPropertiesDefinition;
import org.eclipse.winery.model.tosca.Definitions;
import org.eclipse.winery.model.tosca.ObjectFactory;
import org.eclipse.winery.model.tosca.TDeploymentArtifact;
import org.eclipse.winery.model.tosca.TDeploymentArtifacts;
import org.eclipse.winery.model.tosca.TEntityTemplate;
import org.eclipse.winery.model.tosca.TEntityType;
import org.eclipse.winery.model.tosca.TEntityType.PropertiesDefinition;
import org.eclipse.winery.model.tosca.TExtensibleElements;
import org.eclipse.winery.model.tosca.TImplementationArtifacts;
import org.eclipse.winery.model.tosca.TImplementationArtifacts.ImplementationArtifact;
import org.eclipse.winery.model.tosca.TNodeTemplate;
import org.eclipse.winery.model.tosca.TServiceTemplate;
import org.eclipse.winery.model.tosca.TTopologyTemplate;
import org.eclipse.winery.repository.Constants;
import org.eclipse.winery.repository.JAXBSupport;
import org.eclipse.winery.repository.Utils;
import org.eclipse.winery.repository.backend.constants.Filename;
import org.eclipse.winery.repository.datatypes.ids.admin.AdminId;
import org.eclipse.winery.repository.datatypes.ids.elements.VisualAppearanceId;
import org.eclipse.winery.repository.resources.AbstractComponentsResource;
import org.eclipse.winery.repository.resources.IHasTypeReference;
import org.eclipse.winery.repository.resources._support.IPersistable;
import org.eclipse.winery.repository.resources.admin.NamespacesResource;
import org.eclipse.winery.repository.resources.entitytypeimplementations.nodetypeimplementations.NodeTypeImplementationResource;
import org.eclipse.winery.repository.resources.entitytypes.TopologyGraphElementEntityTypeResource;
import org.eclipse.winery.repository.resources.imports.xsdimports.XSDImportsResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.ls.LSInput;

import com.sun.jersey.core.header.ContentDisposition;

/**
 * Contains generic utility functions for the Backend
 * 
 * Contains everything that is useful for our ids etc. Does <em>not</em> contain
 * anything that has to do with resources
 */
public class BackendUtils {
	
	private static final Logger logger = LoggerFactory.getLogger(BackendUtils.class);
	
	
	/**
	 * Deletes given file/dir and returns appropriate response code
	 */
	public static Response delete(GenericId id) {
		if (!Repository.INSTANCE.exists(id)) {
			return Response.status(Status.NOT_FOUND).build();
		}
		try {
			Repository.INSTANCE.forceDelete(id);
		} catch (IOException e) {
			BackendUtils.logger.error(e.getMessage(), e);
			return Response.serverError().entity(e.getMessage()).build();
		}
		return Response.noContent().build();
	}
	
	/**
	 * Deletes given file and returns appropriate response code
	 */
	public static Response delete(RepositoryFileReference ref) {
		if (!Repository.INSTANCE.exists(ref)) {
			return Response.status(Status.NOT_FOUND).build();
		}
		try {
			Repository.INSTANCE.forceDelete(ref);
		} catch (IOException e) {
			BackendUtils.logger.error(e.getMessage(), e);
			return Response.serverError().entity(e.getMessage()).build();
		}
		return Response.ok().build();
	}
	
	/**
	 * Generates given TOSCA element and returns appropriate response code <br  />
	 * 
	 * In the case of an existing resource, the other possible return code is
	 * 302. This code has no Status constant, therefore we use Status.CONFLICT,
	 * which is also possible.
	 * 
	 * @return <ul>
	 *         <li>
	 *         <ul>
	 *         <li>Status.CREATED (201) if the resource has been created,</li>
	 *         <li>Status.CONFLICT if the resource already exists,</li>
	 *         <li>Status.INTERNAL_SERVER_ERROR (500) if something went wrong</li>
	 *         </ul>
	 *         </li>
	 *         <li>URI: the absolute URI of the newly created resource</li>
	 *         </ul>
	 */
	public static ResourceCreationResult create(GenericId id) {
		ResourceCreationResult res = new ResourceCreationResult();
		if (Repository.INSTANCE.exists(id)) {
			// res.setStatus(302);
			res.setStatus(Status.CONFLICT);
		} else {
			if (Repository.INSTANCE.flagAsExisting(id)) {
				res.setStatus(Status.CREATED);
				// @formatter:off
				// This method is a generic method
				// We cannot return an "absolute" URL as the URL is always
				// relative to the caller
				// Does not work: String path = Prefs.INSTANCE.getResourcePath()
				// + "/" +
				// Utils.getURLforPathInsideRepo(id.getPathInsideRepo());
				// We distinguish between two cases: TOSCAcomponentId and
				// TOSCAelementId
				// @formatter:on
				String path;
				if (id instanceof TOSCAComponentId) {
					// here, we return namespace + id, as it is only possible to
					// post on the TOSCA component*s* resource to create an
					// instance of a TOSCA component
					TOSCAComponentId tcId = (TOSCAComponentId) id;
					path = tcId.getNamespace().getEncoded() + "/" + tcId.getXmlId().getEncoded() + "/";
				} else {
					assert (id instanceof TOSCAElementId);
					// We just return the id as we assume that only the parent
					// of this id may create sub elements
					path = id.getXmlId().getEncoded() + "/";
				}
				// we have to encode it twice to get correct URIs
				path = Utils.getURLforPathInsideRepo(path);
				URI uri = Utils.createURI(path);
				res.setUri(uri);
				res.setId(id);
			} else {
				res.setStatus(Status.INTERNAL_SERVER_ERROR);
			}
		}
		return res;
	}
	
	/**
	 * 
	 * Sends the file if modified and "not modified" if not modified future work
	 * may put each file with a unique id in a separate folder in tomcat * use
	 * that static URL for each file * if file is modified, URL of file changes
	 * * -> client always fetches correct file
	 * 
	 * additionally "Vary: Accept" header is added (enables caching of the
	 * response)
	 * 
	 * method header for calling method public <br />
	 * <code>Response getXY(@HeaderParam("If-Modified-Since") String modified) {...}</code>
	 * 
	 * 
	 * @param ref references the file to be send
	 * @param modified - HeaderField "If-Modified-Since" - may be "null"
	 * @return Response to be sent to the client
	 */
	public static Response returnRepoPath(RepositoryFileReference ref, String modified) {
		return BackendUtils.returnRefAsResponseBuilder(ref, modified).build();
	}
	
	/**
	 * @return true if given fileDate is newer then the modified date (or
	 *         modified is null)
	 */
	public static boolean isFileNewerThanModifiedDate(long millis, String modified) {
		if (modified == null) {
			return true;
		}
		
		Date modifiedDate = null;
		
		assert (Locale.getDefault() == Locale.ENGLISH);
		try {
			modifiedDate = DateUtils.parseDate(modified, org.apache.http.impl.cookie.DateUtils.DEFAULT_PATTERNS);
		} catch (ParseException e) {
			BackendUtils.logger.error(e.getMessage(), e);
		}
		
		if (modifiedDate != null) {
			// modifiedDate does not carry milliseconds, but fileDate does
			// therefore we have to do a range-based comparison
			if ((millis - modifiedDate.getTime()) < DateUtils.MILLIS_PER_SECOND) {
				return false;
			}
		}
		
		return true;
	}
	
	/**
	 * This is not repository specific, but we leave it close to the only caller
	 * 
	 * If the passed ref is newer than the modified date (or the modified date
	 * is null), an OK response with an inputstream pointing to the path is
	 * returned
	 */
	private static ResponseBuilder returnRefAsResponseBuilder(RepositoryFileReference ref, String modified) {
		if (!Repository.INSTANCE.exists(ref)) {
			return Response.status(Status.NOT_FOUND);
		}
		
		FileTime lastModified;
		try {
			lastModified = Repository.INSTANCE.getLastModifiedTime(ref);
		} catch (IOException e1) {
			BackendUtils.logger.debug("Could not get lastModifiedTime", e1);
			return Response.serverError();
		}
		
		// do we really need to send the file or can send "not modified"?
		if (!BackendUtils.isFileNewerThanModifiedDate(lastModified.toMillis(), modified)) {
			return Response.status(Status.NOT_MODIFIED);
		}
		
		ResponseBuilder res;
		try {
			res = Response.ok(Repository.INSTANCE.newInputStream(ref));
		} catch (IOException e) {
			BackendUtils.logger.debug("Could not open input stream", e);
			return Response.serverError();
		}
		res = res.lastModified(new Date(lastModified.toMillis()));
		// vary:accept header is always set to be safe
		res = res.header(HttpHeaders.VARY, HttpHeaders.ACCEPT);
		// determine and set MIME content type
		try {
			res = res.header(HttpHeaders.CONTENT_TYPE, Repository.INSTANCE.getMimeType(ref));
		} catch (IOException e) {
			BackendUtils.logger.debug("Could not determine mime type", e);
			return Response.serverError();
		}
		// set filename
		ContentDisposition contentDisposition = ContentDisposition.type("attachment").fileName(ref.getFileName()).modificationDate(new Date(lastModified.toMillis())).build();
		res.header("Content-Disposition", contentDisposition);
		return res;
	}
	
	/**
	 * Updates the given property in the given configuration. Currently always
	 * returns "no content", because the underlying class does not report any
	 * errors during updating. <br />
	 * 
	 * If null or "" is passed as value, the property is cleared
	 * 
	 * @return Status.NO_CONTENT
	 */
	public static Response updateProperty(Configuration configuration, String property, String val) {
		if (StringUtils.isBlank(val)) {
			configuration.clearProperty(property);
		} else {
			configuration.setProperty(property, val);
		}
		return Response.noContent().build();
	}
	
	/**
	 * Persists the resource and returns appropriate response
	 */
	public static Response persist(IPersistable res) {
		Response r;
		try {
			res.persist();
		} catch (IOException e) {
			BackendUtils.logger.debug("Could not persist resource", e);
			r = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e).build();
			return r;
		}
		r = Response.noContent().build();
		return r;
	}
	
	/**
	 * Writes data to file. Replaces the file's content with the given content.
	 * The file does not need to exist
	 * 
	 * @param ref Reference to the File to write to (overwrite)
	 * @param content the data to write
	 * @return a JAX-RS Response containing the result. NOCONTENT if successful,
	 *         InternalSeverError otherwise
	 */
	public static Response putContentToFile(RepositoryFileReference ref, String content, MediaType mediaType) {
		try {
			Repository.INSTANCE.putContentToFile(ref, content, mediaType);
		} catch (IOException e) {
			BackendUtils.logger.error(e.getMessage(), e);
			return Response.serverError().entity(e.getMessage()).build();
		}
		return Response.noContent().build();
	}
	
	public static Response putContentToFile(RepositoryFileReference ref, InputStream inputStream, MediaType mediaType) {
		try {
			Repository.INSTANCE.putContentToFile(ref, inputStream, mediaType);
		} catch (IOException e) {
			BackendUtils.logger.error(e.getMessage(), e);
			return Response.serverError().entity(e.getMessage()).build();
		}
		return Response.noContent().build();
	}
	
	public static <T extends TOSCAComponentId> T getTOSCAcomponentId(Class<T> idClass, String qnameStr) {
		QName qname = QName.valueOf(qnameStr);
		return BackendUtils.getTOSCAcomponentId(idClass, qname.getNamespaceURI(), qname.getLocalPart(), false);
	}
	
	public static <T extends TOSCAComponentId> T getTOSCAcomponentId(Class<T> idClass, QName qname) {
		// we got two implementation possibilities: one is to directly use the
		// QName constructor,
		// the other is to use a namespace, localname, urlencoded constructor
		// we opt for the latter one, which forces the latter constructor to
		// exist at all ids
		return BackendUtils.getTOSCAcomponentId(idClass, qname.getNamespaceURI(), qname.getLocalPart(), false);
	}
	
	public static <T extends TOSCAComponentId> T getTOSCAcomponentId(Class<T> idClass, String namespace, String id, boolean URLencoded) {
		Constructor<T> constructor;
		try {
			constructor = idClass.getConstructor(String.class, String.class, boolean.class);
		} catch (NoSuchMethodException | SecurityException e) {
			BackendUtils.logger.error("Could not get constructor for id " + idClass.getName(), e);
			throw new IllegalStateException(e);
		}
		T tcId;
		try {
			tcId = constructor.newInstance(namespace, id, URLencoded);
		} catch (InstantiationException | IllegalAccessException
				| IllegalArgumentException | InvocationTargetException e) {
			BackendUtils.logger.error("Could not create id instance", e);
			throw new IllegalStateException(e);
		}
		return tcId;
	}
	
	/**
	 * @param id the id to determine the namespace of the parent for
	 * @return the namespace of the first TOSCAcomponentId found in the ID
	 *         hierarchy
	 */
	public static Namespace getNamespace(TOSCAElementId id) {
		GenericId parent = id.getParent();
		while (!(parent instanceof TOSCAComponentId)) {
			parent = parent.getParent();
		}
		return ((TOSCAComponentId) parent).getNamespace();
	}
	
	public static String getName(TOSCAComponentId instanceId) {
		// TODO: Here is a performance issue as we don't use caching or a database
		// Bad, but without performance loss: Use "text = instanceId.getXmlId().getDecoded();"
		TExtensibleElements instanceElement = AbstractComponentsResource.getComponentInstaceResource(instanceId).getElement();
		return ModelUtilities.getNameWithIdFallBack(instanceElement);
	}
	
/**
	 * Do <em>not</em> use this for creating URLs. Use
	 *
	 * {@link org.eclipse.winery.repository.Utils.getURLforPathInsideRepo(String)}
	 *
	 * or
	 *
	 * {@link org.eclipse.winery.repository.Utils.getAbsoluteURL(GenericId)
	 * instead.
	 *
	 * @return the path starting from the root element to the current element.
	 *         Separated by "/", URLencoded, but <b>not</b> double encoded. With
	 *         trailing slash if sub-resources can exist
	 * @throws IllegalStateException if id is of an unknown subclass of id
	 */
	public static String getPathInsideRepo(GenericId id) {
		if (id == null) {
			throw new NullPointerException("id is null");
		}
		
		// for creating paths see also org.eclipse.winery.repository.Utils.getIntermediateLocationStringForType(String, String)
		// and org.eclipse.winery.common.Util.getRootPathFragment(Class<? extends TOSCAcomponentId>)
		if (id instanceof AdminId) {
			return "admin/" + id.getXmlId().getEncoded() + "/";
		} else if (id instanceof GenericImportId) {
			GenericImportId i = (GenericImportId) id;
			String res = "imports/";
			res = res + Util.URLencode(i.getType()) + "/";
			res = res + i.getNamespace().getEncoded() + "/";
			res = res + i.getXmlId().getEncoded() + "/";
			return res;
		} else if (id instanceof TOSCAComponentId) {
			return IdUtil.getPathFragment(id);
		} else if (id instanceof TOSCAElementId) {
			// we cannot reuse IdUtil.getPathFragment(id) as this TOSCAelementId
			// might be nested in an AdminId
			return BackendUtils.getPathInsideRepo(id.getParent()) + id.getXmlId().getEncoded() + "/";
		} else {
			throw new IllegalStateException("Unknown subclass of GenericId " + id.getClass());
		}
	}
	
/**
	 * Do <em>not</em> use this for creating URLs. Use
	 *
	 * {@link org.eclipse.winery.repository.Utils.getURLforPathInsideRepo(String)}
	 *
	 * or
	 *
	 * {@link org.eclipse.winery.repository.Utils.getAbsoluteURL(GenericId)
	 * instead.
	 *
	 * @return the path starting from the root element to the current element.
	 *         Separated by "/", parent URLencoded. Without trailing slash.
	 */
	public static String getPathInsideRepo(RepositoryFileReference ref) {
		return BackendUtils.getPathInsideRepo(ref.getParent()) + ref.getFileName();
	}
	
	/**
	 * Returns the reference to the definitions XML storing the TOSCA for the
	 * given id
	 * 
	 * @param id the id to lookup
	 * @return the reference
	 */
	public static RepositoryFileReference getRefOfDefinitions(TOSCAComponentId id) {
		String name = Util.getTypeForComponentId(id.getClass());
		name = name + Constants.SUFFIX_TOSCA_DEFINITIONS;
		RepositoryFileReference ref = new RepositoryFileReference(id, name);
		return ref;
	}
	
	/**
	 * Returns the reference to the properties file storing the TOSCA
	 * information for the given id
	 * 
	 * @param id the id to lookup
	 * @return the reference
	 */
	public static RepositoryFileReference getRefOfConfiguration(GenericId id) {
		String name;
		// Hack to determine file name
		if (id instanceof TOSCAComponentId) {
			name = Util.getTypeForComponentId(((TOSCAComponentId) id).getClass());
			name = name + Constants.SUFFIX_PROPERTIES;
		} else if (id instanceof AdminId) {
			name = Utils.getTypeForAdminId(((AdminId) id).getClass());
			name = name + Constants.SUFFIX_PROPERTIES;
		} else {
			assert (id instanceof TOSCAElementId);
			TOSCAElementId tId = (TOSCAElementId) id;
			if (tId instanceof PlansId) {
				name = Filename.FILENAME_PROPERTIES_PLANCONTAINER;
			} else if (tId instanceof VisualAppearanceId) {
				// quick hack for special name here
				name = Filename.FILENAME_PROPERTIES_VISUALAPPEARANCE;
			} else {
				name = Util.getTypeForElementId(tId.getClass()) + Constants.SUFFIX_PROPERTIES;
			}
		}
		
		RepositoryFileReference ref = new RepositoryFileReference(id, name);
		return ref;
	}
	
	/**
	 * @param qNameOfTheType the QName of the type, where all TOSCAComponentIds,
	 *            where the associated element points to the type
	 * @param clazz the Id class of the entities to discover
	 */
	public static <X extends TOSCAComponentId> Collection<X> getAllElementsRelatedWithATypeAttribute(Class<X> clazz, QName qNameOfTheType) {
		// we do not use any database system,
		// therefore we have to crawl through each node type implementation by ourselves
		SortedSet<X> allIds = Repository.INSTANCE.getAllTOSCAComponentIds(clazz);
		Collection<X> res = new HashSet<>();
		for (X id : allIds) {
			IHasTypeReference resource;
			try {
				resource = (IHasTypeReference) AbstractComponentsResource.getComponentInstaceResource(id);
			} catch (ClassCastException e) {
				String error = "Requested following the type, but the component instance does not implmenet IHasTypeReference";
				BackendUtils.logger.error(error);
				throw new IllegalStateException(error);
			}
			// The resource may have been freshly initialized due to existence of a directory
			// then it has no node type assigned leading to ntiRes.getType() being null
			// we ignore this error here
			if (qNameOfTheType.equals(resource.getType())) {
				// the component instance is an implementation of the associated node type
				res.add(id);
			}
		}
		return res;
	}
	
	/**
	 * Returns a list of the topology template nested in the given service
	 * template
	 */
	public static List<TNodeTemplate> getAllNestedNodeTemplates(TServiceTemplate serviceTemplate) {
		List<TNodeTemplate> l = new ArrayList<TNodeTemplate>();
		TTopologyTemplate topologyTemplate = serviceTemplate.getTopologyTemplate();
		if (topologyTemplate == null) {
			return Collections.emptyList();
		}
		for (TEntityTemplate t : topologyTemplate.getNodeTemplateOrRelationshipTemplate()) {
			if (t instanceof TNodeTemplate) {
				l.add((TNodeTemplate) t);
			}
		}
		return l;
	}
	
	private static Collection<QName> getAllReferencedArtifactTemplates(TDeploymentArtifacts tDeploymentArtifacts) {
		if (tDeploymentArtifacts == null) {
			return Collections.emptyList();
		}
		List<TDeploymentArtifact> deploymentArtifacts = tDeploymentArtifacts.getDeploymentArtifact();
		if (deploymentArtifacts == null) {
			return Collections.emptyList();
		}
		Collection<QName> res = new ArrayList<>();
		for (TDeploymentArtifact da : deploymentArtifacts) {
			QName artifactRef = da.getArtifactRef();
			if (artifactRef != null) {
				res.add(artifactRef);
			}
		}
		return res;
	}
	
	private static Collection<QName> getAllReferencedArtifactTemplates(TImplementationArtifacts tImplementationArtifacts) {
		if (tImplementationArtifacts == null) {
			return Collections.emptyList();
		}
		List<ImplementationArtifact> implementationArtifacts = tImplementationArtifacts.getImplementationArtifact();
		if (implementationArtifacts == null) {
			return Collections.emptyList();
		}
		Collection<QName> res = new ArrayList<>();
		for (ImplementationArtifact ia : implementationArtifacts) {
			QName artifactRef = ia.getArtifactRef();
			if (artifactRef != null) {
				res.add(artifactRef);
			}
		}
		return res;
	}
	
	public static Collection<QName> getArtifactTemplatesOfReferencedDeploymentArtifacts(TNodeTemplate nodeTemplate) {
		List<QName> l = new ArrayList<QName>();
		
		// DAs may be assigned directly to a node template
		Collection<QName> allReferencedArtifactTemplates = BackendUtils.getAllReferencedArtifactTemplates(nodeTemplate.getDeploymentArtifacts());
		l.addAll(allReferencedArtifactTemplates);
		
		// DAs may be assigned via node type implementations
		QName nodeTypeQName = nodeTemplate.getType();
		Collection<NodeTypeImplementationId> allNodeTypeImplementations = BackendUtils.getAllElementsRelatedWithATypeAttribute(NodeTypeImplementationId.class, nodeTypeQName);
		for (NodeTypeImplementationId nodeTypeImplementationId : allNodeTypeImplementations) {
			NodeTypeImplementationResource ntiRes = new NodeTypeImplementationResource(nodeTypeImplementationId);
			allReferencedArtifactTemplates = BackendUtils.getAllReferencedArtifactTemplates(ntiRes.getNTI().getDeploymentArtifacts());
			l.addAll(allReferencedArtifactTemplates);
		}
		
		return l;
	}
	
	public static Collection<QName> getArtifactTemplatesOfReferencedImplementationArtifacts(TNodeTemplate nodeTemplate) {
		List<QName> l = new ArrayList<QName>();
		
		// IAs may be assigned via node type implementations
		QName nodeTypeQName = nodeTemplate.getType();
		Collection<NodeTypeImplementationId> allNodeTypeImplementations = BackendUtils.getAllElementsRelatedWithATypeAttribute(NodeTypeImplementationId.class, nodeTypeQName);
		for (NodeTypeImplementationId nodeTypeImplementationId : allNodeTypeImplementations) {
			NodeTypeImplementationResource ntiRes = new NodeTypeImplementationResource(nodeTypeImplementationId);
			Collection<QName> allReferencedArtifactTemplates = BackendUtils.getAllReferencedArtifactTemplates(ntiRes.getNTI().getImplementationArtifacts());
			l.addAll(allReferencedArtifactTemplates);
		}
		
		return l;
	}
	
	/**
	 * Creates a new TDefintions element wrapping a TOSCA Component instance.
	 * The namespace of the tosca component is used as namespace and
	 * {@code winery-defs-for-} concatenated with the (unique) ns prefix and
	 * idOfContainedElement is used as id
	 * 
	 * @param toscAcomponentId the id of the element the wrapper is used for
	 * 
	 * @return a definitions element prepared for wrapping a TOSCA component
	 *         instance
	 */
	public static Definitions createWrapperDefinitions(TOSCAComponentId tcId) {
		ObjectFactory of = new ObjectFactory();
		Definitions defs = of.createDefinitions();
		
		// set target namespace
		// an internal namespace is not possible
		//   a) tPolicyTemplate and tArtfactTemplate do NOT support the "targetNamespace" attribute
		//   b) the imports statement would look bad as it always imported the artificial namespace
		defs.setTargetNamespace(tcId.getNamespace().getDecoded());
		
		// set a unique id to create a valid definitions element
		// we do not use UUID to be more human readable and deterministic (for debugging)
		String prefix = NamespacesResource.getPrefix(tcId.getNamespace());
		String elId = tcId.getXmlId().getDecoded();
		String id = "winery-defs-for_" + prefix + "-" + elId;
		defs.setId(id);
		
		return defs;
	}
	
	/**
	 * @throws IOException if content could not be updated in the repository
	 * @throws IllegalStateException if an JAXBException occurred. This should
	 *             never happen.
	 */
	public static void persist(Object o, RepositoryFileReference ref, MediaType mediaType) throws IOException {
		// We assume that the object is not too large
		// Otherwise, http://io-tools.googlecode.com/svn/www/easystream/apidocs/index.html should be used
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		Marshaller m;
		try {
			m = JAXBSupport.createMarshaller(true);
			m.marshal(o, out);
		} catch (JAXBException e) {
			BackendUtils.logger.error("Could not put content to file", e);
			throw new IllegalStateException(e);
		}
		byte[] data = out.toByteArray();
		ByteArrayInputStream in = new ByteArrayInputStream(data);
		// this may throw an IOExcpetion. We propagate this exception.
		Repository.INSTANCE.putContentToFile(ref, in, mediaType);
	}
	
	/**
	 * Updates the color if the color is not yet existent
	 * 
	 * @param name the name of the component. Used as basis for a generated
	 *            color
	 * @param qname the QName of the color attribute
	 * @param otherAttributes the plain "XML" attributes. They are used to check
	 * @param res
	 */
	public static String getColorAndSetDefaultIfNotExisting(String name, QName qname, Map<QName, String> otherAttributes, TopologyGraphElementEntityTypeResource res) {
		String colorStr = otherAttributes.get(qname);
		if (colorStr == null) {
			colorStr = Util.getColor(name);
			otherAttributes.put(qname, colorStr);
			BackendUtils.persist(res);
		}
		return colorStr;
	}
	
	/**
	 * 
	 * @param tcId The element type id to get the location for
	 * @param uri uri to use if in XML export mode, null if in CSAR export mode
	 * @param wrapperElementLocalName the local name of the wrapper element
	 * @return
	 */
	public static String getImportLocationForWinerysPropertiesDefinitionXSD(EntityTypeId tcId, URI uri, String wrapperElementLocalName) {
		String loc = BackendUtils.getPathInsideRepo(tcId);
		loc = loc + "propertiesdefinition/";
		loc = Utils.getURLforPathInsideRepo(loc);
		if (uri == null) {
			loc = loc + wrapperElementLocalName + ".xsd";
			// for the import later, we need "../" in front
			loc = "../" + loc;
		} else {
			loc = uri + loc + "xsd";
		}
		return loc;
	}
	
	/**
	 * @param ref the file to read from
	 */
	public static XSModel getXSModel(final RepositoryFileReference ref) {
		if (ref == null) {
			return null;
		}
		final InputStream is;
		try {
			is = Repository.INSTANCE.newInputStream(ref);
		} catch (IOException e) {
			BackendUtils.logger.debug("Could not create input stream", e);
			return null;
		}
		
		// we rely on xerces to parse the XSD
		// idea based on http://stackoverflow.com/a/5165177/873282
		XSImplementation impl = new XSImplementationImpl();
		XSLoader schemaLoader = impl.createXSLoader(null);
		
		// minimal LSInput implementation sufficient for XSLoader in Oracle's JRE7
		LSInput input = new LSInput() {
			
			@Override
			public void setSystemId(String systemId) {
			}
			
			@Override
			public void setStringData(String stringData) {
			}
			
			@Override
			public void setPublicId(String publicId) {
			}
			
			@Override
			public void setEncoding(String encoding) {
			}
			
			@Override
			public void setCharacterStream(Reader characterStream) {
			}
			
			@Override
			public void setCertifiedText(boolean certifiedText) {
			}
			
			@Override
			public void setByteStream(InputStream byteStream) {
			}
			
			@Override
			public void setBaseURI(String baseURI) {
			}
			
			@Override
			public String getSystemId() {
				return null;
			}
			
			@Override
			public String getStringData() {
				return null;
			}
			
			@Override
			public String getPublicId() {
				return BackendUtils.getPathInsideRepo(ref);
			}
			
			@Override
			public String getEncoding() {
				return "UTF-8";
			}
			
			@Override
			public Reader getCharacterStream() {
				try {
					return new InputStreamReader(is, "UTF-8");
				} catch (UnsupportedEncodingException e) {
					System.out.println("exeption");
					throw new IllegalStateException("UTF-8 is unkown", e);
				}
			}
			
			@Override
			public boolean getCertifiedText() {
				return false;
			}
			
			@Override
			public InputStream getByteStream() {
				return null;
			}
			
			@Override
			public String getBaseURI() {
				return null;
			}
		};
		XSModel model = schemaLoader.load(input);
		return model;
	}
	
	/**
	 * Derives Winery's Properties Definition from an existing properties
	 * definition
	 * 
	 * @param ci the entity type to try to modify the WPDs
	 * @param errors the list to add errors to
	 */
	public static void deriveWPD(TEntityType ci, List<String> errors) {
		BackendUtils.logger.trace("deriveWPD");
		PropertiesDefinition propertiesDefinition = ci.getPropertiesDefinition();
		QName element = propertiesDefinition.getElement();
		if (element == null) {
			BackendUtils.logger.debug("only works for an element definition, not for types");
		} else {
			BackendUtils.logger.debug("Looking for the definition of {" + element.getNamespaceURI() + "}" + element.getLocalPart());
			// fetch the XSD defining the element
			XSDImportsResource importsRes = new XSDImportsResource();
			Map<String, RepositoryFileReference> mapFromLocalNameToXSD = importsRes.getMapFromLocalNameToXSD(element.getNamespaceURI(), false);
			RepositoryFileReference ref = mapFromLocalNameToXSD.get(element.getLocalPart());
			if (ref == null) {
				String msg = "XSD not found for " + element.getNamespaceURI() + " / " + element.getLocalPart();
				BackendUtils.logger.debug(msg);
				errors.add(msg);
				return;
			}
			
			XSModel xsModel = BackendUtils.getXSModel(ref);
			XSElementDeclaration elementDeclaration = xsModel.getElementDeclaration(element.getLocalPart(), element.getNamespaceURI());
			if (elementDeclaration == null) {
				String msg = "XSD model claimed to contain declaration for {" + element.getNamespaceURI() + "}" + element.getLocalPart() + ", but it did not.";
				BackendUtils.logger.debug(msg);
				errors.add(msg);
				return;
			}
			
			// go through the XSD definition and
			XSTypeDefinition typeDefinition = elementDeclaration.getTypeDefinition();
			if (typeDefinition instanceof XSComplexTypeDefinition) {
				XSComplexTypeDefinition cTypeDefinition = (XSComplexTypeDefinition) typeDefinition;
				XSParticle particle = cTypeDefinition.getParticle();
				if (particle == null) {
					BackendUtils.logger.debug("XSD does not follow the requirements put by winery: Complex type does not contain particles");
				} else {
					XSTerm term = particle.getTerm();
					if (term instanceof XSModelGroup) {
						XSModelGroup modelGroup = (XSModelGroup) term;
						if (modelGroup.getCompositor() == XSModelGroup.COMPOSITOR_SEQUENCE) {
							XSObjectList particles = modelGroup.getParticles();
							int len = particles.getLength();
							boolean everyThingIsASimpleType = true;
							PropertyDefinitionKVList list = new PropertyDefinitionKVList();
							if (len != 0) {
								for (int i = 0; i < len; i++) {
									XSParticle innerParticle = (XSParticle) particles.item(i);
									XSTerm innerTerm = innerParticle.getTerm();
									if (innerTerm instanceof XSElementDeclaration) {
										XSElementDeclaration innerElementDeclaration = (XSElementDeclaration) innerTerm;
										String name = innerElementDeclaration.getName();
										XSTypeDefinition innerTypeDefinition = innerElementDeclaration.getTypeDefinition();
										if (innerTypeDefinition instanceof XSSimpleType) {
											XSSimpleType xsSimpleType = (XSSimpleType) innerTypeDefinition;
											String typeNS = xsSimpleType.getNamespace();
											String typeName = xsSimpleType.getName();
											if (typeNS.equals(XMLConstants.W3C_XML_SCHEMA_NS_URI)) {
												PropertyDefinitionKV def = new PropertyDefinitionKV();
												def.setKey(name);
												// convention at WPD: use "xsd" as prefix for XML Schema Definition
												def.setType("xsd:" + typeName);
												list.add(def);
											} else {
												everyThingIsASimpleType = false;
												break;
											}
										} else {
											everyThingIsASimpleType = false;
											break;
										}
									} else {
										everyThingIsASimpleType = false;
										break;
									}
								}
							}
							if (everyThingIsASimpleType) {
								// everything went allright, we can add a WPD
								WinerysPropertiesDefinition wpd = new WinerysPropertiesDefinition();
								wpd.setIsDerivedFromXSD(Boolean.TRUE);
								wpd.setElementName(element.getLocalPart());
								wpd.setNamespace(element.getNamespaceURI());
								wpd.setPropertyDefinitionKVList(list);
								ModelUtilities.replaceWinerysPropertiesDefinition(ci, wpd);
								BackendUtils.logger.debug("Successfully generated WPD");
							} else {
								BackendUtils.logger.debug("XSD does not follow the requirements put by winery: Not all types in the sequence are simple types");
							}
						} else {
							BackendUtils.logger.debug("XSD does not follow the requirements put by winery: Model group is not a sequence");
						}
					} else {
						BackendUtils.logger.debug("XSD does not follow the requirements put by winery: Not a model group");
					}
				}
			} else {
				BackendUtils.logger.debug("XSD does not follow the requirements put by winery: No Complex Type Definition");
			}
		}
	}
	
	/**
	 * Returns all components available of the given id type
	 * 
	 * Similar functionality as {@link
	 * org.eclipse.winery.repository.backend.IGenericRepository.
	 * getAllTOSCAComponentIds(Class<T>)}, but it crawls through the repository
	 * 
	 * This method is required as we do not use a database.
	 * 
	 * @param idClass class of the Ids to search for
	 * @return empty set if no ids are available
	 */
	public <T extends TOSCAElementId> SortedSet<T> getAllTOSCAElementIds(Class<T> idClass) {
		throw new IllegalStateException("Not yet implemented");
		
		/*
		 Implementation idea:
		   * switch of instance of idClass
		   * nodetemplate / relationshiptemplate -> fetch all service templates -> crawl through topology -> add all to res
		   * req/cap do as above, but inspect nodetemplate
		   * (other special handlings; check spec where each type can be linked from)
		 */
	}
	
	/**
	 * Converts the given collection of TOSCA Component Ids to a collection of
	 * QNames by using the getQName() method.
	 * 
	 * This is required for QNameChooser.tag
	 */
	public static Collection<QName> convertTOSCAComponentIdCollectionToQNameCollection(Collection<? extends TOSCAComponentId> col) {
		Collection<QName> res = new ArrayList<>();
		for (TOSCAComponentId id : col) {
			res.add(id.getQName());
		}
		return res;
	}
	
}