Subversion Repositories DevTools

Rev

Rev 7176 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
6914 dpurdie 1
package com.erggroup.buildtool.ripple;
2
 
3
import java.io.File;
4
import java.util.ArrayList;
7082 dpurdie 5
import java.util.Comparator;
6914 dpurdie 6
import java.util.HashMap;
7
import java.util.Iterator;
8
import java.util.LinkedHashSet;
9
import java.util.Vector;
10
 
7033 dpurdie 11
import org.slf4j.Logger;
12
import org.slf4j.LoggerFactory;
6914 dpurdie 13
 
14
import com.erggroup.buildtool.ripple.ReleaseManager.BuildReason;
15
import com.erggroup.buildtool.utilities.MutableInt;
16
import com.erggroup.buildtool.utilities.XmlBuilder;
17
import com.erggroup.buildtool.utilities.utilities;
18
 
19
public class Package
20
{
21
    /**
22
     * name of package, must not contain spaces
23
     * 
24
     * @attribute
25
     */
26
    String mName = new String();
27
 
28
    /**
29
     * package scope
30
     * 
31
     * @attribute
32
     */
33
    String mExtension = new String();
34
 
35
    /**
36
     * instance identifier
37
     * 
38
     * @attribute
39
     */
40
    public String mVersion = new String();
7082 dpurdie 41
 
42
    /**
43
     * If this package is a candidate for building, then this value will be calculated
44
     * and will  be used if the package is selected for building
45
     */
46
    public String mNextVersion = null;
6914 dpurdie 47
 
48
    /**
49
     * Version string as specified by the user. Used with a ripple type of 'F'
50
     */
51
    String mFixedVersion = new String();
52
 
53
    /**
54
     * unique identifier 
55
     *      daemon builds = mName + mExtension
56
     *      escrow builds = mName + mVersion + mExtension
57
     * 
58
     * @attribute
59
     */
60
    public String mAlias = new String();
61
 
62
    /**
63
     * Version Control System Tag
64
     * 
65
     * @attribute
66
     */
67
    String mVcsTag = new String();
68
 
69
    /**
70
     * build standards
71
     * 
72
     * @attribute
73
     */
74
    public Vector<BuildStandard> mBuildStandardCollection = new Vector<BuildStandard>();
75
 
76
    /**
77
     * GBE_MACHTYPE used to build generic packages for this baseline only has
78
     * meaning in the daemon build, not the escrow build accessed by
79
     * BuildStandard::getPlatform, getBuildStandard
80
     * 
81
     * @attribute
82
     */
83
    public static String mGenericMachtype = System.getenv("GBE_MACHTYPE");
84
 
85
    /**
7163 dpurdie 86
     * primary package version key pv_id in database
6914 dpurdie 87
     * 
88
     * @attribute
89
     */
7163 dpurdie 90
    public int mId;
6914 dpurdie 91
 
7163 dpurdie 92
    public class PkgDependency {
93
        String alias;
94
        Integer pvId;
95
        Package pkg;
96
 
97
        public PkgDependency( String alias, Integer pvId)
98
        {
99
            this.alias = alias;
100
            this.pvId = pvId;
101
            pkg = null;
102
        }
103
 
104
        //  Assist in debugging
105
        public String toString()
106
        {
107
            String pkgData = "";
108
            if (pkg != null)
109
                pkgData = " {" + pkg.mId + ":" + pkg.mAlias +"}";
110
 
111
            return Integer.toString(pvId) + ":" + alias  + pkgData;
112
        }
113
    }
114
 
6914 dpurdie 115
    /**
7163 dpurdie 116
     * Package dependencies
117
     * A triplet of alias, pvId and Package
118
     *  alias and pvId are read from the database
119
     *  Package is located during planning
6914 dpurdie 120
     * 
121
     */
7163 dpurdie 122
    public Vector<PkgDependency> mDependencyCollection = new Vector<PkgDependency>();
123
 
6914 dpurdie 124
    /**
125
     * indication of the nature of change
126
     * 
127
     * @attribute
128
     */
129
    Package.VersionNumberingStandard mChangeType = new VersionNumberingStandard();
130
 
131
    /**
132
     * determines what field is rippled on a package version whose dependencies
133
     * have changed
134
     * 
135
     * @attribute
136
     */
137
    Package.VersionNumberingStandard mRippleField = new VersionNumberingStandard();
138
 
139
    /**
140
     * interested owners
141
     * 
142
     * @attribute
143
     */
144
    private Vector<String> mBuildFailureEmailCollection = new Vector<String>();
145
 
146
    /**
147
     * when true will trigger unit tests as part of the package build phase in
148
     * daemon mode
149
     * 
150
     * @attribute
151
     */
152
    public boolean mHasAutomatedUnitTests = false;
153
 
154
    /**
155
     * when true, do not ripple this package through packages which are
156
     * dependent upon it in daemon mode
157
     * 
158
     * @attribute
159
     */
160
    public boolean mAdvisoryRipple = false;
161
 
162
    /**
163
     * Pegged packages will:
164
     *   - Not be rippled
165
     *   - Not be test buildable
166
     *   - Dependencies will not be considered
167
     *   Daemon Mode:
168
     *      Are not built in this release
169
     *      Are imported with out consideration as to their dependencies
170
     *      Must exist in dpkg_archive - will not be built if not present
171
     *   Escrow Mode:
172
     *      Have no special consideration
173
     *      The package and its dependencies will be built
174
     *
175
     * Packages imported into the release from an SDK will be treated as if they were
176
     * pegged by the built system.
177
     * 
178
     */
179
    public boolean mIsPegged = false;
180
    public boolean mIsSdk = false;
181
 
182
    /**
183
     * Unbuildable packages will not be candidates for a ripple or a rebuild
184
     * If they do not exist in dpkg_archive, then then will cause an error
185
     */
186
    public boolean mIsBuildable = true;
187
 
188
    /**
189
     * determines the build file the package is built in, or not
7082 dpurdie 190
     * <br>   1 Post Plan: build file - Result of planning
191
     * <br>   2 Post Plan: Future build requirement
192
     * <br>   3 Post Plan: Package has no build requirement
193
     * <br>   0 not yet processed (initial value) 
194
     * <br>  -1 not reproducible
195
     * <br>  -2 escrow: not reproducible on the build platforms configured for this release
196
     * <br>  -3 daemon: do not ripple
197
     * <br>  -4 directly dependent on package versions not in the baseline
198
     * <br>  -5 indirectly dependent on package versions which are not reproducible due to detected fault
199
     * <br>  -6 circular dependency
200
     * <br>  -7 Pegged or SDK Imported package not in dpkg_archive
201
     * <br>  -8 Pegged or SDK Imported package
202
     * <br>  -9 Rejected Daemon Instruction
203
     * <br>  -10 UnBuildable Package not in dpkg_archive
204
     * <br>  -11 Marked as RippleStop
7099 dpurdie 205
     * <br>  -12 Cannot calc next version number
6914 dpurdie 206
     * 
207
     */
208
    int mBuildFile = 0;
209
 
210
    /** Retained reason the package will not be built
211
     *  Save negative values as mBuildFile
212
     * @attribute
213
     */
214
    int mNoBuildReason = 0;
215
 
216
    /**
217
     * used for escrow build purposes set true when a package has been processed
218
     * 
219
     * @attribute
220
     */
221
    boolean mProcessed = false;
222
 
223
    /**
224
     * Reason that a package is being built
225
     */
226
    public BuildReason mBuildReason = null;
227
 
228
    /**
229
     * set true for WIP package versions only used in daemon mode
230
     * 
231
     * @attribute
232
     */
233
    public boolean mDirectlyPlanned = false;
234
 
235
    /**
236
     * set true when it is determined to be ripple built
237
     * 
238
     * @attribute
239
     */
240
    boolean mIndirectlyPlanned = false;
241
 
242
    /**
243
     * non zero instruction number when it is determined to be ripple built by force
244
     * 
245
     * @attribute
246
     */
7082 dpurdie 247
    public int mForcedRippleInstruction = 0;
6914 dpurdie 248
 
249
    /**
250
     * test build - non zero instruction number when it is determined to be test built
251
     * 
252
     * @attribute
253
     */
7082 dpurdie 254
    public int mTestBuildInstruction = 0;
6914 dpurdie 255
 
256
    /**
257
     * unique pkg_id in the database used for querying package version existence
258
     * in the database in daemon mode
259
     * 
260
     * @attribute
261
     */
262
    public int mPid;
263
 
264
    /**
265
     * maximum major number supported for determining ripple number
266
     * 
267
     * @attribute
268
     */
269
    int mMajorLimit;
270
 
271
    /**
272
     * maximum minor number supported for determining ripple number
273
     * 
274
     * @attribute
275
     */
276
    int mMinorLimit;
277
 
278
    /**
279
     * maximum patch number supported for determining ripple number
280
     * 
281
     * @attribute
282
     */
283
    int mPatchLimit;
284
 
285
    /**
286
     * maximum build number number supported for determining ripple number
287
     * 
288
     * @attribute
289
     */
290
    int mBuildLimit;
291
 
292
    /**
293
     * Logger
294
     * 
295
     * @attribute
296
     */
7033 dpurdie 297
    private static final Logger mLogger = LoggerFactory.getLogger(Package.class);
6914 dpurdie 298
 
299
    /**
300
     * dpkg archive location - The writable dpkg_archive
301
     * 
302
     * @attribute
303
     */
304
    public static String mGbeDpkg = System.getenv("GBE_DPKG");
305
 
306
    /**
307
     *  A physically close replica of dpkg_archive
308
     *  Is not writable 
309
     */
310
    public static final String mGbeDpkgReplica = System.getenv("GBE_DPKG_REPLICA");
311
 
312
    /**
313
     * Exception message used upon detection an archive does not exist Seems
314
     * this is a rare but transient and recoverable scenario
315
     * 
316
     * @attribute
317
     */
318
    public static final String mRecoverable = "dpkg_archive does not exist, recovery will be attempted";
319
 
320
    /**
321
     * true if the package exists in the package archive (dpkg_archive)
322
     * 
323
     * @attribute
324
     */
325
    private boolean mArchivalExistence = true;
326
 
327
    /**
328
     * when true will trigger source control interaction eg labelling
329
     * 
330
     * @attribute
331
     */
332
    public boolean mRequiresSourceControlInteraction = true;
333
 
334
    /**
335
     * when true has been checked for circular dependency
336
     * 
337
     * @attribute
338
     */
339
    boolean mCheckedCircularDependency = false;
340
 
341
    /**
342
     * when true has circular dependency, or in the process of detecting a circular dependency
343
     * 
344
     * @attribute
345
     */
346
    boolean mHasCircularDependency = false;
347
 
348
    /**
349
     * Bread crumb used to detect circular dependencies
350
     * 0 - Normal state
351
     * 1 - Crumb has been set
352
     * 2 - Crumb is a part of a circular dependency
353
     * 3 - Crumb is the start/end of the loop
354
     */
355
    int mBreadCrumb = 0;
356
 
357
    /**
358
     * Planned packages (WIPS) need calculate a new version number
359
     * This should be based on the Version of the package that the WIP was based upon
360
     */
361
	public String mPrevVersion;
362
 
7082 dpurdie 363
    /** Used to preserve the basic ordering of the package list(s)
364
     *  Only valid for the duration of the comparison sorts
365
     */
366
    int mSeqId;	
367
 
6914 dpurdie 368
	/** Indicates that the package not be ripple built at this time
369
	 *  Sequence is:
370
	 *     User sets to 's'
371
	 *     This buildtool will set to 'w' when a ripple has been detected
372
	 *     User resets to NULL when the ripple can be resumed. This is stored as an 'n'
373
	 * 
374
	 */
375
    public char mRippleStop;
7046 dpurdie 376
 
377
    /** The duration (seconds) of the last build of this package
378
     *  Used to estimate the cost of building the package
379
     *  A WIP packages gets the buildTime of the package its based upon
380
     */
381
    public int mBuildTime;
6914 dpurdie 382
 
7082 dpurdie 383
    /** Set to indicate that the package is NOT a part of the full release set
384
     *  The package is a WIP, TestBuild or a ForcedRipple
385
     *  
386
     *   Used to simplify detection of such packages and to limit processing of these packages
387
     *   May be cleared if the package is added to the full set
388
     */
389
    public boolean mIsNotReleased = false;
390
 
391
    /** Used by some algorithms for various indications
392
     * 
393
     */
394
    public boolean mIsProcessed = false;
395
 
6914 dpurdie 396
    /**
397
     * Constructor
7099 dpurdie 398
     * Used to create planned packages (WIP). In these packages the change_type is significant
6914 dpurdie 399
     * 
7099 dpurdie 400
     * @param pkgId	   Package Name Identifier
401
     * @param pvId        Package Version Id
402
     * @param pkgName     Package Name
403
     * @param pkgVersion  Package Version
404
     * @param vExt        Package Suffix
6914 dpurdie 405
     * @param alias        Package Alias
7099 dpurdie 406
     * @param pkgVcsTag  Vcs Tag
407
     * @param rippleField Ripple Field
408
     * @param changeType  Change Type
6914 dpurdie 409
     */
7099 dpurdie 410
    public Package(int pkgId, int pvId, String pkgName, String pkgVersion, String vExt, String alias, String pkgVcsTag, char rippleField, char changeType)
6914 dpurdie 411
    {
7099 dpurdie 412
        mLogger.debug("Package 1: pv_id {} pkg_name {} v_ext {} alias {} pkg_vcs_tag {} ripple_field {} change_type {}", pvId, pkgName,vExt,alias, pkgVcsTag, rippleField, changeType);
413
        mId = pvId;
414
        mName = pkgName;
415
        mPid = pkgId;
6914 dpurdie 416
        mVersion = "0.0.0000";
7099 dpurdie 417
        mExtension = vExt;
6914 dpurdie 418
        mAlias = alias;
7099 dpurdie 419
        mVcsTag = pkgVcsTag;
6914 dpurdie 420
 
421
        // Remove the package suffix from package_version to create the fixed version number
7099 dpurdie 422
        mFixedVersion = pkgVersion;
6914 dpurdie 423
        mFixedVersion = mFixedVersion.substring(0, mFixedVersion.length() - mExtension.length());
424
 
425
        // a ripple_field of 'L' indicates this package has limited version numbering
7099 dpurdie 426
        if (changeType == 'M') {
427
            mChangeType.setMajor(rippleField == 'L' ? true : false);
6914 dpurdie 428
 
7099 dpurdie 429
        } else if (changeType == 'N') {
430
            mChangeType.setMinor(rippleField == 'L' ? true : false);
6914 dpurdie 431
 
7099 dpurdie 432
        } else if (changeType == 'P') {
433
            mChangeType.setPatch(rippleField == 'L' ? true : false);
6914 dpurdie 434
 
7099 dpurdie 435
        } else if (changeType == 'F') {
6914 dpurdie 436
            mChangeType.setFixed();
437
 
438
        } else {
439
            mChangeType.setUnknown();
440
        }
441
    }
442
 
443
    /**
444
     * Constructor
445
     * Used to create existing packages, in these packages the ripple_field is significant
446
     * 
7099 dpurdie 447
     * @param pkgId	   Package Name Identifier
448
     * @param pvId        Package Version Id
449
     * @param pkgName     Package Name
450
     * @param pkgVersion  Package Version
451
     * @param vExt        Package Suffix
6914 dpurdie 452
     * @param alias        Package Alias
7099 dpurdie 453
     * @param pkgVcsTag  Vcs Tag
454
     * @param rippleField Ripple Field
6914 dpurdie 455
     */
7099 dpurdie 456
    public Package(int pkgId,int pvId, String pkgName, String pkgVersion, String vExt, String alias, String pkgVcsTag, char rippleField)
6914 dpurdie 457
    {
7099 dpurdie 458
        mLogger.debug("Package 2: pv_id {} pkg_name {} v_ext {} alias {} pkg_vcs_tag {} ripple_field {}", pvId, pkgName,vExt,alias, pkgVcsTag, rippleField);
459
        mId = pvId;
460
        mName = pkgName;
461
        mPid = pkgId;
462
        mVersion = pkgVersion;
463
        int endindex = mVersion.length() - vExt.length();
6914 dpurdie 464
 
465
        if (endindex > 0)
466
        {
467
            mVersion = mVersion.substring(0, endindex);
468
        }
469
 
7099 dpurdie 470
        mExtension = vExt;
6914 dpurdie 471
        mAlias = alias;
7099 dpurdie 472
        mVcsTag = pkgVcsTag;
6914 dpurdie 473
 
474
        // setBuild is the default
7099 dpurdie 475
        if (rippleField == 'M') {
6914 dpurdie 476
            mRippleField.setMajor();
477
 
7099 dpurdie 478
        } else if (rippleField == 'm') {
6914 dpurdie 479
            mRippleField.setMinor();
480
 
7099 dpurdie 481
        } else if (rippleField == 'p') {
6914 dpurdie 482
            mRippleField.setPatch();
483
 
7099 dpurdie 484
        } else if (rippleField == 'L') {
6914 dpurdie 485
            mRippleField.setLimit();
486
        }
487
    }
488
 
489
    /**
490
     * constructor
491
     */
492
    Package()
493
    {
494
        mLogger.debug("Package 3");
495
        mId = 0;
496
        mName = "null";
497
        mExtension = "null";
498
        mAlias = "null";
499
        mVcsTag = "null";
500
    }
501
 
502
    /**
7082 dpurdie 503
     * Constructor for unit test purposes
504
     *  Will invoke applyPV and save the results for the UTF framework
6914 dpurdie 505
     */
506
    public Package(ReleaseManager rm, String version, int majorLimit, int minorLimit, int patchLimit, int buildNumberLimit)
507
    {
508
        mId = -1;
509
        mRippleField.setLimit();
510
        mVersion = version;
511
        mMajorLimit = majorLimit;
512
        mMinorLimit = minorLimit;
513
        mPatchLimit = patchLimit;
514
        mBuildLimit = buildNumberLimit;
515
 
516
        if (version.endsWith(".cots"))
517
        {
518
            mExtension = ".cots";
519
            mVersion = version.substring(0, version.length() - 5);
520
            mChangeType.setMajor(false);
521
            mChangeType.setMinor(false);
522
            mChangeType.setPatch(true);
523
            mRippleField.setBuild();
524
        }
525
 
526
        try
527
        {
7082 dpurdie 528
            mId = applyPV(rm);
6914 dpurdie 529
        } catch (Exception e)
530
        {
531
        }
532
    }
533
 
534
    /**
7082 dpurdie 535
     * Constructor for unit test purposes
536
     * Performs a partial copy of a package - sufficient for test purposes
537
     * @param base      - Base package  
538
     * @param newPvId   - New pvid of the package
539
     */
540
 
541
    public Package(int newPvId, Package base) {
542
 
543
        mId = newPvId;
544
        mPid = base.mPid;
545
        mName = base.mName;
546
        mExtension = base.mExtension;
547
        mVersion = base.mVersion;
548
        mAlias = base.mAlias;
549
        mVcsTag = base.mVcsTag;
550
        mFixedVersion = base.mFixedVersion;
551
        mChangeType = base.mChangeType;
552
        mRippleField = base.mRippleField;
553
        mBuildTime = base.mBuildTime;
554
        mHasAutomatedUnitTests = base.mHasAutomatedUnitTests;
555
        mSeqId = base.mSeqId;
556
 
557
        mBuildFailureEmailCollection = new Vector<String>(base.mBuildFailureEmailCollection);
7163 dpurdie 558
        mDependencyCollection = new Vector<PkgDependency>(base.mDependencyCollection);
7082 dpurdie 559
        mBuildStandardCollection = new Vector<BuildStandard>( base.mBuildStandardCollection);
560
    }
561
 
562
    /** Generate a nice text string useful for debugging
563
     * 
564
     */
565
    public String toString()
566
    {
567
        return mId + ":" + mAlias;
568
    }
569
 
570
    /**
6914 dpurdie 571
     * accessor for unit test purposes
572
     */
573
    public int getId()
574
    {
575
        return mId;
576
    }
577
 
578
    /**
579
     * accessor for unit test purposes
580
     */
581
    public String getVersion()
582
    {
583
        return mVersion;
584
    }
585
 
7082 dpurdie 586
    /**
587
     * accessor for unit test purposes
6914 dpurdie 588
     */
7082 dpurdie 589
    public String getNextVersion()
6914 dpurdie 590
    {
7082 dpurdie 591
        return mNextVersion;
6914 dpurdie 592
    }
593
 
594
 
595
    /**
596
     * returns true if mBuildStandardCollection is not empty
597
     */
598
    boolean isReproducible()
599
    {
7082 dpurdie 600
        mLogger.debug("isReproducible on Package {}", mName);
6914 dpurdie 601
        boolean retVal = ! mBuildStandardCollection.isEmpty();
7176 dpurdie 602
        mLogger.debug("isReproducible returned {}", retVal);
6914 dpurdie 603
        return retVal;
604
    }
605
 
606
 
607
    /**
608
     * returns true if at least one of its BuildStandards has mGeneric true
609
     */
610
    boolean isGeneric()
611
    {
7082 dpurdie 612
        mLogger.debug("isGeneric on Package {}", mName);
6914 dpurdie 613
        boolean retVal = false;
614
        for (Iterator<BuildStandard> it = mBuildStandardCollection.iterator(); it.hasNext();)
615
        {
616
            BuildStandard buildStandard = it.next();
617
 
618
            if (buildStandard.isGeneric())
619
            {
620
                retVal = true;
621
                break;
622
            }
623
        }
624
 
7176 dpurdie 625
        mLogger.debug("isGeneric returned {}", retVal);
6914 dpurdie 626
        return retVal;
627
    }
628
 
629
    /**
630
     * Returns true if at least one of the packages build standards can be 
631
     * built in the named machine class. 
632
     *  
633
     * Used to determine if the package can be built with the current buildset 
634
     * by iteration over all machine classes in the buildset. 
635
     */
636
    boolean canBeBuildby(String machineClass)
637
    {
638
        mLogger.debug("canBeBuildby on Package " + mName);
639
        boolean retVal = false;
640
        for (Iterator<BuildStandard> it = mBuildStandardCollection.iterator(); it.hasNext();)
641
        {
642
            BuildStandard buildStandard = it.next();
643
 
644
            if (buildStandard.isGeneric())
645
            {
646
                retVal = true;
647
                break;
648
            }
649
 
650
            if (buildStandard.mMachClass.equals(machineClass))
651
            {
652
                retVal = true;
653
                break;
654
            }
655
        }
656
 
657
        mLogger.info("canBeBuildby returned " + retVal);
658
        return retVal;
659
    }
660
 
661
    /**
662
     * Compare the build standards of two packages 
663
     * Used only in escrow mode 
664
     * Used to compare a package and its dependents (one by one) 
665
     *  
666
     * Returns true if the parent (this) package  can be built in the same escrow 
667
     * build iteration as the dependent package. 
668
     *  
669
     * This is a complex decision and has a few built in assumptions. 
670
     *  
671
     * If the dependent package is 'generic' then the parent package can be built 
672
     *  
673
     * If the parent package is generic then is can only be built if the dependent is 
674
     * also generic. Otherwise we must assume that the parent package is an agregator 
675
     * package and requires artifacts from the dependent package built by all of its 
676
     * required build machines. 
677
     *  
678
     * If both packages are not generic, then if the build standards of the parent and the 
679
     * dependent are identical then 
680
     *  
681
     *      If we have one build standard, we can build the parent in this iteration as
682
     *      the dependent package has been completely built.
683
     *  
684
     *      If we have more than one build standard ( but they are identical ) then we
685
     *      ASSUME that we can build the parent in this iteration. The assumption is that
686
     *      there is no mixing between build machines. ie: Windows consumer users Windows
687
     *      artifacts and the Linux consumer uses Linux artifacts ...
688
     *  
689
     * If both packages are not generic and the build standards are not identical, then 
690
     * things get hard. The safest solution is to assume the parent cannot be built in this 
691
     * iteration. This is not a bad assumption. 
692
     *  
693
     */
694
    boolean haveSameBuildStandards(Package d)
695
    {
696
        HashMap<String, Boolean> standardSet = new HashMap<String, Boolean>();
697
        boolean isGeneric = false;
698
        boolean isGeneric_d = false;
699
        boolean isIdentical = true;
700
 
701
        // Scan the build standards of the parent package and create a hash map for each machine
702
        // class required by the parent. Also determine if the parent is generic.
703
        for (Iterator<BuildStandard> it = mBuildStandardCollection.iterator(); it.hasNext();)
704
        {
705
            BuildStandard bs = it.next();
706
            standardSet.put(bs.mMachClass, false);
707
 
708
            if (bs.isGeneric())
709
            {
710
                isGeneric = true;
711
                break;
712
            }
713
        }
714
 
715
        // Scan the build standards in the dependent package and remove items from the map
716
        // If it was not in the map then the dependent package builds for platforms that the
717
        // parent package does not - thus the build standards cannot be identical
718
        // Also determine if the dependent is generic.
719
        for (Iterator<BuildStandard> it = d.mBuildStandardCollection.iterator(); it.hasNext();)
720
        {
721
            BuildStandard bs = it.next();
722
             if (bs.isGeneric())
723
             {
724
             isGeneric_d = true;
725
             break;
726
             }
727
 
728
            Boolean value = standardSet.remove(bs.mMachClass);
729
            if (value == null)
730
            {
731
                isIdentical = false;
732
                break;
733
            }
734
        }
735
 
736
        //
737
        // If there are any items left in the map then the parent package builds on machines
738
        // that the dependent does not. The two are not identical.
739
 
740
        if (!standardSet.isEmpty())
741
        {
742
            isIdentical = false;
743
        }
744
 
745
        // If dependent is generic, then it will be build on the first platform in this iteration
746
        // All is good
747
 
748
        if( isGeneric_d)
749
            return true;
750
 
751
        //  If I am generic and the dependent is generic, then I can be built at this time
752
        // 
753
        //  Will not reach here as we have already said that if dependenbt is generic, then all is good
754
        //  if (isGeneric_d && isGeneric)
755
        //  {
756
        //      return true;
757
        //  }
758
 
759
 
760
        //  If I am generic then I must wait for ALL dependent to be built on all platforms
761
        //  Thus I can't be built this round
762
        //
763
        if (isGeneric)
764
            return false;
765
 
766
        //
767
        //  If the two sets of build standards don't have generic, BUT are identical
768
        //  the we can assume that there is no cross breading. and that windows bits build from windows bits
769
        //  and solaris bits build from solaris
770
        //
771
        if (isIdentical)
772
            return true;
773
 
774
        //  The two sets of build standards are not an exact match
775
        //  Cheap solution: Assume that I can't be built this round
776
        //  Possible solution: If I am a subset of the dependent - then I can be built
777
        //                     If I am a superset of the dependent - then ???
778
        return false;
779
 
780
    }
781
 
782
    /**
7082 dpurdie 783
     * Applies the required version number change. Will calculate mNextVersion
784
     * while not changing mVersion.
6914 dpurdie 785
     * 
786
     * @param releaseManager    Release Manager instance to work against
787
     * 
7082 dpurdie 788
     * @return 0 : success
789
     *         1 : cannot work with non standard versioning
790
     *         2 : ripple field limitations prevent a ripple build
791
     *         3 : Invalid Change Type
6914 dpurdie 792
     * @exception Exception
793
     */
7082 dpurdie 794
 
795
    int applyPV(ReleaseManager releaseManager) throws Exception
6914 dpurdie 796
    {
797
        String logInfo = "applyPV," + mName;
7099 dpurdie 798
        String changeType = "";
7176 dpurdie 799
        mLogger.warn("applyPV on Package {}", mName);
7082 dpurdie 800
 
6914 dpurdie 801
        //
7082 dpurdie 802
        //  This method used to actually perform the version change
803
        //  Now it just calculates the potential value
804
        //  Must not alter mVersion as it will be used if the package is not selected to be built,
805
        //  but some of the code assumes that it can be.
806
        //
807
        String originalVersion = mVersion;
808
 
809
        //
6914 dpurdie 810
        // Four scenarios, only applyPV for 3 of them
811
        // mDirectlyPlanned mIndirectlyPlanned mArchivalExistence mForcedRipple
812
        // Action
813
        // WIP/test build exists: true true don't care don't care applyPV
814
        // Package version is out of date: false true true don't care applyPV
815
        // Forced ripple: false true don't care > 0 applyPV
816
        // Package version does not exist: false true false = 0 do not applyPV
817
        //
818
        if (!mDirectlyPlanned && mIndirectlyPlanned && !mArchivalExistence && mForcedRippleInstruction == 0)
819
        {
820
            // the package has an mIndirectlyPlanned flag set true in daemon
821
            // mode because the package does not exist in an archive
822
            // do not apply a different package version
7176 dpurdie 823
            mLogger.warn("applyPV. Rebuild Package {}", mName);
6914 dpurdie 824
            mLogger.info("applyPv returned 0");
825
            return 0;
826
        }
827
 
828
        // override - no longer doing a rebuild - version number change from this point on
829
        if (mTestBuildInstruction == 0)
830
        {
831
            mRequiresSourceControlInteraction = true;
832
        }
833
 
834
        //	Force test builds to use a sensible version number
835
        if (mTestBuildInstruction > 0 )
836
        {
837
        	mChangeType.resetData();		// Resolve conflict via build numbers
838
        	mRippleField.setBuild();
839
        	mVersion = "99.99.98999";		// Such that rippling build number will goto 99.99.99000
840
        }
841
 
842
        //
843
        // Detect invalid change type
844
        // Flagged when package instance is created
845
        //
846
        if (mChangeType.mUnknown)
847
        {
7176 dpurdie 848
            mLogger.warn("Package Version unknown on Package {} Version: {}", mName, mVersion);
6914 dpurdie 849
            mLogger.info("applyPv returned 3");
850
            return 3;
851
        }
852
 
853
        // If we are not calculating the new package version because the user
854
        // has fixed the version of the package. We are given the new package version.
855
        if (mChangeType.mFixed)
856
        {
857
            // mVersion is already setup
858
 
7082 dpurdie 859
            mNextVersion = mFixedVersion;
7176 dpurdie 860
            mLogger.warn("Package Version specified on Package {} New Version: {}", mName,  mNextVersion);
6914 dpurdie 861
            mLogger.info("applyPv returned 0");
862
            return 0;
863
        }
864
 
865
        // We need to calculate the new version number
866
        //
867
        MutableInt major = new MutableInt(0);
868
        MutableInt minor = new MutableInt(0);
869
        MutableInt patch = new MutableInt(1000);
870
 
871
        // Planned packages have a previous version number to be used as the bases for the
872
        // calculation. Ripples won't.
873
        //
874
        if (mPrevVersion != null)
875
        {
876
        	mVersion = mPrevVersion;
877
        }
878
        String[] field = mVersion.split("\\D");
879
        String nonStandardCotsVersion = "";
880
        logInfo += ", Prev:" + mVersion;
881
 
882
        if (field.length == 3)
883
        {
884
            major.value = Integer.parseInt(field[0]);
885
            minor.value = Integer.parseInt(field[1]);
886
            patch.value = Integer.parseInt(field[2]);
887
        } 
888
        else
889
        {
890
            //
891
            // Can ripple a .cots package under very controlled conditions
892
            // Its ends with a .patchBuild field
893
            // Package is marked as ripple via build number
894
            // Change type of Major and Minor are not allowed
895
            //
7082 dpurdie 896
            if (!mChangeType.mMajor && !mChangeType.mMinor && mRippleField.mBuild && mExtension.compareTo(".cots") == 0 && field.length > 0)
6914 dpurdie 897
            {
898
                // allow and work with (ripple build) versions a.b.c.d....xxxx
899
                // where xxxx.length > 3
900
                String patchStr = field[field.length - 1];
901
                int patchLen = patchStr.length();
902
 
903
                // check patchStr is the last (at least 4) digits
904
                if (patchLen > 3
905
                        && mVersion.substring(mVersion.length() - patchLen, mVersion.length()).compareTo(patchStr) == 0)
906
                {
907
                    patch.value = Integer.parseInt(patchStr);
908
                    nonStandardCotsVersion = mVersion.substring(0, mVersion.length() - patchLen);
909
                }
910
            }
911
 
912
            if (nonStandardCotsVersion.length() == 0)
913
            {
914
                // cannot work with non standard versioning
7176 dpurdie 915
                mLogger.warn("applyPV cannot work with non standard versioning");
6914 dpurdie 916
                mLogger.info("applyPv returned 1");
917
                return 1;
918
            }
919
        }
920
 
921
        if (nonStandardCotsVersion.length() == 0 && patch.value < 1000 && field[2].substring(0, 1).compareTo("0") != 0)
922
        {
7082 dpurdie 923
            mLogger.info("applyPV accomodate old style Version of the form 1.0.1");
6914 dpurdie 924
            patch.value = patch.value * 1000;
925
        }
926
 
927
        // mChangeType overrides mRippleField
928
        do
929
        {
930
            if (mChangeType.mMajor)
931
            {
7099 dpurdie 932
                changeType = ",CT Major";
6914 dpurdie 933
                if (!incrementFieldsAccordingToLimits(4, major, minor, patch))
934
                {
935
                    mLogger.info("applyPv returned 2");
7082 dpurdie 936
                    mVersion = originalVersion;
6914 dpurdie 937
                    return 2;
938
                }
939
            } else if (mChangeType.mMinor)
940
            {
7099 dpurdie 941
                changeType = ",CT Minor";
6914 dpurdie 942
                if (!incrementFieldsAccordingToLimits(3, major, minor, patch))
943
                {
944
                    mLogger.info("applyPv returned 2");
7082 dpurdie 945
                    mVersion = originalVersion;
6914 dpurdie 946
                    return 2;
947
                }
948
            } else if (mChangeType.mPatch)
949
            {
7099 dpurdie 950
                changeType = ",CT Patch";
6914 dpurdie 951
                if (!incrementFieldsAccordingToLimits(2, major, minor, patch))
952
                {
953
                    mLogger.info("applyPv returned 2");
7082 dpurdie 954
                    mVersion = originalVersion;
6914 dpurdie 955
                    return 2;
956
                }
957
            } else
958
            {
959
                if (mRippleField.mMajor)
960
                {
7099 dpurdie 961
                    changeType = ",R Major";
6914 dpurdie 962
                    major.value++;
7082 dpurdie 963
                    mLogger.info("applyPV mRippleField.mMajor {}", major.value);
6914 dpurdie 964
                    minor.value = 0;
965
                    patch.value = 0;
966
                } else if (mRippleField.mMinor)
967
                {
7099 dpurdie 968
                    changeType = ",R Minor";
6914 dpurdie 969
                    minor.value++;
7082 dpurdie 970
                    mLogger.info("applyPV mRippleField.mMinor {}", minor.value);
6914 dpurdie 971
                    patch.value = 0;
972
                } else if (mRippleField.mPatch)
973
                {
7099 dpurdie 974
                    changeType = ",R Patch";
6914 dpurdie 975
                    patch.value = ((patch.value / 1000) + 1) * 1000; 
7082 dpurdie 976
                    mLogger.info("applyPV mRippleField.mPatch {}", patch.value);
6914 dpurdie 977
                } else if (mRippleField.mBuild)
978
                {
7099 dpurdie 979
                    changeType = ", R Build";
6914 dpurdie 980
                    patch.value++;
7082 dpurdie 981
                    mLogger.info("applyPV mRippleField.mBuild {}", patch.value);
6914 dpurdie 982
                } else
983
                {
984
                    if (!incrementFieldsAccordingToLimits(1, major, minor, patch))
985
                    {
986
                        mLogger.info("applyPv returned 2");
7082 dpurdie 987
                        mVersion = originalVersion;
6914 dpurdie 988
                        return 2;
989
                    }
990
                }
991
            }
992
 
993
            if (nonStandardCotsVersion.length() == 0)
994
            {
995
                mVersion = String.valueOf(major.value) + "." + String.valueOf(minor.value) + ".";
996
            } else
997
            {
998
                mVersion = nonStandardCotsVersion;
999
            }
1000
 
1001
            if (patch.value < 10)
1002
            {
1003
                mVersion += "000";
1004
            } else if (patch.value < 100)
1005
            {
1006
                mVersion += "00";
1007
            } else if (patch.value < 1000)
1008
            {
1009
                mVersion += "0";
1010
            }
1011
 
1012
            mVersion += String.valueOf(patch.value);
7082 dpurdie 1013
        } while (exists(releaseManager));
6914 dpurdie 1014
 
7099 dpurdie 1015
        logInfo += changeType + ", Next Version:" + mVersion;
7176 dpurdie 1016
        mLogger.warn(logInfo);
6914 dpurdie 1017
        mLogger.info("applyPv returned 0");
7082 dpurdie 1018
        mNextVersion = mVersion;
1019
        mVersion = originalVersion;
6914 dpurdie 1020
        return 0;
1021
    }
1022
 
1023
    /**
1024
     * increments fields according to mRippleField.mLimit if necessary will
1025
     * apply it to the field passed as follows 1 = build 2 = patch 3 = minor
1026
     * other = major returns true on success false on ripple field limitations
1027
     * prevent a ripple build
1028
     */
1029
    private boolean incrementFieldsAccordingToLimits(int field, MutableInt major, MutableInt minor, MutableInt patch)
1030
    {
1031
        boolean retVal = true;
1032
 
1033
        if (!mChangeType.mLimit && !mRippleField.mLimit)
1034
        {
1035
            // simple case
1036
            // no need to take field limits into consideration
1037
            switch (field)
1038
            {
1039
            case 1:
1040
                // unreachable
1041
                // the only scenario involving build number manipulation
1042
                // involves the mRippleField.mLimit being set
1043
                retVal = false;
1044
                break;
1045
            case 2:
1046
                do
1047
                {
1048
                    patch.value++;
1049
                } while ((patch.value / 1000) * 1000 != patch.value);
1050
                mLogger.info("incrementFieldsAccordingToLimits patch " + patch.value);
1051
                break;
1052
            case 3:
1053
                minor.value++;
1054
                mLogger.info("incrementFieldsAccordingToLimits minor " + minor.value);
1055
                patch.value = 0;
1056
                break;
1057
            default:
1058
                major.value++;
1059
                mLogger.info("incrementFieldsAccordingToLimits major " + major.value);
1060
                minor.value = 0;
1061
                patch.value = 0;
1062
            }
1063
        } else
1064
        {
1065
            // take field limits into consideration
1066
            boolean changeOccurred = false;
1067
            boolean incrementField = true;
1068
 
1069
            switch (field)
1070
            {
1071
            case 1:
1072
                if (mBuildLimit != 0)
1073
                {
1074
                    // increment or reset the patch build number
1075
                    int buildNumber = patch.value - (patch.value / 1000) * 1000;
1076
 
1077
                    if (buildNumber < mBuildLimit)
1078
                    {
1079
                        // can increment the patch build number
1080
                        patch.value++;
1081
                        mLogger.info("incrementFieldsAccordingToLimits mRippleField.mLimit build number " + patch.value);
1082
                        changeOccurred = true;
1083
                        incrementField = false;
1084
                    } else
1085
                    {
1086
                        if (mPatchLimit == 0)
1087
                        {
1088
                            // reset the patch number and patch build number
1089
                            patch.value = 0;
1090
                        }
1091
                    }
1092
                }
1093
                // no break by design
1094
            case 2:
1095
                if (mPatchLimit != 0 && incrementField)
1096
                {
1097
                    // increment or reset the patch number
1098
                    if ((patch.value / 1000) < mPatchLimit)
1099
                    {
1100
                        do
1101
                        {
1102
                            patch.value++;
1103
                        } while ((patch.value / 1000) * 1000 != patch.value);
1104
 
1105
                        mLogger.info("incrementFieldsAccordingToLimits mRippleField.mLimit patch " + patch.value);
1106
                        changeOccurred = true;
1107
                        incrementField = false;
1108
                    } else
1109
                    {
1110
                        // reset the patch number and patch build number
1111
                        patch.value = 0;
1112
                    }
1113
                }
1114
                // no break by design
1115
            case 3:
1116
                if (mMinorLimit != 0 && incrementField)
1117
                {
1118
                    // increment or reset the minor number
1119
                    if (minor.value < mMinorLimit)
1120
                    {
1121
                        minor.value++;
1122
                        patch.value = 0;
1123
                        mLogger.info("incrementFieldsAccordingToLimits mRippleField.mLimit minor " + minor.value);
1124
                        changeOccurred = true;
1125
                        incrementField = false;
1126
                    } else
1127
                    {
1128
                        // reset the minor number
1129
                        minor.value = 0;
1130
                    }
1131
                }
1132
                // no break by design
1133
            default:
1134
                if (mMajorLimit != 0 && incrementField)
1135
                {
1136
                    // increment or reset the major number
1137
                    if (major.value < mMajorLimit)
1138
                    {
1139
                        // increment the major number
1140
                        changeOccurred = true;
1141
                        major.value++;
1142
                        minor.value = 0;
1143
                        patch.value = 0;
1144
                        mLogger.info("incrementFieldsAccordingToLimits mRippleField.mLimit major " + major.value);
1145
                    }
1146
                }
1147
            }
1148
 
1149
            if (!changeOccurred)
1150
            {
1151
                // unable to increment a field due to field limitations
7176 dpurdie 1152
                mLogger.warn("incrementFieldsAccordingToLimits ripple field limitations prevent a ripple build");
6914 dpurdie 1153
                mLogger.info("incrementFieldsAccordingToLimits returned false");
1154
                retVal = false;
1155
            }
1156
        }
1157
 
1158
        return retVal;
1159
    }
1160
 
1161
    /**
7082 dpurdie 1162
     * Check if a specified Version of the package exists in dpkg_archive or the Release Manager Database
6914 dpurdie 1163
     * 
1164
     * @param releaseManager Release Manager Instance
1165
     * 
1166
     * @return True if the Package Version exists within the Release Manager Database
1167
     * @exception Exception
1168
     */
7082 dpurdie 1169
    private boolean exists(ReleaseManager releaseManager) throws Exception
6914 dpurdie 1170
    {
1171
        mLogger.debug("exists on Package " + mName + " version " + mVersion + " extension " + mExtension);
1172
        boolean retVal = false;
1173
 
7046 dpurdie 1174
        if (!releaseManager.mUseDatabase)
6914 dpurdie 1175
        {
1176
            mLogger.info("exists !releaseManager.mUseDatabase");
1177
        }
1178
        else
1179
        {
1180
            //  Check Package Archive
1181
            retVal = existsInDpkgArchive();
1182
            if (!retVal)
1183
            {
1184
                //  Check Release Manager Database
1185
                retVal = releaseManager.queryPackageVersions(mPid, mVersion + mExtension);
1186
            }
1187
        }
1188
 
1189
        mLogger.info("exists returned " + retVal);
1190
        return retVal;
1191
    }
1192
 
1193
 
1194
    /**
1195
     * Check to see if a package exists in dpkg_archive
1196
     * 
1197
     * @return true if the version exists in dpkg_archive
1198
     * @exception Exception Thrown if dpkg_archive does not exist. The 'cause' of 'mRecoverable' is special and
1199
     *                      will be trapped later to determine if this is a recoverable exception.
1200
     */
1201
    boolean existsInDpkgArchive() throws Exception
1202
    {
1203
        mLogger.debug("existsInDpkgArchive on " + mName);
1204
        boolean retVal = false;
1205
        String name = utilities.catDir(mName, mVersion + mExtension );
1206
 
1207
        //  If a replica exists, then check it first
1208
        //  If we are configured with a replica its because access to the main archive is slow
1209
        //  and we want this check to be fast
1210
        //
1211
        if (mGbeDpkgReplica != null && mGbeDpkgReplica.length() > 0)
1212
        {
1213
            File dpkg = new File(mGbeDpkgReplica);
1214
            if (!dpkg.exists())
1215
            {
7176 dpurdie 1216
                mLogger.error("existsInDpkgArchive. mGbeDpkgReplica not accessable. {}", mRecoverable);
6914 dpurdie 1217
                throw new Exception(mRecoverable);
1218
            }
1219
 
1220
            if( utilities.freshFileExists(utilities.catDir(mGbeDpkgReplica, name) ) )
1221
            {
1222
                mLogger.info("existsInDpkgArchive mGbeDpkgReplica");
1223
                retVal = true;
1224
            }
1225
        }
1226
 
1227
        //  Check (possibly remote) dpkg_archive for files existence if it was not found locally
1228
        //
1229
        if ( !retVal )
1230
        {
1231
 
1232
            //  If the package archive does not exist at the moment, then we have a network issue
1233
            //  This is a recoverable error
1234
 
1235
            File dpkg = new File(mGbeDpkg);
1236
            if (!dpkg.exists())
1237
            {
7176 dpurdie 1238
                mLogger.error("existsInDpkgArchive. mGbeDpkg not accessable. {}", mRecoverable);
6914 dpurdie 1239
                throw new Exception(mRecoverable);
1240
            }
1241
 
1242
            if( utilities.freshFileExists(utilities.catDir(mGbeDpkg, name) ) )
1243
            {
1244
                mLogger.info("existsInDpkgArchive mGbeDpkg");
1245
                retVal = true;
1246
            }
1247
        }
1248
 
1249
        mArchivalExistence = retVal;
7176 dpurdie 1250
        mLogger.debug("existsInDpkgArchive returned {}", retVal);
6914 dpurdie 1251
        return retVal;
1252
    }
1253
 
1254
    /**
1255
     * returns true if the required package archives (dpkg_archive) exist
1256
     * attempt to recover from their transient loss
1257
     */
1258
    public static boolean recover()
1259
    {
1260
        mLogger.debug("recover");
1261
        boolean retVal = false;
1262
 
1263
        String Release = mGbeDpkg;
1264
        if (Release != null)
1265
        {
1266
            if ( utilities.freshFileExists(mGbeDpkg) )
1267
            {
1268
                retVal = true;
7033 dpurdie 1269
                mLogger.error("recover: dpkg_archive access has been restored");
6914 dpurdie 1270
            }
1271
        }
1272
 
7176 dpurdie 1273
        mLogger.debug("recover returned {}", retVal);
6914 dpurdie 1274
        return retVal;
1275
    }
1276
 
1277
    /**
1278
     * Returns a data structure of unique email addresses
1279
     * Uses the Global Email Collection and the packages own failure email collection
1280
     */
1281
    private LinkedHashSet<String>buildEmailList(RippleEngine rippleEngine)
1282
    {
1283
        //  Create a single list of email targets ensuring only one instance of each email address
1284
        //  ie: Remove duplicates, null and empty strings
1285
        LinkedHashSet<String> hs = new LinkedHashSet<String>();
1286
 
1287
        // Global and Project Wide emails
1288
        for (Iterator<String> it = rippleEngine.mMailGlobalCollection.iterator(); it.hasNext();)
1289
        {
1290
            String item = it.next();
1291
            if (item != null && item.length() > 0) 
1292
            {
1293
                hs.add(item);
1294
            }
1295
        }
1296
 
1297
        // Package specific collection
1298
        for (Iterator<String> it = mBuildFailureEmailCollection.iterator(); it.hasNext();)
1299
        {
1300
            String item = it.next();
1301
            if (item != null && item.length() > 0) 
1302
            {
1303
                hs.add(item);
1304
            }
1305
        }
1306
 
1307
        return hs;
1308
    }
1309
 
1310
    /**
1311
     * Add email information in a form suitable for creating an Ant file
1312
     * @param   rippleEngine    - Ripple Engine Instance
1313
     * @param   xml             - An XmlBuilder element to extend
1314
     */
1315
    void emailInfo(RippleEngine rippleEngine, XmlBuilder xml)
1316
    {
1317
 
1318
        //  Create a single list of email targets ensuring only one instance of each email address
1319
        //  ie: Remove duplicates
1320
        LinkedHashSet<String> hs = buildEmailList(rippleEngine);
1321
 
1322
        for (Iterator<String> it = hs.iterator(); it.hasNext();)
1323
        {
1324
            String email = it.next();
1325
            XmlBuilder entry = xml.addNewElement("owner");
1326
            entry.addAttribute("email", email);
1327
        }
1328
    }
1329
 
1330
    /**
1331
     * Returns email information in a form suitable for direct use
1332
     * @param   rippleEngine Current Release Manager context
1333
     * @return  A comma separated list of user names. May return a 'null' String
1334
     */
1335
    String emailInfoNonAntTask(RippleEngine rippleEngine)
1336
    {
1337
        //  Create a single list of email targets ensuring only one instance of each email address
1338
        //  ie: Remove duplicates
1339
        LinkedHashSet<String> hs = buildEmailList(rippleEngine);
1340
 
1341
        String retVal = null;
1342
        for (Iterator<String> it = hs.iterator(); it.hasNext();)
1343
        {
1344
            String email = it.next();
1345
 
1346
            if (retVal == null)
1347
            {
7155 dpurdie 1348
                retVal = "";
6914 dpurdie 1349
            } else
1350
            {
1351
                retVal += ",";
1352
            }
1353
            retVal += email;
1354
        }
1355
 
1356
        return retVal;
1357
    }
1358
 
1359
    /**
1360
     * Adds email to mBuildFailureEmailCollection.
1361
     * Do not worry about multiple entries. These will be handled when the data is extracted
7082 dpurdie 1362
     * @param   email   - Email address. Null is allowed and will not be added
6914 dpurdie 1363
     */
1364
    public void addEmail(String email)
1365
    {
7082 dpurdie 1366
        if (email != null) 
6914 dpurdie 1367
        {
7082 dpurdie 1368
            mBuildFailureEmailCollection.add(email);
6914 dpurdie 1369
        }
1370
    }
1371
 
1372
    /**
7082 dpurdie 1373
     * Returns true if the package is a part of a circular dependency within the provided collection
6914 dpurdie 1374
     * 
1375
     * If the package depends on a package with a circular dependency then the function
1376
     * will return false.
1377
     */
7082 dpurdie 1378
    public boolean hasCircularDependency(ArrayList<Package> packageCollection)
6914 dpurdie 1379
    {
7082 dpurdie 1380
        mLogger.debug("hasCircularDependency: {}", mAlias);
1381
        boolean retVal = detectCircularDependency(mAlias, packageCollection, null);
7176 dpurdie 1382
        mLogger.debug("hasCircularDependency returned {} ", retVal);
6914 dpurdie 1383
        return retVal;
1384
    }
7082 dpurdie 1385
 
1386
    /** Reset the circular dependency testing information
1387
     * 
1388
     * @param packageCollection - Collection of packages to process
1389
     */
1390
    public static void resetCircularDependency(ArrayList<Package> packageCollection)
1391
    {
1392
        for (Iterator<Package> it = packageCollection.iterator(); it.hasNext(); )
1393
        {
1394
            Package p = it.next();
1395
            p.mCheckedCircularDependency = false;
1396
            p.mHasCircularDependency = false;
1397
            p.mBreadCrumb = 0;
1398
        }
1399
    }
6914 dpurdie 1400
 
1401
    /**
1402
     * Returns true is a part of a circular dependency
1403
     * Will examine all the packages sub dependencies and mark those that do have a
1404
     * circular dependency.
1405
     * 
1406
     * This process works by descending the dependency tree and dropping a bread crumb
1407
     * If the bread crumb is seen during the decent, then a circle has been detected and
1408
     * the package (with the bread crumb) will be marked as having a circular dependency
1409
     * 
1410
     *  Assumes that the caller will walk ALL packages and flag those with a circular
1411
     *  dependence AND those that depend on that package.
1412
     * 
1413
     */
7082 dpurdie 1414
    private boolean detectCircularDependency(String alias, ArrayList<Package> packageCollection, Package parent)
6914 dpurdie 1415
    {
1416
        mLogger.debug("detectCircularDependency");
1417
        boolean retVal = false;
1418
 
1419
        // if this package has yet to be checked for circular dependency
1420
        if (!mCheckedCircularDependency)
1421
        {
1422
            // Will be set as we drill down through dependencies
1423
            // If we see this marker (bread crumb) then we have a loop
1424
            if (mBreadCrumb != 0)
1425
            {
1426
                mBreadCrumb = 3;
1427
 
1428
                mHasCircularDependency = true;
1429
                if(parent != null && parent.mBreadCrumb != 3 )
1430
                {
1431
                    parent.mBreadCrumb = 2;
1432
                }
1433
            }
1434
            else
1435
            {
1436
                // Mark this package as potentially having a circular dependency
1437
                // Will now drill down and see if we hit a marker
1438
                mBreadCrumb = 1;
1439
 
1440
                // Recurse down the dependencies and sub dependencies
7163 dpurdie 1441
                for (Iterator<PkgDependency> it2 = mDependencyCollection.iterator(); it2.hasNext();)
6914 dpurdie 1442
                {
7163 dpurdie 1443
                    PkgDependency depEntry = it2.next();
7082 dpurdie 1444
 
1445
                    // Scan the collection looking for the specified package alias
1446
                    for (Iterator<Package> it = packageCollection.iterator(); it.hasNext();)
1447
                    {
1448
                        Package dependency = it.next();
1449
 
7163 dpurdie 1450
                        if ( dependency.mAlias.compareTo( depEntry.alias ) == 0 )
7082 dpurdie 1451
                        {
1452
                            dependency.detectCircularDependency(alias, packageCollection, this);
1453
                            break;
1454
                        }
1455
                    }
6914 dpurdie 1456
                }
1457
 
1458
                if (mBreadCrumb == 2)
1459
                {
1460
                    mHasCircularDependency = true;
1461
                    if(parent != null && parent.mBreadCrumb != 3 )
1462
                    {
1463
                        parent.mBreadCrumb = 2;
1464
                    }
1465
                }
1466
                mBreadCrumb = 0;
1467
            }
1468
 
1469
            // Flag package as having been examined
1470
            mCheckedCircularDependency = true;
1471
        } 
1472
 
1473
        // return the persisted circular dependency outcome
1474
        retVal = mHasCircularDependency;
7082 dpurdie 1475
        mLogger.info("detectCircularDependency 2 returned {}", retVal);
6914 dpurdie 1476
        return retVal;
1477
    }
7082 dpurdie 1478
 
1479
    /** Set the sequence of all packages in the list
1480
     *  Used so that the Unit Tests function can preserve the basic ordering of the list 
1481
     */
1482
    public static void setSequence(ArrayList<Package> al)
1483
    {
1484
        int seq = 1;
1485
        for (Iterator<Package> it = al.iterator(); it.hasNext(); )
1486
        {
1487
            Package p = it.next();
1488
            p.mSeqId = seq++;
1489
        }
1490
    }
6914 dpurdie 1491
 
7082 dpurdie 1492
    /** Reset the processed flag on a collection of packages
1493
     * 
1494
     */
1495
    public static void resetProcessed (ArrayList<Package> al)
1496
    {
1497
        for (Iterator<Package> it = al.iterator(); it.hasNext(); )
1498
        {
1499
            Package p = it.next();
1500
            p.mIsProcessed = false;
1501
            p.mProcessed = false;
1502
            if (p.mBuildFile > 0)
1503
                p.mBuildFile = 0;
1504
        }
1505
    }
1506
 
1507
    /** Comparator for sorting package collections by mSeqId
1508
     *  Used to preserve order for unit testing
1509
     */
1510
    public static final Comparator<Package> SeqComparator = new Comparator<Package>() {
1511
 
1512
        /**
1513
         * Returns -ve: p1 is less than p2
1514
         *           0: p1 = p2
1515
         *         +ve: p1 > p2
1516
         */
1517
        public int compare (Package p1, Package p2) {
1518
                return p1.mSeqId - p2.mSeqId;
1519
        }
1520
    };
1521
 
6914 dpurdie 1522
    /**
1523
     * entity class supporting the ERG version numbering standard:
1524
     * <major>.<minor>.<patch/build> patch/build is at least a 4 digit number
1525
     * whose last 3 digits represent the build
1526
     */
1527
    public class VersionNumberingStandard
1528
    {
1529
        /**
1530
         * in terms of the mChangeType Package field, when true indicates the
1531
         * contract of the package has changed in a non backwardly compatible
1532
         * manner in terms of the mRippleField Package field, when true indicates
1533
         * the major version number will be incremented
1534
         * 
1535
         * @attribute
1536
         */
1537
        private boolean mMajor = false;
1538
 
1539
        /**
1540
         * in terms of the mChangeType Package field, when true indicates the
1541
         * contract of the package has changed in a backwardly compatible manner
1542
         * in terms of the mRippleField Package field, when true indicates the
1543
         * minor version number will be incremented
1544
         * 
1545
         * @attribute
1546
         */
1547
        private boolean mMinor = false;
1548
 
1549
        /**
1550
         * in terms of the mChangeType Package field, when true indicates the
1551
         * contract of the package has not changed, but the package has changed
1552
         * internally in terms of the mRippleField Package field, when true
1553
         * indicates the minor version number will be incremented
1554
         * 
1555
         * @attribute
1556
         */
1557
        private boolean mPatch = false;
1558
 
1559
        /**
1560
         * in terms of the mChangeType Package field, when true indicates the
1561
         * package has not changed, its dependencies potentially have in terms
1562
         * of the mRippleField Package field, when true indicates the build
1563
         * number will be incremented
1564
         * 
1565
         * @attribute
1566
         */
1567
        private boolean mBuild = true;
1568
 
1569
        /**
1570
         * in terms of the mChangeType Package field, when true indicates the
1571
         * major, minor, and patch number will be incremented according to field
1572
         * limits in terms of the mRippleField Package field, when true indicates
1573
         * the major, minor, patch and build number will be incremented
1574
         * according to field limits
1575
         * 
1576
         * @attribute
1577
         */
1578
        private boolean mLimit = false;
1579
 
1580
        /**
1581
         * in terms of the mChangeType Package field, when true indicates the
1582
         * package version number will not be rippled. The user will have fixed
1583
         * the version number. This is only application to WIP packages
1584
         * 
1585
         * @attribute
1586
         */
1587
        private boolean mFixed = false;
1588
 
1589
        /**
1590
         * in terms of the mChangeType Package field, when true indicates the
1591
         * method of rippling a package version number is not known.
1592
         * 
1593
         * @attribute
1594
         */
1595
        private boolean mUnknown = false;
1596
 
1597
        /**
1598
         * constructor
1599
         */
1600
        private VersionNumberingStandard()
1601
        {
1602
            mLogger.debug("VersionNumberingStandard");
1603
        }
1604
 
1605
        /**
1606
         * Reset all values to a known state
1607
         * 
1608
         */
1609
        void resetData()
1610
        {
1611
            mBuild = false;
1612
            mMajor = false;
1613
            mMinor = false;
1614
            mPatch = false;
1615
            mLimit = false;
1616
            mFixed = false;
1617
            mUnknown = false;
1618
        }
1619
 
1620
        /**
1621
         * sets mBuild true, mMajor false, mMinor false, mPatch false, mLimit
1622
         * false
1623
         */
1624
        void setBuild()
1625
        {
1626
            mLogger.debug("setBuild");
1627
            resetData();
1628
            mBuild = true;
1629
        }
1630
 
1631
        /**
1632
         * sets mBuild false, mMajor true, mMinor false, mPatch false, mLimit
1633
         * false
1634
         */
1635
        void setMajor()
1636
        {
1637
            mLogger.debug("setMajor");
1638
            resetData();
1639
            mMajor = true;
1640
        }
1641
 
1642
        /**
1643
         * sets mBuild false, mMajor true, mMinor false, mPatch false, mLimit
1644
         * limit
1645
         */
1646
        void setMajor(boolean limit)
1647
        {
1648
            mLogger.debug("setMajor " + limit);
1649
            resetData();
1650
            mMajor = true;
1651
            mLimit = limit;
1652
        }
1653
 
1654
        /**
1655
         * sets mBuild false, mMajor false, mMinor true, mPatch false, mLimit
1656
         * false
1657
         */
1658
        void setMinor()
1659
        {
1660
            mLogger.debug("setMinor");
1661
            resetData();
1662
            mMinor = true;
1663
        }
1664
 
1665
        /**
1666
         * sets mBuild false, mMajor false, mMinor true, mPatch false, mLimit
1667
         * limit
1668
         */
1669
        void setMinor(boolean limit)
1670
        {
1671
            mLogger.debug("setMinor " + limit);
1672
            resetData();
1673
            mMinor = true;
1674
            mLimit = limit;
1675
        }
1676
 
1677
        /**
1678
         * sets mBuild false, mMajor false, mMinor false, mPatch true, mLimit
1679
         * false
1680
         */
1681
        void setPatch()
1682
        {
1683
            mLogger.debug("setPatch");
1684
            resetData();
1685
            mPatch = true;
1686
        }
1687
 
1688
        /**
1689
         * sets mBuild false, mMajor false, mMinor false, mPatch true, mLimit
1690
         * limit
1691
         */
1692
        void setPatch(boolean limit)
1693
        {
1694
            mLogger.debug("setPatch");
1695
            resetData();
1696
            mPatch = true;
1697
            mLimit = limit;
1698
        }
1699
 
1700
        /**
1701
         * sets mBuild false, mMajor false, mMinor false, mPatch false, mLimit
1702
         * true
1703
         */
1704
        void setLimit()
1705
        {
1706
            mLogger.debug("setPatch");
1707
            resetData();
1708
            mLimit = true;
1709
        }
1710
 
1711
        /**
1712
         * sets parameters to indicate that the change type is Fixed. The
1713
         * version number is set by the user and a ripple will not be calculated
1714
         */
1715
        void setFixed()
1716
        {
1717
            mLogger.debug("setFixed");
1718
            resetData();
1719
            mFixed = true;
1720
        }
1721
 
1722
        /**
1723
         * Sets parameters to indicate that the change type is not known
1724
         * 
1725
         */
1726
        void setUnknown()
1727
        {
1728
            resetData();
1729
            mUnknown = true;
1730
        }
1731
 
1732
    }
1733
 
7082 dpurdie 1734
    /**
1735
     *  Add a package dependency
7163 dpurdie 1736
     *  @param p - The package to add as a dependent. Uses data from the provided package
7082 dpurdie 1737
     *  @return - The current package to allow chaining of calls
1738
     */
1739
    public Package addDependency(Package p) {
7163 dpurdie 1740
        mDependencyCollection.add(new PkgDependency(p.mAlias, p.mId ));
7082 dpurdie 1741
        return this;
1742
    }
7163 dpurdie 1743
 
1744
    /**
1745
     *  Add a package dependency
1746
     *  @param alias - Alias of the dependent package
1747
     *  @param pvId  - pvId of the dependent package
1748
     *  @return - The current package to allow chaining of calls
1749
     */
1750
    public Package addDependency(String alias, Integer pvId) {
1751
        mDependencyCollection.add(new PkgDependency(alias, pvId ));
1752
        return this;
1753
    }
7082 dpurdie 1754
 
1755
    /** Clear the packages list of dependencies
1756
     *  UTF use
1757
     *  @return - The current package to allow chaining of calls
1758
     */
1759
    public Package resetDependencies() {
1760
        mDependencyCollection.clear();
1761
        return this;
1762
    }
1763
 
6914 dpurdie 1764
}