]> git.proxmox.com Git - mirror_edk2.git/blame - Tools/Source/FrameworkWizard/src/org/tianocore/frameworkwizard/platform/ui/global/GlobalData.java
1. adjust contents layout of SPD header editor, FPD header editor.
[mirror_edk2.git] / Tools / Source / FrameworkWizard / src / org / tianocore / frameworkwizard / platform / ui / global / GlobalData.java
CommitLineData
a13899c5 1/** @file\r
2 GlobalData class. \r
3 \r
4 GlobalData provide initializing, instoring, querying and update global data.\r
5 It is a bridge to intercommunicate between multiple component, such as AutoGen,\r
6 PCD and so on. \r
7 \r
8Copyright (c) 2006, Intel Corporation\r
9All rights reserved. This program and the accompanying materials\r
10are licensed and made available under the terms and conditions of the BSD License\r
11which accompanies this distribution. The full text of the license may be found at\r
12http://opensource.org/licenses/bsd-license.php\r
13\r
14THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
15WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
16**/\r
17package org.tianocore.frameworkwizard.platform.ui.global;\r
18\r
19import org.apache.xmlbeans.XmlObject;\r
20import org.tianocore.DbPathAndFilename;\r
21import org.tianocore.FrameworkDatabaseDocument;\r
22import org.tianocore.ModuleSurfaceAreaDocument;\r
23import org.tianocore.ModuleSurfaceAreaDocument.ModuleSurfaceArea;\r
24import org.tianocore.frameworkwizard.platform.ui.id.FpdModuleIdentification;\r
25import org.tianocore.frameworkwizard.platform.ui.id.ModuleIdentification;\r
26import org.tianocore.frameworkwizard.platform.ui.id.PackageIdentification;\r
27import org.tianocore.frameworkwizard.platform.ui.id.PlatformIdentification;\r
28\r
29import java.io.File;\r
30import java.util.Comparator;\r
31import java.util.HashMap;\r
32import java.util.HashSet;\r
33import java.util.Iterator;\r
a13899c5 34import java.util.List;\r
35import java.util.Map;\r
36import java.util.Set;\r
a13899c5 37import java.util.logging.Logger;\r
a13899c5 38\r
39/**\r
40 GlobalData provide initializing, instoring, querying and update global data.\r
41 It is a bridge to intercommunicate between multiple component, such as AutoGen,\r
42 PCD and so on. \r
43 \r
44 <p>Note that all global information are initialized incrementally. All data will \r
45 parse and record only of necessary during build time. </p>\r
46 \r
47 @since GenBuild 1.0\r
48**/\r
49public class GlobalData {\r
50\r
51\r
52 public static Logger log = Logger.getAnonymousLogger();\r
53 public static KeyComparator comparator = new KeyComparator();\r
54 ///\r
55 /// Record current WORKSPACE Directory\r
56 ///\r
57 private static String workspaceDir = "";\r
58 \r
59 ///\r
60 /// Be used to ensure Global data will be initialized only once.\r
61 ///\r
410e0e9f 62// private static boolean globalFlag = false;\r
a13899c5 63 \r
64 ///\r
65 /// Framework Database information: package list and platform list\r
66 ///\r
67 private static Set<PackageIdentification> packageList = new HashSet<PackageIdentification>(); \r
68\r
69 private static Set<PlatformIdentification> platformList = new HashSet<PlatformIdentification>();\r
70\r
71 ///\r
72 /// Every detail SPD informations: Module list, Library class definition,\r
73 /// Package header file, GUID/PPI/Protocol definitions\r
74 ///\r
75 private static final Map<PackageIdentification, Spd> spdTable = new HashMap<PackageIdentification, Spd>();\r
76\r
77 ///\r
78 /// Build informations are divided into three parts:\r
79 /// 1. From MSA 2. From FPD 3. From FPD' ModuleSA\r
80 ///\r
81 private static Map<ModuleIdentification, Map<String, XmlObject>> nativeMsa = new HashMap<ModuleIdentification, Map<String, XmlObject>>();\r
82\r
83 private static Map<FpdModuleIdentification, Map<String, XmlObject>> fpdModuleSA= new HashMap<FpdModuleIdentification, Map<String, XmlObject>>();\r
84\r
85 private static XmlObject fpdBuildOptions;\r
86\r
87 private static XmlObject fpdDynamicPcds;\r
88 \r
89 ///\r
90 /// Parsed modules list\r
91 ///\r
92 private static Map<FpdModuleIdentification, Map<String, XmlObject>> parsedModules = new HashMap<FpdModuleIdentification, Map<String, XmlObject>>();\r
93 \r
94 ///\r
95 /// built modules list with ARCH, TARGET, TOOLCHAIN\r
96 ///\r
97 private static Set<FpdModuleIdentification> builtModules = new HashSet<FpdModuleIdentification>();\r
98 \r
99 ///\r
100 /// PCD memory database stored all PCD information which collected from FPD,MSA and SPD.\r
101 ///\r
102// private static final MemoryDatabaseManager pcdDbManager = new MemoryDatabaseManager();\r
103\r
104 ///\r
105 /// build target + tool chain family/tag name + arch + command types + command options\r
106 ///\r
107 private static Map<String, Object> toolChainOptions;\r
108 private static Map<String, Object> toolChainFamilyOptions;\r
109 private static Map<String, String> toolChainDefinitions;\r
a13899c5 110 ///\r
111 ///\r
112 ///\r
113 private static Set<String> targets;\r
114 ///\r
115 ///\r
116 ///\r
117 private static Set<String> toolChainFamilies;\r
118 ///\r
119 ///\r
120 ///\r
121 private static Set<String> toolChains;\r
122 ///\r
123 /// keep track which toolchain family a toolchain tag belongs to\r
124 ///\r
125 private static Map<String, Set<String>> toolChainFamilyMap;\r
126 private static Map<String, Set<String>> toolChainCommandMap;\r
127 \r
128 ///\r
129 /// list of Arch: EBC, ARM, IA32, X64, IPF, PPC\r
130 ///\r
131 private static Set<String> archs;\r
132\r
133 ///\r
134 /// list of Command Type: CC, LIB, LINK, ASL, ASM, ASMLINK, PP\r
135 ///\r
136 private static Set<String> commandTypes;\r
137 \r
138 /**\r
139 Parse framework database (DB) and all SPD files listed in DB to initialize\r
140 the environment for next build. This method will only be executed only once\r
141 in the whole build process. \r
142 \r
143 @param workspaceDatabaseFile the file name of framework database\r
144 @param workspaceDir current workspace directory path\r
145 @throws Exception\r
146 Framework Dababase or SPD or MSA file is not valid\r
147 **/\r
148 public synchronized static void initInfo(String workspaceDatabaseFile, String workspaceDir) throws Exception {\r
a13899c5 149 \r
150 //\r
151 // Backup workspace directory. It will be used by other method\r
152 //\r
153 GlobalData.workspaceDir = workspaceDir.replaceAll("(\\\\)", "/");\r
154 File dbFile = new File(workspaceDir + File.separatorChar + workspaceDatabaseFile);\r
155 try {\r
156 FrameworkDatabaseDocument db = (FrameworkDatabaseDocument) XmlObject.Factory.parse(dbFile);\r
157 //\r
158 // validate FrameworkDatabaseFile\r
159 //\r
160// if (! db.validate()) {\r
161// throw new Exception("Framework Database file [" + dbFile.getPath() + "] is invalid.");\r
162// }\r
163 //\r
164 // Get package list\r
165 //\r
166 List<DbPathAndFilename> packages = db.getFrameworkDatabase().getPackageList().getFilenameList();\r
167 \r
168 Iterator iter = packages.iterator();\r
169 while (iter.hasNext()) {\r
170 DbPathAndFilename dbPath = (DbPathAndFilename)iter.next();\r
171 String fileName = dbPath.getStringValue();\r
172 Spd spd = new Spd(new File(workspaceDir + File.separatorChar + fileName));\r
410e0e9f 173 if (!packageList.contains(spd.getPackageId())) {\r
174 packageList.add(spd.getPackageId());\r
175 }\r
a13899c5 176 spdTable.put(spd.getPackageId(), spd);\r
177 }\r
178\r
179 \r
180 } catch (Exception e) {\r
181 e.printStackTrace();\r
182 throw new Exception("Parse workspace Database [" + dbFile.getPath() + "] Error.\n" + e.getMessage());\r
183 }\r
184 }\r
185 \r
186 /**\r
187 Get the current WORKSPACE Directory. \r
188 \r
189 @return current workspace directory\r
190 **/\r
191 public synchronized static String getWorkspacePath() {\r
192 return workspaceDir;\r
193 }\r
194\r
195\r
196 /**\r
197 Get the MSA file name with absolute path\r
198 */\r
199 public synchronized static File getMsaFile(ModuleIdentification moduleId) throws Exception {\r
200 File msaFile = null;\r
201 //\r
202 // TBD. Do only when package is null. \r
203 //\r
204 Iterator iter = packageList.iterator();\r
205 while (iter.hasNext()) {\r
206 PackageIdentification packageId = (PackageIdentification)iter.next();\r
207 Spd spd = spdTable.get(packageId);\r
208 msaFile = spd.getModuleFile(moduleId);\r
209 if (msaFile != null ) {\r
210 break ;\r
211 }\r
212 }\r
213 if (msaFile == null){\r
214 throw new Exception("Can't find Module [" + moduleId.getName() + "] in all packages. ");\r
215 }\r
216 else {\r
217 return msaFile;\r
218 }\r
219 }\r
220\r
221 public synchronized static PackageIdentification getPackageForModule(ModuleIdentification moduleId) {\r
222 //\r
223 // If package already defined in module\r
224 //\r
225 if (moduleId.getPackage() != null) {\r
226 return moduleId.getPackage();\r
227 }\r
228 \r
229 PackageIdentification packageId = null;\r
230 Iterator iter = packageList.iterator();\r
231 while (iter.hasNext()) {\r
232 packageId = (PackageIdentification)iter.next();\r
233 \r
234 Spd spd = spdTable.get(packageId);\r
235 if (spd.getModuleFile(moduleId) != null ) {\r
236 moduleId.setPackage(packageId);\r
237 break ;\r
238 }\r
239 }\r
240 if (packageId == null){\r
241 return null;\r
242 }\r
243 else {\r
244 return packageId;\r
245 }\r
246 }\r
247 \r
248 /**\r
249 Difference between build and parse: ToolChain and Target\r
250 **/\r
251 public synchronized static boolean isModuleBuilt(FpdModuleIdentification moduleId) {\r
252 return builtModules.contains(moduleId);\r
253 }\r
254 \r
255 public synchronized static void registerBuiltModule(FpdModuleIdentification fpdModuleId) {\r
256 builtModules.add(fpdModuleId);\r
257 }\r
258\r
259 \r
260 public synchronized static void registerFpdModuleSA(FpdModuleIdentification fpdModuleId, Map<String, XmlObject> doc) throws Exception{\r
261 Map<String, XmlObject> result = new HashMap<String, XmlObject>();\r
262 Set keySet = doc.keySet();\r
263 Iterator iter = keySet.iterator();\r
264 while (iter.hasNext()){\r
265 String key = (String)iter.next();\r
266 XmlObject item = cloneXmlObject(doc.get(key), true);\r
267 result.put(key, item);\r
268 }\r
269 fpdModuleSA.put(fpdModuleId, result);\r
270 }\r
271 \r
272 /**\r
273 Query overrided module surface area information. If current is Package\r
274 or Platform build, also include the information from FPD file. \r
275 \r
276 <p>Note that surface area parsing is incremental. That means the method will \r
277 only parse the MSA and MBD files if necessary. </p>\r
278 \r
279 @param moduleName the base name of the module\r
280 @return the overrided module surface area information\r
281 @throws Exception\r
282 MSA or MBD is not valid\r
283 **/\r
284 public synchronized static Map<String, XmlObject> getDoc(FpdModuleIdentification fpdModuleId) throws Exception {\r
285 if (parsedModules.containsKey(fpdModuleId)) {\r
286 return parsedModules.get(fpdModuleId);\r
287 }\r
288 Map<String, XmlObject> doc = new HashMap<String, XmlObject>();\r
289 ModuleIdentification moduleId = fpdModuleId.getModule();\r
290 //\r
291 // First part: get the MSA files info\r
292 //\r
293 doc = getNativeMsa(moduleId);\r
294 \r
295 //\r
296 // Second part: put build options\r
297 //\r
298 doc.put("BuildOptions", fpdBuildOptions);\r
299 \r
300 //\r
301 // Third part: get Module info from FPD, such as Library instances, PCDs\r
302 //\r
303 if (fpdModuleSA.containsKey(fpdModuleId)){\r
304 //\r
305 // merge module info in FPD to final Doc\r
306 // For Library Module, do nothing here\r
307 //\r
308 doc.putAll(fpdModuleSA.get(fpdModuleId));\r
309 }\r
310 parsedModules.put(fpdModuleId, doc);\r
311 return doc;\r
312 }\r
313\r
314 public synchronized static Map<String, XmlObject> getDoc(ModuleIdentification moduleId, String arch) throws Exception {\r
315 FpdModuleIdentification fpdModuleId = new FpdModuleIdentification(moduleId, arch);\r
316 return getDoc(fpdModuleId);\r
317 }\r
318 /**\r
319 Query the native MSA information with module base name. \r
320 \r
321 <p>Note that MSA parsing is incremental. That means the method will \r
322 only to parse the MSA files when never parsed before. </p>\r
323 \r
324 @param moduleName the base name of the module\r
325 @return the native MSA information\r
326 @throws Exception\r
327 MSA file is not valid\r
328 **/\r
329 public synchronized static Map<String, XmlObject> getNativeMsa(ModuleIdentification moduleId) throws Exception {\r
330 if (nativeMsa.containsKey(moduleId)) {\r
331 return nativeMsa.get(moduleId);\r
332 }\r
333 File msaFile = getMsaFile(moduleId);\r
334 Map<String, XmlObject> msaMap = getNativeMsa(msaFile);\r
335 nativeMsa.put(moduleId, msaMap);\r
336 return msaMap;\r
337 }\r
338 \r
339 public synchronized static Map<String, XmlObject> getNativeMsa(File msaFile) throws Exception {\r
410e0e9f 340 \r
a13899c5 341 try {\r
342 ModuleSurfaceAreaDocument doc = (ModuleSurfaceAreaDocument)XmlObject.Factory.parse(msaFile);\r
343 //\r
344 // Validate File if they accord with XML Schema\r
345 //\r
346// if ( ! doc.validate()){\r
347// throw new Exception("Module Surface Area file [" + msaFile.getPath() + "] is invalid.");\r
348// }\r
349 //\r
350 // parse MSA file\r
351 //\r
352 ModuleSurfaceArea msa= doc.getModuleSurfaceArea();\r
353 Map<String, XmlObject> msaMap = new HashMap<String, XmlObject>();\r
354 msaMap.put("ModuleSurfaceArea", msa);\r
355 msaMap.put("MsaHeader", cloneXmlObject(msa.getMsaHeader(), true));\r
356 msaMap.put("LibraryClassDefinitions", cloneXmlObject(msa.getLibraryClassDefinitions(), true));\r
357 msaMap.put("SourceFiles", cloneXmlObject(msa.getSourceFiles(), true));\r
358 msaMap.put("PackageDependencies", cloneXmlObject(msa.getPackageDependencies(), true));\r
359 msaMap.put("Protocols", cloneXmlObject(msa.getProtocols(), true));\r
360 msaMap.put("PPIs", cloneXmlObject(msa.getPPIs(), true));\r
361 msaMap.put("Guids", cloneXmlObject(msa.getGuids(), true));\r
362 msaMap.put("Externs", cloneXmlObject(msa.getExterns(), true));\r
363 return msaMap;\r
364 }\r
365 catch (Exception ex){\r
366 throw new Exception(ex.getMessage());\r
367 }\r
368 }\r
369 \r
370 public static Map<String, XmlObject> getFpdBuildOptions() {\r
371 Map<String, XmlObject> map = new HashMap<String, XmlObject>();\r
372 map.put("BuildOptions", fpdBuildOptions);\r
373 return map;\r
374 }\r
375 \r
376 public static void setFpdBuildOptions(XmlObject fpdBuildOptions) throws Exception{\r
377 GlobalData.fpdBuildOptions = cloneXmlObject(fpdBuildOptions, true);\r
378 }\r
379\r
380 public static XmlObject getFpdDynamicPcds() {\r
381 return fpdDynamicPcds;\r
382 }\r
383\r
384 public static void setFpdDynamicPcds(XmlObject fpdDynamicPcds) {\r
385 GlobalData.fpdDynamicPcds = fpdDynamicPcds;\r
386 }\r
387\r
388 //////////////////////////////////////////////\r
389 //////////////////////////////////////////////\r
390 \r
391 public static Set<ModuleIdentification> getModules(PackageIdentification packageId){\r
392 Spd spd = spdTable.get(packageId);\r
393 if (spd == null ) {\r
394 Set<ModuleIdentification> dummy = new HashSet<ModuleIdentification>();\r
395 return dummy;\r
396 }\r
397 else {\r
398 return spd.getModules();\r
399 }\r
400 }\r
401\r
a13899c5 402 public synchronized static PlatformIdentification getPlatform(String name) throws Exception {\r
403 Iterator iter = platformList.iterator();\r
404 while(iter.hasNext()){\r
405 PlatformIdentification platformId = (PlatformIdentification)iter.next();\r
406 if (platformId.getName().equalsIgnoreCase(name)) {\r
407 GlobalData.log.info("Platform: " + platformId + platformId.getFpdFile());\r
408 return platformId;\r
409 }\r
410 }\r
411 throw new Exception("Can't find platform [" + name + "] in current workspace. ");\r
412 }\r
413 \r
414 public synchronized static File getPackageFile(PackageIdentification packageId) throws Exception {\r
415 Iterator iter = packageList.iterator();\r
416 while(iter.hasNext()){\r
417 PackageIdentification packageItem = (PackageIdentification)iter.next();\r
418 if (packageItem.equals(packageId)) {\r
419 packageId.setName(packageItem.getName());\r
420 return packageItem.getSpdFile();\r
421 }\r
422 }\r
423 throw new Exception("Can't find " + packageId + " in current workspace. ");\r
424 }\r
425 \r
426 public synchronized static File getModuleFile(ModuleIdentification moduleId) throws Exception {\r
427 PackageIdentification packageId = getPackageForModule(moduleId);\r
428 moduleId.setPackage(packageId);\r
429 Spd spd = spdTable.get(packageId);\r
430 return spd.getModuleFile(moduleId);\r
431 }\r
432 //\r
433 // expanded by FrameworkWizard\r
434 //\r
435 public synchronized static XmlObject getModuleXmlObject(ModuleIdentification moduleId) throws Exception {\r
436 PackageIdentification packageId = getPackageForModule(moduleId);\r
437 moduleId.setPackage(packageId);\r
438 Spd spd = spdTable.get(packageId);\r
439 return spd.msaDocMap.get(moduleId);\r
440 }\r
441 \r
442 public synchronized static XmlObject getPackageXmlObject(PackageIdentification packageId) {\r
443 Spd spd = spdTable.get(packageId);\r
444 if (spd != null){\r
445 return spd.spdDocMap.get("PackageSurfaceArea");\r
446 }\r
447 return null;\r
448 }\r
449 \r
450 public synchronized static Set<PackageIdentification> getPackageList(){\r
451 return packageList;\r
452 }\r
453 ///// remove!!\r
454 private static XmlObject cloneXmlObject(XmlObject object, boolean deep) throws Exception {\r
455 if ( object == null) {\r
456 return null;\r
457 }\r
458 XmlObject result = null;\r
459 try {\r
460 result = XmlObject.Factory.parse(object.getDomNode()\r
461 .cloneNode(deep));\r
462 } catch (Exception ex) {\r
463 throw new Exception(ex.getMessage());\r
464 }\r
465 return result;\r
466 }\r
467\r
468 ////// Tool Chain Related, try to refine and put some logic process to ToolChainFactory\r
469 public static void setBuildToolChainFamilyOptions(Map<String, Object> map) {\r
470 toolChainFamilyOptions = map;\r
471 }\r
472\r
473 public static Map<String, Object> getToolChainFamilyOptions() {\r
474 return toolChainFamilyOptions;\r
475 }\r
476\r
477 public static void setBuildToolChainOptions(Map<String, Object> map) {\r
478 toolChainOptions = map;\r
479 }\r
480\r
481 public static Map<String, Object> getToolChainOptions() {\r
482 return toolChainOptions;\r
483 }\r
484\r
485 public static void setTargets(Set<String> targetSet) {\r
486 GlobalData.log.info("TargetSet: " + targetSet);\r
487 targets = targetSet;\r
488 }\r
489\r
490 public static String[] getTargets() {\r
491 return (String[])targets.toArray(new String[targets.size()]);\r
492 }\r
493\r
494 public static void setToolChains(Set<String> toolChainSet) {\r
495 toolChains = toolChainSet;\r
496 }\r
497\r
498 public static String[] getToolChains() {\r
499 String[] toolChainList = new String[toolChains.size()];\r
500 return (String[])toolChains.toArray(toolChainList);\r
501 }\r
502\r
503 public static void setToolChainFamilies(Set<String> toolChainFamilySet) {\r
504 toolChainFamilies = toolChainFamilySet;\r
505 }\r
506\r
507 public static void setToolChainFamiliyMap(Map<String, Set<String>> map) {\r
508 /*\r
509 Set<String> keys = map.keySet();\r
510 Iterator it = keys.iterator();\r
511 while (it.hasNext()) {\r
512 String toolchain = (String)it.next();\r
513 Set<String> familyMap = (Set<String>)map.get(toolchain);\r
514 Iterator fit = familyMap.iterator();\r
515 System.out.print(toolchain + ": ");\r
516 while (fit.hasNext()) {\r
517 System.out.print((String)fit.next() + " ");\r
518 }\r
519 System.out.println("");\r
520 }\r
521 */\r
522 toolChainFamilyMap = map;\r
523 }\r
524\r
525 public static String[] getToolChainFamilies() {\r
526 String[] toolChainFamilyList = new String[toolChainFamilies.size()];\r
527 return (String[])toolChainFamilies.toArray(toolChainFamilyList);\r
528 }\r
529\r
530 public static String[] getToolChainFamilies(String toolChain) {\r
531 Set<String> familySet = (Set<String>)toolChainFamilyMap.get(toolChain);\r
532 String[] toolChainFamilyList = new String[familySet.size()];\r
533 return (String[])familySet.toArray(toolChainFamilyList);\r
534 }\r
535\r
536 public static Set<String> getToolChainFamilySet(String toolChain) {\r
537 return (Set<String>)toolChainFamilyMap.get(toolChain);\r
538 }\r
539\r
540 public static void setArchs(Set<String> archSet) {\r
541 archs = archSet;\r
542 }\r
543\r
544 public static String[] getArchs() {\r
545 String[] archList = new String[archs.size()];\r
546 return (String[])archs.toArray(archList);\r
547 }\r
548 /*\r
549\r
550 */\r
551 public static void SetCommandTypes(Set<String> commandTypeSet) {\r
552 commandTypes = commandTypeSet;\r
553 }\r
554 /*\r
555\r
556 */\r
557 public static void SetCommandTypes(Map<String, Set<String>> commandTypeMap) {\r
558 toolChainCommandMap = commandTypeMap;\r
559 }\r
560 /*\r
561\r
562 */\r
563 public static String[] getCommandTypes() {\r
564 String[] commandList = new String[commandTypes.size()];\r
565 return (String[])commandTypes.toArray(commandList);\r
566 }\r
567 /*\r
568\r
569 */\r
570 public static String[] getCommandTypes(String toolChain) {\r
571 Set<String> commands = (Set<String>)toolChainCommandMap.get(toolChain);\r
572 if (commands == null) {\r
573 return new String[0];\r
574 }\r
575\r
576 String[] commandList = new String[commands.size()];\r
577 return (String[])commands.toArray(commandList);\r
578 }\r
a13899c5 579 /*\r
580\r
581 */\r
582 public static String getCommandSetting(String commandDescString) {\r
583 return (String)toolChainDefinitions.get(commandDescString);\r
584 }\r
a13899c5 585 /*\r
586\r
587 */\r
588 public static void setToolChainDefinitions(Map<String, String> def) {\r
589 toolChainDefinitions = def;\r
590 }\r
591\r
592 public static Map<String, String> getToolChainDefinitions() {\r
593 return toolChainDefinitions;\r
594 }\r
595\r
a13899c5 596}\r
597\r
598final class KeyComparator implements Comparator<String> {\r
599 public int compare(String x, String y) {\r
600 return x.compareToIgnoreCase(y);\r
601 }\r
602 \r
603}\r
604\r