1/* valaversionattribute.vala
2 *
3 * Copyright (C) 2013  Florian Brosch
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
18 *
19 * Author:
20 * 	Florian Brosch <flo.brosch@gmail.com>
21 */
22
23using GLib;
24
25
26/**
27 * Represents a [Version] attribute
28 */
29public class Vala.VersionAttribute {
30	private weak Symbol symbol;
31
32	private bool? _deprecated;
33	private bool? _experimental;
34
35	/**
36	 * Constructs a new VersionAttribute.
37	 *
38	 * @param symbol the owner
39	 * @return a new VersionAttribute
40	 * @see Vala.Symbol
41	 */
42	public VersionAttribute (Symbol symbol) {
43		this.symbol = symbol;
44	}
45
46
47
48	/**
49	 * Specifies whether this symbol has been deprecated.
50	 */
51	public bool deprecated {
52		get {
53			if (_deprecated == null) {
54				_deprecated = symbol.get_attribute_bool ("Version", "deprecated", false)
55					|| symbol.get_attribute_string ("Version", "deprecated_since") != null
56					|| symbol.get_attribute_string ("Version", "replacement") != null
57					// [Deprecated] is deprecated
58					|| symbol.get_attribute ("Deprecated") != null;
59			}
60			return _deprecated;
61		}
62		set {
63			_deprecated = value;
64			symbol.set_attribute_bool ("Version", "deprecated", _deprecated);
65		}
66	}
67
68	/**
69	 * Specifies what version this symbol has been deprecated since.
70	 */
71	public string? deprecated_since {
72		owned get {
73			return symbol.get_attribute_string ("Version", "deprecated_since")
74				// [Deprecated] is deprecated
75				?? symbol.get_attribute_string ("Deprecated", "since");
76		}
77		set {
78			symbol.set_attribute_string ("Version", "deprecated_since", value);
79		}
80	}
81
82	/**
83	 * Specifies the replacement if this symbol has been deprecated.
84	 */
85	public string? replacement {
86		owned get {
87			return symbol.get_attribute_string ("Version", "replacement")
88				// [Deprecated] is deprecated
89				?? symbol.get_attribute_string ("Deprecated", "replacement");
90		}
91		set {
92			symbol.set_attribute_string ("Version", "replacement", value);
93		}
94	}
95
96
97
98	/**
99	 * Specifies whether this symbol is experimental.
100	 */
101	public bool experimental {
102		get {
103			if (_experimental == null) {
104				_experimental = symbol.get_attribute_bool ("Version", "experimental", false)
105					|| symbol.get_attribute_string ("Version", "experimental_until") != null
106					|| symbol.get_attribute ("Experimental") != null;
107			}
108			return _experimental;
109		}
110		set {
111			_experimental = value;
112			symbol.set_attribute_bool ("Version", "experimental", value);
113		}
114	}
115
116	/**
117	 * Specifies until which version this symbol is experimental.
118	 */
119	public string? experimental_until {
120		owned get {
121			return symbol.get_attribute_string ("Version", "experimental_until");
122		}
123		set {
124			symbol.set_attribute_string ("Version", "experimental_until", value);
125		}
126	}
127
128
129
130	/**
131	 * The minimum version for {@link Vala.VersionAttribute.symbol}
132	 */
133	public string? since {
134		owned get {
135			return symbol.get_attribute_string ("Version", "since");
136		}
137		set {
138			symbol.set_attribute_string ("Version", "since", value);
139		}
140	}
141
142
143
144	/**
145	 * Check to see if the symbol is experimental, deprecated or not available
146	 * and emit a warning if it is.
147	 */
148	public bool check (SourceReference? source_ref = null) {
149		bool result = false;
150
151		// deprecation:
152		if (symbol.external_package && deprecated) {
153			string? package_version = symbol.source_reference.file.installed_version;
154
155			if (!CodeContext.get ().deprecated && (package_version == null || deprecated_since == null || VersionAttribute.cmp_versions (package_version, deprecated_since) >= 0)) {
156				Report.deprecated (source_ref, "`%s' %s%s".printf (symbol.get_full_name (), (deprecated_since == null) ? "is deprecated" : "has been deprecated since %s".printf (deprecated_since), (replacement == null) ? "" : ". Use %s".printf (replacement)));
157			}
158			result = true;
159		}
160
161		// availability:
162		if (symbol.external_package && since != null) {
163			string? package_version = symbol.source_reference.file.installed_version;
164
165			if (CodeContext.get ().since_check && package_version != null && VersionAttribute.cmp_versions (package_version, since) < 0) {
166				unowned string filename = symbol.source_reference.file.filename;
167				string pkg = Path.get_basename (filename[0:filename.last_index_of_char ('.')]);
168				Report.error (source_ref, "`%s' is not available in %s %s. Use %s >= %s".printf (symbol.get_full_name (), pkg, package_version, pkg, since));
169			}
170			result = true;
171		}
172
173		// experimental:
174		if (symbol.external_package && experimental) {
175			if (!CodeContext.get ().experimental) {
176				string? package_version = symbol.source_reference.file.installed_version;
177				string? experimental_until = this.experimental_until;
178
179				if (experimental_until == null || package_version == null || VersionAttribute.cmp_versions (package_version, experimental_until) < 0) {
180					Report.experimental (source_ref, "`%s' is experimental%s".printf (symbol.get_full_name (), (experimental_until != null) ? " until %s".printf (experimental_until) : ""));
181				}
182			}
183			result = true;
184		}
185
186		return result;
187	}
188
189
190	/**
191	 * A simple version comparison function.
192	 *
193	 * @param v1str a version number
194	 * @param v2str a version number
195	 * @return an integer less than, equal to, or greater than zero, if v1str is <, == or > than v2str
196	 * @see GLib.CompareFunc
197	 */
198	public static int cmp_versions (string v1str, string v2str) {
199		string[] v1arr = v1str.split (".");
200		string[] v2arr = v2str.split (".");
201		int i = 0;
202
203		while (v1arr[i] != null && v2arr[i] != null) {
204			int v1num = int.parse (v1arr[i]);
205			int v2num = int.parse (v2arr[i]);
206
207			if (v1num < 0 || v2num < 0) {
208				// invalid format
209				return 0;
210			}
211
212			if (v1num > v2num) {
213				return 1;
214			}
215
216			if (v1num < v2num) {
217				return -1;
218			}
219
220			i++;
221		}
222
223		if (v1arr[i] != null && v2arr[i] == null) {
224			return 1;
225		}
226
227		if (v1arr[i] == null && v2arr[i] != null) {
228			return -1;
229		}
230
231		return 0;
232	}
233}
234