xref: /Universal-ctags/parsers/maven2.c (revision 3671ad7255885a0c8f6ff4979d80c70f201ea411)
1 /*
2  *
3  *   Copyright (c) 2016, Masatake YAMATO
4  *   Copyright (c) 2016, Red Hat, K.K.
5  *
6  *   This source code is released for free distribution under the terms of the
7  *   GNU General Public License version 2 or (at your option) any later version.
8  *
9  *   This module contains functions for generating tags for maven2 project model
10  *   defined in http://maven.apache.org/POM/4.0.0,
11  *              http://maven.apache.org/maven-v4_0_0.xsd.
12  */
13 
14 #include "general.h"	/* must always come first */
15 
16 #include "entry.h"
17 #include "parse.h"
18 #include "read.h"
19 #include "routines.h"
20 #include "selectors.h"
21 #include "xml.h"
22 
23 #include <string.h>
24 
25 typedef enum {
26 	K_GROUP_ID, K_ARTIFACT_ID, K_PROPERTY, K_REPOSITORY_ID
27 } maven2Kind;
28 
29 typedef enum {
30 	R_GROUP_ID_PARENT,
31 	R_GROUP_ID_DEPENDENCY,
32 } maven2GroupIdRole;
33 
34 typedef enum {
35 	R_ARTIFACT_ID_PARENT,
36 	R_ARTIFACT_ID_DEPENDENCY,
37 } maven2ArtifactIdRole;
38 
39 static roleDefinition Maven2GroupIdRoles [] = {
40 	{ true, "parent", "parent" },
41 	{ true, "dependency", "dependency" },
42 };
43 
44 static roleDefinition Maven2ArtifactIdRoles [] = {
45 	{ true, "parent", "parent" },
46 	{ true, "dependency", "dependency" },
47 };
48 
49 static kindDefinition Maven2Kinds [] = {
50 	{ true,  'g', "groupId",    "group identifiers",
51 	  .referenceOnly = false, ATTACH_ROLES (Maven2GroupIdRoles) },
52 	{ true,  'a', "artifactId", "artifact identifiers",
53 	  .referenceOnly = false, ATTACH_ROLES (Maven2ArtifactIdRoles) },
54 	{ true,  'p', "property",   "properties" },
55 	{ true,  'r', "repositoryId", "repository identifiers" },
56 };
57 
58 static void makeTagWithScope (xmlNode *node,
59 			      const char *xpath,
60 			      const struct sTagXpathMakeTagSpec *spec,
61 			      struct sTagEntryInfo *tag,
62 			      void *userData);
63 
64 static void makeTagRecursively (xmlNode *node,
65 			    const char *xpath,
66 				const struct sTagXpathRecurSpec *spec,
67 				xmlXPathContext *ctx,
68 				void *userData);
69 
makeTagForProperties(xmlNode * node,const char * xpath CTAGS_ATTR_UNUSED,const struct sTagXpathRecurSpec * spec CTAGS_ATTR_UNUSED,xmlXPathContext * ctx CTAGS_ATTR_UNUSED,void * userData CTAGS_ATTR_UNUSED)70 static void makeTagForProperties (xmlNode *node,
71 			      const char *xpath CTAGS_ATTR_UNUSED,
72 				  const struct sTagXpathRecurSpec *spec CTAGS_ATTR_UNUSED,
73 				  xmlXPathContext *ctx CTAGS_ATTR_UNUSED,
74 				  void *userData CTAGS_ATTR_UNUSED)
75 {
76 	const xmlChar* str;
77 	tagEntryInfo tag;
78 
79 	str = node->name;
80 	initTagEntry (&tag, (char *)str, K_PROPERTY);
81 	tag.lineNumber = xmlGetLineNo (node);
82 	tag.filePosition = getInputFilePositionForLine (tag.lineNumber);
83 
84 	makeTagEntry (&tag);
85 }
86 
87 
88 enum maven2XpathTable {
89 	TABLE_MAIN,
90 	TABLE_PARENT,
91 	TABLE_DEPEDENCY,
92 };
93 
94 static tagXpathTable maven2XpathMainTable[] = {
95 	{ "/*[local-name()='project']/*[local-name()='groupId']",
96 	  LXPATH_TABLE_DO_MAKE,
97 	  { .makeTagSpec = { K_GROUP_ID, ROLE_DEFINITION_INDEX,
98 			     makeTagWithScope } }
99 	},
100 	{ "/*[local-name()='project']/*[local-name()='parent']",
101 	  LXPATH_TABLE_DO_RECUR,
102 	  { .recurSpec = { makeTagRecursively, TABLE_PARENT } }
103 	},
104 	{ "/*[local-name()='project']/*[local-name()='dependencies']/*[local-name()='dependency']",
105 	  LXPATH_TABLE_DO_RECUR,
106 	  { .recurSpec = { makeTagRecursively, TABLE_DEPEDENCY } }
107 	},
108 	{ "/*[local-name()='project']/*[local-name()='artifactId']",
109 	  LXPATH_TABLE_DO_MAKE,
110 	  { .makeTagSpec = { K_ARTIFACT_ID, ROLE_DEFINITION_INDEX,
111 			     makeTagWithScope } }
112 	},
113 	{ "/*[local-name()='project']/*[local-name()='properties']/*",
114 	  LXPATH_TABLE_DO_RECUR,
115 	  { .recurSpec = { makeTagForProperties } }
116 	},
117 	{ "/*[local-name()='project']/*[local-name()='repositories']/*[local-name()='repository']/*[local-name()='id']",
118 	  LXPATH_TABLE_DO_MAKE,
119 	  { .makeTagSpec = { K_REPOSITORY_ID, ROLE_DEFINITION_INDEX, } }
120 	},
121 };
122 
123 static tagXpathTable maven2XpathParentTable[] = {
124 	{ "./*[local-name()='groupId']",
125 	  LXPATH_TABLE_DO_MAKE,
126 	  { .makeTagSpec = { K_GROUP_ID, R_GROUP_ID_PARENT,
127 			     makeTagWithScope } }
128 	},
129 	{ "./*[local-name()='artifactId']",
130 	  LXPATH_TABLE_DO_MAKE,
131 	  { .makeTagSpec = { K_ARTIFACT_ID, R_ARTIFACT_ID_PARENT,
132 			     makeTagWithScope } }
133 	},
134 };
135 
136 static tagXpathTable maven2XpathDependencyTable[] = {
137 	{ "./*[local-name()='groupId']",
138 	  LXPATH_TABLE_DO_MAKE,
139 	  { .makeTagSpec = { K_GROUP_ID, R_GROUP_ID_DEPENDENCY,
140 			     makeTagWithScope } }
141 	},
142 	{ "./*[local-name()='artifactId']",
143 	  LXPATH_TABLE_DO_MAKE,
144 	  { .makeTagSpec = { K_ARTIFACT_ID, R_ARTIFACT_ID_DEPENDENCY,
145 			     makeTagWithScope } }
146 	},
147 };
148 
149 static tagXpathTableTable maven2XpathTableTable[] = {
150 	[TABLE_MAIN] = { ARRAY_AND_SIZE(maven2XpathMainTable) },
151 	[TABLE_PARENT] = { ARRAY_AND_SIZE(maven2XpathParentTable) },
152 	[TABLE_DEPEDENCY] = { ARRAY_AND_SIZE(maven2XpathDependencyTable) },
153 };
154 
155 typedef enum {
156 	F_VERSION,
157 } maven2Field;
158 
159 static fieldDefinition Maven2Fields [] = {
160 	{
161 		.name = "version",
162 		.description = "version of artifact",
163 		.enabled = false,
164 	}
165 };
166 
attachVersionIfExisting(struct sTagEntryInfo * tag,xmlNode * node)167 static char* attachVersionIfExisting (struct sTagEntryInfo *tag, xmlNode *node)
168 {
169 	char *version = NULL;
170 
171 	for (node = node->next; node != NULL; node = node->next)
172 	{
173 		if (strcmp ((char *)node->name, "version") == 0)
174 		{
175 			version = (char *)xmlNodeGetContent (node);
176 			break;
177 		}
178 	}
179 	if (version)
180 		attachParserField (tag, false, Maven2Fields [F_VERSION].ftype, version);
181 	return version;
182 }
183 
makeTagWithScope(xmlNode * node,const char * xpath CTAGS_ATTR_UNUSED,const struct sTagXpathMakeTagSpec * spec,struct sTagEntryInfo * tag,void * userData)184 static void makeTagWithScope (xmlNode *node,
185 			      const char *xpath CTAGS_ATTR_UNUSED,
186 			      const struct sTagXpathMakeTagSpec *spec,
187 			      struct sTagEntryInfo *tag,
188 			      void *userData)
189 {
190 	int *corkIndexes = userData;
191 	int i;
192 	char* version = NULL;
193 
194 	if (tag->kindIndex == K_ARTIFACT_ID)
195 		version = attachVersionIfExisting (tag, node);
196 
197 	i = makeTagEntry (tag);
198 
199 	if (version)
200 		xmlFree (version);
201 
202 	if ((tag->kindIndex == K_GROUP_ID)
203 	    || (tag->kindIndex == K_ARTIFACT_ID))
204 		corkIndexes [spec->kind] = i;
205 }
206 
207 static void
findMaven2TagsForTable(enum maven2XpathTable tindex,xmlNode * node,xmlXPathContext * ctx)208 findMaven2TagsForTable (enum maven2XpathTable tindex,
209 			xmlNode *node,
210 			xmlXPathContext *ctx)
211 {
212 	int corkIndexes [] = {
213 		[K_GROUP_ID]    = CORK_NIL,
214 		[K_ARTIFACT_ID] = CORK_NIL,
215 	};
216 
217 	findXMLTags (ctx, node, tindex, &corkIndexes);
218 
219 	tagEntryInfo *tag = getEntryInCorkQueue (corkIndexes [K_ARTIFACT_ID]);
220 	if (tag && corkIndexes [K_GROUP_ID] != CORK_NIL)
221 		tag->extensionFields.scopeIndex = corkIndexes [K_GROUP_ID];
222 }
223 
makeTagRecursively(xmlNode * node,const char * xpath CTAGS_ATTR_UNUSED,const struct sTagXpathRecurSpec * spec,xmlXPathContext * ctx,void * userData CTAGS_ATTR_UNUSED)224 static void makeTagRecursively (xmlNode *node,
225 			      const char *xpath CTAGS_ATTR_UNUSED,
226 				  const struct sTagXpathRecurSpec *spec,
227 				  xmlXPathContext *ctx,
228 				  void *userData CTAGS_ATTR_UNUSED)
229 {
230 	findMaven2TagsForTable (spec->nextTable, node, ctx);
231 }
232 
233 static void
findMaven2Tags(void)234 findMaven2Tags (void)
235 {
236 	scheduleRunningBaseparser (RUN_DEFAULT_SUBPARSERS);
237 }
238 
239 static void
runXPathEngine(xmlSubparser * s,xmlXPathContext * ctx,xmlNode * root)240 runXPathEngine(xmlSubparser *s,
241 			   xmlXPathContext *ctx, xmlNode *root)
242 {
243 	findMaven2TagsForTable (TABLE_MAIN, root, ctx);
244 }
245 
246 static xmlSubparser maven2Subparser = {
247 	.subparser = {
248 		.direction = SUBPARSER_BI_DIRECTION,
249 	},
250 	.runXPathEngine = runXPathEngine,
251 };
252 
253 extern parserDefinition*
Maven2Parser(void)254 Maven2Parser (void)
255 {
256 	static const char *const extensions [] = { "pom", "xml", NULL };
257 	static const char *const patterns [] =   { "pom.xml", NULL };
258 	parserDefinition* const def = parserNew ("Maven2");
259 	static selectLanguage selectors[] = { selectByXpathFileSpec, NULL };
260 
261 	static xpathFileSpec xpathFileSpecs[] = {
262 		{
263 			.rootElementName = "project",
264 			.rootNSHref      = "http://maven.apache.org/POM/4.0.0",
265 		},
266 	};
267 
268 	static parserDependency dependencies [] = {
269 		[0] = { DEPTYPE_SUBPARSER, "XML", &maven2Subparser },
270 	};
271 
272 	def->dependencies = dependencies;
273 	def->dependencyCount = ARRAY_SIZE (dependencies);
274 
275 	def->kindTable         = Maven2Kinds;
276 	def->kindCount     = ARRAY_SIZE (Maven2Kinds);
277 	def->extensions    = extensions;
278 	def->patterns      = patterns;
279 	def->parser        = findMaven2Tags;
280 	def->tagXpathTableTable  = maven2XpathTableTable;
281 	def->tagXpathTableCount  = ARRAY_SIZE (maven2XpathTableTable);
282 	def->useCork = CORK_QUEUE;
283 	def->selectLanguage = selectors;
284 	def->fieldTable = Maven2Fields;
285 	def->fieldCount = ARRAY_SIZE (Maven2Fields);
286 	def->xpathFileSpecs = xpathFileSpecs;
287 	def->xpathFileSpecCount = ARRAY_SIZE (xpathFileSpecs);
288 	return def;
289 }
290