This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 252595
Collapse All | Expand All

(-)a/java.j2seproject/nbproject/project.xml (+8 lines)
Lines 163-168 Link Here
163
                    </run-dependency>
163
                    </run-dependency>
164
                </dependency>
164
                </dependency>
165
                <dependency>
165
                <dependency>
166
                    <code-name-base>org.netbeans.modules.java.preprocessorbridge</code-name-base>
167
                    <build-prerequisite/>
168
                    <compile-dependency/>
169
                    <run-dependency>
170
                        <specification-version>1.41</specification-version>
171
                    </run-dependency>
172
                </dependency>
173
                <dependency>
166
                    <code-name-base>org.netbeans.modules.java.project</code-name-base>
174
                    <code-name-base>org.netbeans.modules.java.project</code-name-base>
167
                    <build-prerequisite/>
175
                    <build-prerequisite/>
168
                    <compile-dependency/>
176
                    <compile-dependency/>
(-)a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEActionProvider.java (-21 / +219 lines)
Lines 48-54 Link Here
48
import java.beans.PropertyChangeListener;
48
import java.beans.PropertyChangeListener;
49
import java.io.File;
49
import java.io.File;
50
import java.io.IOException;
50
import java.io.IOException;
51
import java.io.InputStream;
52
import java.io.OutputStream;
53
import java.lang.ref.Reference;
51
import java.lang.ref.WeakReference;
54
import java.lang.ref.WeakReference;
55
import java.net.URISyntaxException;
52
import java.net.URL;
56
import java.net.URL;
53
import java.util.Arrays;
57
import java.util.Arrays;
54
import java.util.Collections;
58
import java.util.Collections;
Lines 57-70 Link Here
57
import java.util.Map;
61
import java.util.Map;
58
import java.util.Properties;
62
import java.util.Properties;
59
import java.util.Set;
63
import java.util.Set;
64
import java.util.WeakHashMap;
65
import java.util.function.Function;
60
import java.util.logging.Level;
66
import java.util.logging.Level;
61
import java.util.logging.Logger;
67
import java.util.logging.Logger;
62
import org.apache.tools.ant.module.api.support.ActionUtils;
68
import org.apache.tools.ant.module.api.support.ActionUtils;
63
import org.netbeans.api.annotations.common.CheckForNull;
69
import org.netbeans.api.annotations.common.CheckForNull;
64
import org.netbeans.api.annotations.common.NonNull;
70
import org.netbeans.api.annotations.common.NonNull;
71
import org.netbeans.api.annotations.common.StaticResource;
65
import org.netbeans.api.java.classpath.ClassPath;
72
import org.netbeans.api.java.classpath.ClassPath;
66
import org.netbeans.api.java.project.JavaProjectConstants;
73
import org.netbeans.api.java.project.JavaProjectConstants;
67
import org.netbeans.api.java.source.BuildArtifactMapper;
74
import org.netbeans.api.java.source.BuildArtifactMapper;
75
import org.netbeans.api.project.FileOwnerQuery;
76
import org.netbeans.api.project.Project;
68
import org.netbeans.modules.java.api.common.SourceRoots;
77
import org.netbeans.modules.java.api.common.SourceRoots;
69
import org.netbeans.modules.java.api.common.ant.UpdateHelper;
78
import org.netbeans.modules.java.api.common.ant.UpdateHelper;
70
import org.netbeans.modules.java.api.common.project.ProjectProperties;
79
import org.netbeans.modules.java.api.common.project.ProjectProperties;
Lines 72-87 Link Here
72
import org.netbeans.modules.java.api.common.project.BaseActionProvider.Callback3;
81
import org.netbeans.modules.java.api.common.project.BaseActionProvider.Callback3;
73
import org.netbeans.modules.java.api.common.project.ProjectConfigurations;
82
import org.netbeans.modules.java.api.common.project.ProjectConfigurations;
74
import org.netbeans.modules.java.j2seproject.api.J2SEBuildPropertiesProvider;
83
import org.netbeans.modules.java.j2seproject.api.J2SEBuildPropertiesProvider;
84
import org.netbeans.modules.java.preprocessorbridge.spi.CompileOnSaveAction;
75
import org.netbeans.spi.project.ActionProvider;
85
import org.netbeans.spi.project.ActionProvider;
76
import org.netbeans.spi.project.LookupProvider;
86
import org.netbeans.spi.project.LookupProvider;
77
import org.netbeans.spi.project.ProjectServiceProvider;
87
import org.netbeans.spi.project.ProjectServiceProvider;
78
import org.netbeans.spi.project.SingleMethod;
88
import org.netbeans.spi.project.SingleMethod;
79
import org.netbeans.spi.project.support.ant.PropertyEvaluator;
89
import org.netbeans.spi.project.support.ant.PropertyEvaluator;
90
import org.openide.filesystems.FileLock;
80
import org.openide.filesystems.FileObject;
91
import org.openide.filesystems.FileObject;
81
import org.openide.filesystems.FileUtil;
92
import org.openide.filesystems.FileUtil;
93
import org.openide.modules.Places;
94
import org.openide.util.BaseUtilities;
95
import org.openide.util.Exceptions;
82
import org.openide.util.Lookup;
96
import org.openide.util.Lookup;
97
import org.openide.util.Pair;
83
import org.openide.util.Parameters;
98
import org.openide.util.Parameters;
84
import org.openide.util.WeakListeners;
99
import org.openide.util.WeakListeners;
100
import org.openide.util.lookup.ServiceProvider;
85
101
86
/** Action provider of the J2SE project. This is the place where to do
102
/** Action provider of the J2SE project. This is the place where to do
87
 * strange things to J2SE actions. E.g. compile-single.
103
 * strange things to J2SE actions. E.g. compile-single.
Lines 341-348 Link Here
341
    }
357
    }
342
358
343
    private static final class CosAction implements BuildArtifactMapper.ArtifactsUpdated,
359
    private static final class CosAction implements BuildArtifactMapper.ArtifactsUpdated,
344
            PropertyChangeListener {
360
            CompileOnSaveAction, PropertyChangeListener {
361
        private static Map<Project,Reference<CosAction>> instances = new WeakHashMap<>();
345
        private static final String COS_UPDATED = "$cos.update";    //NOI18N
362
        private static final String COS_UPDATED = "$cos.update";    //NOI18N
363
        private static final String COS_CUSTOM = "$cos.update.custom";    //NOI18N
364
        private static final String PROP_TARGET = "cos.update.target.internal";  //NOI18N
365
        private static final String PROP_SCRIPT = "cos.update.script.internal";  //NOI18N
366
        private static final String PROP_SRCDIR = "cos.src.dir.internal";   //NOI18N
367
        private static final String PROP_INCLUDES ="cos.includes.internal"; //NOI18N
368
        private static final String SNIPPETS = "executor-snippets"; //NOI18N
369
        private static final String SCRIPT = "cos-update.xml"; //NOI18N
370
        private static final String TARGET = "cos-update-internal"; //NOI18N
371
        private static final String SCRIPT_TEMPLATE = "/org/netbeans/modules/java/j2seproject/resources/cos-update-snippet.xml"; //NOI18N
346
        private static final Object NONE = new Object();
372
        private static final Object NONE = new Object();
347
        private final J2SEActionProvider owner;
373
        private final J2SEActionProvider owner;
348
        private final PropertyEvaluator eval;
374
        private final PropertyEvaluator eval;
Lines 351-356 Link Here
351
        private final BuildArtifactMapper mapper;
377
        private final BuildArtifactMapper mapper;
352
        private final Map</*@GuardedBy("this")*/URL,BuildArtifactMapper.ArtifactsUpdated> currentListeners;
378
        private final Map</*@GuardedBy("this")*/URL,BuildArtifactMapper.ArtifactsUpdated> currentListeners;
353
        private volatile Object targetCache;
379
        private volatile Object targetCache;
380
        private volatile Boolean enabledCache;
354
381
355
        private CosAction(
382
        private CosAction(
356
                @NonNull final J2SEActionProvider owner,
383
                @NonNull final J2SEActionProvider owner,
Lines 367-394 Link Here
367
            this.src.addPropertyChangeListener(WeakListeners.propertyChange(this, this.src));
394
            this.src.addPropertyChangeListener(WeakListeners.propertyChange(this, this.src));
368
            this.tests.addPropertyChangeListener(WeakListeners.propertyChange(this, this.tests));
395
            this.tests.addPropertyChangeListener(WeakListeners.propertyChange(this, this.tests));
369
            updateRootsListeners();
396
            updateRootsListeners();
397
            instances.put(owner.getProject(), new WeakReference<>(this));
370
        }
398
        }
371
399
372
        @Override
400
        @Override
401
        public boolean isUpdateClasses() {
402
            return getTarget() != null && isCustomUpdate();
403
        }
404
405
        @Override
406
        public boolean isUpdateResources() {
407
            return getTarget() != null && isCustomUpdate();
408
        }
409
410
        @Override
411
        public Boolean performAction(Context ctx) throws IOException {
412
            switch (ctx.getOperation()) {
413
                case UPDATE:
414
                    return performUpdate(ctx);
415
                case CLEAN:
416
                    return performClean(ctx);
417
                case SYNC:
418
                    return performSync(ctx);
419
                default:
420
                    throw new IllegalArgumentException(String.valueOf(ctx.getOperation()));                 
421
            }
422
        }               
423
424
        @Override
373
        public void artifactsUpdated(@NonNull final Iterable<File> artifacts) {
425
        public void artifactsUpdated(@NonNull final Iterable<File> artifacts) {
374
            final String target = getTarget();
426
            if (!isCustomUpdate()) {
375
            if (target != null) {
427
                final String target = getTarget();
376
                final FileObject buildXml = owner.findBuildXml();
428
                if (target != null) {
377
                if (buildXml != null) {
429
                    final FileObject buildXml = owner.findBuildXml();
378
                    try {
430
                    if (buildXml != null) {
379
                        ActionUtils.runTarget(
431
                        try {
380
                                buildXml,
432
                                ActionUtils.runTarget(
381
                                new String[] {target},
433
                                    buildXml,
382
                                null,
434
                                    new String[] {target},
383
                                null);
435
                                    null,
384
                    } catch (IOException ioe) {
436
                                        null);
385
                        LOG.log(
437
                        } catch (IOException ioe) {
386
                                Level.WARNING,
438
                            LOG.log(
387
                                "Cannot execute pos compile on save target: {0} in: {1}",   //NOI18N
439
                                    Level.WARNING,
388
                                new Object[]{
440
                                    "Cannot execute pos compile on save target: {0} in: {1}",   //NOI18N
389
                                    target,
441
                                    new Object[]{
390
                                    FileUtil.getFileDisplayName(buildXml)
442
                                        target,
391
                                });
443
                                        FileUtil.getFileDisplayName(buildXml)
444
                                    });
445
                        }
392
                    }
446
                    }
393
                }
447
                }
394
            }
448
            }
Lines 397-405 Link Here
397
        @Override
451
        @Override
398
        public void propertyChange(@NonNull final PropertyChangeEvent evt) {
452
        public void propertyChange(@NonNull final PropertyChangeEvent evt) {
399
            final String name = evt.getPropertyName();
453
            final String name = evt.getPropertyName();
400
            if (name == null || COS_UPDATED.equals(name)) {
454
            if (name == null) {
401
                targetCache = null;
455
                targetCache = null;
402
            } else if (SourceRoots.PROP_ROOTS.equals(name)) {
456
                enabledCache = null;
457
            } else if (COS_UPDATED.equals(name)) {
458
                targetCache = null;
459
            } else if (COS_CUSTOM.equals(name)) {
460
                enabledCache = null;
461
            }else if (SourceRoots.PROP_ROOTS.equals(name)) {
403
                updateRootsListeners();
462
                updateRootsListeners();
404
            }
463
            }
405
        }
464
        }
Lines 440-445 Link Here
440
                    (String) target :
499
                    (String) target :
441
                    null;
500
                    null;
442
        }
501
        }
502
        
503
        private boolean isCustomUpdate() {
504
            Boolean res = enabledCache;
505
            if (res == null) {
506
                final String val = eval.getProperty(COS_CUSTOM);
507
                res = enabledCache = Boolean.valueOf(val);
508
            }
509
            return res;
510
        }
511
        
512
        @CheckForNull
513
        private Boolean performUpdate(@NonNull final Context ctx) {
514
            final String target = getTarget();
515
            if (target != null) {
516
                final FileObject buildXml = owner.findBuildXml();
517
                if (buildXml != null) {
518
                    try {
519
                        final FileObject cosScript = getCosScript();
520
                        final Iterable<? extends File> updated = ctx.getUpdated();
521
                        final Iterable<? extends File> deleted = ctx.getDeleted();
522
                        final File root = ctx.isCopyResources() ?
523
                                BaseUtilities.toFile(ctx.getSourceRoot().toURI()) :
524
                                ctx.getCacheRoot();
525
                        final String includes = createIncludes(root, updated);
526
                        if (includes != null) {
527
                            final Properties props = new Properties();
528
                            props.setProperty(PROP_TARGET, target);
529
                            props.setProperty(PROP_SCRIPT, FileUtil.toFile(buildXml).getAbsolutePath());
530
                            props.setProperty(PROP_SRCDIR, root.getAbsolutePath());
531
                            props.setProperty(PROP_INCLUDES, includes);
532
                            ActionUtils.runTarget(
533
                                    cosScript,
534
                                    new String[] {TARGET},
535
                                    props,
536
                                    null);
537
                        } else {
538
                            LOG.warning("BuildArtifactMapper artifacts do not provide attributes.");    //NOI18N
539
                        }
540
                    } catch (IOException | URISyntaxException e) {
541
                        LOG.log(
542
                                Level.WARNING,
543
                                "Cannot execute update targer on save target: {0} in: {1} due to: {2}",   //NOI18N
544
                                new Object[]{
545
                                    target,
546
                                    FileUtil.getFileDisplayName(buildXml),
547
                                    e.getMessage()
548
                                });
549
                    }
550
                }
551
            }
552
            return true;
553
        }
554
        
555
        @CheckForNull
556
        private Boolean performClean(@NonNull final Context ctx) {
557
            //Not sure what to do
558
            return null;
559
        }
560
        
561
        @CheckForNull
562
        private Boolean performSync(@NonNull final Context ctx) {
563
            //Not sure what to do
564
            return null;
565
        }
566
        
567
        @NonNull
568
        private FileObject getCosScript() throws IOException {
569
            final FileObject snippets = FileUtil.createFolder(
570
                    Places.getCacheSubdirectory(SNIPPETS));
571
            FileObject cosScript = snippets.getFileObject(SCRIPT);
572
            if (cosScript == null) {
573
                cosScript = FileUtil.createData(snippets, SCRIPT);
574
                final FileLock lock = cosScript.lock();
575
                try (InputStream in = getClass().getResourceAsStream(SCRIPT_TEMPLATE);
576
                        OutputStream out = cosScript.getOutputStream(lock)) {
577
                    FileUtil.copy(in, out);
578
                } finally {
579
                    lock.releaseLock();
580
                }
581
            }
582
            return cosScript;
583
        }        
584
        
585
        @CheckForNull
586
        private static String createIncludes(
587
                @NonNull final File root,
588
                @NonNull final Iterable<? extends File> artifacts) {
589
            final StringBuilder include = new StringBuilder();
590
            for (File f : artifacts) {
591
                if (include.length() > 0) {
592
                    include.append(','); //NOI18N
593
                }
594
                include.append(relativize(f,root));
595
            }
596
            return include.length() == 0 ?
597
                    null :
598
                    include.toString();
599
        }
600
        
601
        private static String relativize(
602
                @NonNull final File file,
603
                @NonNull final File folder) {
604
            final String folderPath = folder.getAbsolutePath();
605
            int start = folderPath.length();
606
            if (!folderPath.endsWith(File.separator)) {
607
                start++;
608
            }
609
            return file.getAbsolutePath().substring(start);
610
        }                        
611
        
612
        @CheckForNull
613
        static CosAction getInstance(@NonNull final Project p) {
614
            final Reference<CosAction> r = instances.get(p);
615
            return r != null ?
616
                    r.get() :
617
                    null;
618
        }
443
619
444
        private static final class WeakArtifactUpdated extends WeakReference<BuildArtifactMapper.ArtifactsUpdated>
620
        private static final class WeakArtifactUpdated extends WeakReference<BuildArtifactMapper.ArtifactsUpdated>
445
                implements BuildArtifactMapper.ArtifactsUpdated, Runnable {
621
                implements BuildArtifactMapper.ArtifactsUpdated, Runnable {
Lines 473-476 Link Here
473
            }
649
            }
474
        }
650
        }
475
    }
651
    }
652
    
653
    @ServiceProvider(service = CompileOnSaveAction.Provider.class, position = 10_000)
654
    public static final class Provider implements CompileOnSaveAction.Provider {
655
656
        @Override
657
        public CompileOnSaveAction forRoot(URL root) {
658
            try {
659
                final Project p = FileOwnerQuery.getOwner(root.toURI());
660
                if (p != null) {
661
                    p.getLookup().lookup(ActionProvider.class).getSupportedActions();   //Force initialization
662
                    final CosAction action = CosAction.getInstance(p);
663
                    if (action != null && action.isUpdateClasses()) {
664
                        return action;
665
                    }
666
                }
667
            } catch (URISyntaxException e) {
668
                Exceptions.printStackTrace(e);
669
            }
670
            return null;
671
        }
672
        
673
    }
476
}
674
}
(-)a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/resources/cos-update-snippet.xml (+51 lines)
Line 0 Link Here
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!--
3
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
5
Copyright 2010 Oracle and/or its affiliates. All rights reserved.
6
7
Oracle and Java are registered trademarks of Oracle and/or its affiliates.
8
Other names may be trademarks of their respective owners.
9
10
11
The contents of this file are subject to the terms of either the GNU
12
General Public License Version 2 only ("GPL") or the Common
13
Development and Distribution License("CDDL") (collectively, the
14
"License"). You may not use this file except in compliance with the
15
License. You can obtain a copy of the License at
16
http://www.netbeans.org/cddl-gplv2.html
17
or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
18
specific language governing permissions and limitations under the
19
License.  When distributing the software, include this License Header
20
Notice in each file and include the License file at
21
nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
22
particular file as subject to the "Classpath" exception as provided
23
by Oracle in the GPL Version 2 section of the License file that
24
accompanied this code. If applicable, add the following below the
25
License Header, with the fields enclosed by brackets [] replaced by
26
your own identifying information:
27
"Portions Copyrighted [year] [name of copyright owner]"
28
29
Contributor(s):
30
31
The Original Software is NetBeans. The Initial Developer of the Original
32
Software is Sun Microsystems, Inc. Portions Copyright 2008 Sun
33
Microsystems, Inc. All Rights Reserved.
34
35
If you wish your version of this file to be governed by only the CDDL
36
or only the GPL Version 2, indicate your decision by adding
37
"[Contributor] elects to include this software in this distribution
38
under the [CDDL or GPL Version 2] license." If you do not indicate a
39
single choice of license, a recipient has the option to distribute
40
your version of this file under either the CDDL, the GPL Version 2 or
41
to extend the choice of license to its licensees as provided above.
42
However, if you add GPL Version 2 code and therefore, elected the GPL
43
Version 2 license, then the option applies only if the new code is
44
made subject to such option by the copyright holder.
45
-->
46
<project name="cos-update" default="cos-update-internal">
47
    <target name="cos-update-internal">
48
        <cos-updated id="cos.updated.files" srcdir="${cos.src.dir.internal}" includes="${cos.includes.internal}"/>
49
        <ant antfile="${cos.update.script.internal}" target="${cos.update.target.internal}" useNativeBasedir="true" inheritRefs="true"/>
50
    </target>
51
</project>
(-)a/java.preprocessorbridge/nbproject/project.properties (-1 / +1 lines)
Lines 38-43 Link Here
38
is.autoload=true
38
is.autoload=true
39
javac.compilerargs=-Xlint:unchecked
39
javac.compilerargs=-Xlint:unchecked
40
javac.source=1.7
40
javac.source=1.7
41
spec.version.base=1.40.0
41
spec.version.base=1.41.0
42
javadoc.apichanges=${basedir}/apichanges.xml
42
javadoc.apichanges=${basedir}/apichanges.xml
43
43
(-)a/java.preprocessorbridge/nbproject/project.xml (+10 lines)
Lines 54-59 Link Here
54
                    </run-dependency>
54
                    </run-dependency>
55
                </dependency>
55
                </dependency>
56
                <dependency>
56
                <dependency>
57
                    <code-name-base>org.netbeans.api.java.classpath</code-name-base>
58
                    <build-prerequisite/>
59
                    <compile-dependency/>
60
                    <run-dependency>
61
                        <release-version>1</release-version>
62
                        <specification-version>1.52</specification-version>
63
                    </run-dependency>
64
                </dependency>
65
                <dependency>
57
                    <code-name-base>org.netbeans.libs.javacapi</code-name-base>
66
                    <code-name-base>org.netbeans.libs.javacapi</code-name-base>
58
                    <build-prerequisite/>
67
                    <build-prerequisite/>
59
                    <compile-dependency/>
68
                    <compile-dependency/>
Lines 111-116 Link Here
111
                <friend>org.netbeans.modules.groovy.editor</friend>
120
                <friend>org.netbeans.modules.groovy.editor</friend>
112
                <friend>org.netbeans.modules.java.editor</friend>
121
                <friend>org.netbeans.modules.java.editor</friend>
113
                <friend>org.netbeans.modules.java.hints</friend>
122
                <friend>org.netbeans.modules.java.hints</friend>
123
                <friend>org.netbeans.modules.java.j2seproject</friend>
114
                <friend>org.netbeans.modules.java.source</friend>
124
                <friend>org.netbeans.modules.java.source</friend>
115
                <friend>org.netbeans.modules.java.source.base</friend>
125
                <friend>org.netbeans.modules.java.source.base</friend>
116
                <friend>org.netbeans.modules.java.sourceui</friend>
126
                <friend>org.netbeans.modules.java.sourceui</friend>
(-)a/java.preprocessorbridge/src/org/netbeans/modules/java/preprocessorbridge/api/CompileOnSaveActionQuery.java (+72 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2016 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.java.preprocessorbridge.api;
43
44
import java.net.URL;
45
import org.netbeans.api.annotations.common.CheckForNull;
46
import org.netbeans.api.annotations.common.NonNull;
47
import org.netbeans.modules.java.preprocessorbridge.spi.CompileOnSaveAction;
48
import org.openide.util.Lookup;
49
50
/**
51
 *
52
 * @author Tomas Zezula
53
 */
54
public final class CompileOnSaveActionQuery {
55
    private static final Lookup.Result<CompileOnSaveAction.Provider> instances
56
            = Lookup.getDefault().lookupResult(CompileOnSaveAction.Provider.class);
57
58
    private CompileOnSaveActionQuery() {
59
        throw new IllegalStateException("No instance allowed.");    //NOI18N
60
    }
61
    
62
    @CheckForNull
63
    public static CompileOnSaveAction getAction(@NonNull final URL sourceRoot) {
64
        for (CompileOnSaveAction.Provider p : instances.allInstances()) {
65
            final CompileOnSaveAction action = p.forRoot(sourceRoot);
66
            if (action != null) {
67
                return action;
68
            }
69
        }
70
        return null;
71
    }    
72
}
(-)a/java.preprocessorbridge/src/org/netbeans/modules/java/preprocessorbridge/spi/CompileOnSaveAction.java (+250 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2016 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.java.preprocessorbridge.spi;
43
44
import java.io.File;
45
import java.io.IOException;
46
import java.net.MalformedURLException;
47
import java.net.URL;
48
import java.util.Optional;
49
import java.util.function.Consumer;
50
import java.util.logging.Level;
51
import java.util.logging.Logger;
52
import org.netbeans.api.annotations.common.CheckForNull;
53
import org.netbeans.api.annotations.common.NonNull;
54
import org.netbeans.api.annotations.common.NullAllowed;
55
import org.netbeans.api.annotations.common.NullUnknown;
56
import org.netbeans.api.java.queries.BinaryForSourceQuery;
57
import org.openide.filesystems.FileUtil;
58
import org.openide.util.BaseUtilities;
59
import org.openide.util.Exceptions;
60
import org.openide.util.Parameters;
61
62
/**
63
 * @since 1.41
64
 * @author Tomas Zezula
65
 */
66
public interface CompileOnSaveAction {
67
68
    public Boolean performAction (@NonNull final Context ctx) throws IOException;
69
    public boolean isUpdateResources();
70
    public boolean isUpdateClasses();
71
72
    enum Operation {
73
        CLEAN,
74
        UPDATE,
75
        SYNC
76
    }
77
78
    final class Context {
79
        private final Operation operation;
80
        private final URL srcRoot;
81
        private final boolean isCopyResources;
82
        private final boolean isKeepResourcesUpToDate;
83
        private final File cacheRoot;
84
        private final Iterable<? extends File> updated;
85
        private final Iterable<? extends File> deleted;
86
        private final Object owner;
87
        private final Consumer<Iterable<File>> firer;
88
        
89
        private Context(
90
                @NonNull final Operation operation,
91
                @NonNull final URL srcRoot,
92
                final boolean isCopyResources,
93
                final boolean isKeepResourcesUpToDate,
94
                @NullAllowed final File cacheRoot,
95
                @NullAllowed final Iterable<? extends File> updated,
96
                @NullAllowed final Iterable<? extends File> deleted,
97
                @NullAllowed final Object owner,
98
                @NullAllowed final Consumer<Iterable<File>> firer) {
99
            this.operation = operation;
100
            this.srcRoot = srcRoot;
101
            this.isCopyResources = isCopyResources;
102
            this.isKeepResourcesUpToDate = isKeepResourcesUpToDate;
103
            this.cacheRoot = cacheRoot;
104
            this.updated = updated;
105
            this.deleted = deleted;
106
            this.owner = owner;
107
            this.firer = firer;
108
        }
109
        
110
        @NonNull
111
        public Operation getOperation() {
112
            return operation;
113
        }
114
        
115
        @NonNull
116
        public Iterable<? extends File> getUpdated() {
117
            if (operation != Operation.UPDATE) {
118
                throw new IllegalStateException();
119
            }
120
            return updated;
121
        }
122
        
123
        @NonNull
124
        public Iterable<? extends File> getDeleted() {
125
            if (operation != Operation.UPDATE) {
126
                throw new IllegalStateException();
127
            }
128
            return deleted;
129
        }
130
        
131
        public boolean isCopyResources() {
132
            if (operation == Operation.CLEAN) {
133
                throw new IllegalStateException();
134
            }
135
            return isCopyResources;
136
        }
137
        
138
        public boolean isKeepResourcesUpToDate() {
139
            if (operation != Operation.SYNC) {
140
                throw new IllegalStateException();
141
            }
142
            return isKeepResourcesUpToDate;
143
        }
144
        
145
        @NonNull
146
        public URL getSourceRoot() {
147
            return srcRoot;
148
        }
149
        
150
        @NonNull
151
        public File getCacheRoot() {
152
            if (operation != Operation.UPDATE) {
153
                throw new IllegalStateException();
154
            }
155
            return cacheRoot;
156
        }
157
        
158
        @CheckForNull
159
        public File getTarget() {
160
            return getTarget(srcRoot);
161
        }
162
        
163
        @NonNull
164
        public Object getOwner() {
165
            if (operation != Operation.SYNC) {
166
                throw new IllegalStateException();
167
            }
168
            return owner;
169
        }
170
        
171
        public void filesUpdated(@NonNull final Iterable<File> updatedFiles) {
172
            if (firer != null) {
173
                firer.accept(updatedFiles);
174
            }
175
        }
176
        
177
        @NonNull
178
        public static Context clean(@NonNull final URL srcRoot) {
179
            Parameters.notNull("srcRoot", srcRoot); //NOI18N
180
            return new Context(Operation.CLEAN, srcRoot, false, false, null, null, null, null, null);
181
        }
182
        
183
        @NonNull
184
        public static Context update(
185
                @NonNull final URL srcRoot,
186
                final boolean isCopyResources,
187
                @NonNull final File cacheRoot,
188
                @NonNull final Iterable<? extends File> updated,
189
                @NonNull final Iterable<? extends File> deleted,
190
                @NullAllowed final Consumer<Iterable<File>> firer) {
191
            Parameters.notNull("srcRoot", srcRoot); //NOI18N
192
            Parameters.notNull("cacheRoot", cacheRoot); //NOI18N
193
            Parameters.notNull("updated", updated); //NOI18N
194
            Parameters.notNull("deleted", deleted); //NOI18N            
195
            return new Context(
196
                    Operation.UPDATE, srcRoot, isCopyResources, false, cacheRoot, updated, deleted, null, firer);
197
        }
198
        
199
        @NonNull
200
        public static Context sync(
201
                @NonNull final URL srcRoot,
202
                final boolean isCopyResources,
203
                final boolean isKeepResourcesUpToDate,
204
                @NonNull final Object owner) {
205
            Parameters.notNull("srcRoot", srcRoot); //NOI18N
206
            Parameters.notNull("owner", owner); //NOI18N
207
            return new Context(
208
                    Operation.SYNC, srcRoot, isCopyResources, isKeepResourcesUpToDate, null, null, null, owner, null);
209
        }
210
        
211
        @CheckForNull
212
        public static File getTarget(@NonNull URL srcRoot) {
213
            BinaryForSourceQuery.Result binaryRoots = BinaryForSourceQuery.findBinaryRoots(srcRoot);
214
        
215
            File result = null;
216
217
            for (URL u : binaryRoots.getRoots()) {
218
                assert u != null : "Null in BinaryForSourceQuery.Result.roots: " + binaryRoots; //NOI18N
219
                if (u == null) {
220
                    continue;
221
                }
222
                File f = FileUtil.archiveOrDirForURL(u);
223
224
                try {
225
                    if (FileUtil.isArchiveFile(BaseUtilities.toURI(f).toURL())) {
226
                        continue;
227
                    }
228
229
                    if (f != null && result != null) {
230
                        Logger.getLogger(CompileOnSaveAction.class.getName()).log(
231
                                Level.WARNING,
232
                                "More than one binary directory for root: {0}",
233
                                srcRoot.toExternalForm());
234
                        return null;
235
                    }
236
237
                    result = f;
238
                } catch (MalformedURLException ex) {
239
                    Exceptions.printStackTrace(ex);
240
                }
241
            }
242
243
            return result;
244
        }
245
    }
246
247
    interface Provider {
248
        CompileOnSaveAction forRoot(@NonNull final URL root);
249
    }
250
}
(-)a/java.source.ant/antsrc/org/netbeans/modules/java/source/ant/CosUpdated.java (+239 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2016 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.java.source.ant;
43
44
import java.io.File;
45
import java.io.IOException;
46
import java.io.InputStream;
47
import java.io.OutputStream;
48
import java.util.Iterator;
49
import java.util.Objects;
50
import org.apache.tools.ant.BuildException;
51
import org.apache.tools.ant.Project;
52
import org.apache.tools.ant.Task;
53
import org.apache.tools.ant.types.FileSet;
54
import org.apache.tools.ant.types.Reference;
55
import org.apache.tools.ant.types.Resource;
56
57
/**
58
 *
59
 * @author Tomas Zezula
60
 */
61
public final class CosUpdated extends Task {
62
    private String id;
63
    private File srcdir;
64
    private String includes;
65
    
66
    public void setId(final String id) {
67
        id.getClass();
68
        this.id = id;
69
    }
70
    
71
    
72
    public String getId() {
73
        return this.id;
74
    }
75
    
76
    public void setSrcdir(final File dir) {
77
        this.srcdir = dir;
78
    }
79
    
80
    public File getSrcdir() {
81
        return this.srcdir;
82
    }
83
    
84
    public void setIncludes(final String includes) {
85
        this.includes = includes;
86
    }
87
    
88
    public String getIncludes() {
89
        return this.includes;
90
    }
91
92
    @Override
93
    public void execute() throws BuildException {
94
        if (this.id == null || this.id.isEmpty()) {
95
            throw new BuildException("The id has to be set.");    //NOI18N
96
        }
97
        if (this.srcdir == null || !this.srcdir.isDirectory()) {
98
            throw new BuildException("The srcdir has to point to a directory.");    //NOI18N
99
        }
100
        if (this.includes == null || this.includes.isEmpty()) {
101
            throw new BuildException("The includes has to be set.");    //NOI18N
102
        }
103
        final Project prj = getProject();
104
        final CosFileSet cfs = new CosFileSet();
105
        cfs.setProject(prj);
106
        cfs.setDir(this.srcdir);
107
        for (String include : includes.split(",")) {    //NOI18N
108
            include = include.trim();
109
            if (!include.isEmpty()) {
110
                cfs.createInclude().setName(include);
111
            }
112
        }
113
        prj.addReference(this.id, cfs);
114
    }
115
    
116
    private static final class CosFileSet extends FileSet {
117
118
        @Override
119
        public Iterator<Resource> iterator() {
120
            return new CosFileSetIterator(super.iterator());
121
        }
122
123
        @Override
124
        public boolean isFilesystemOnly() {
125
            return false;
126
        }
127
        
128
        private static final class CosFileSetIterator implements Iterator<Resource> {
129
            
130
            private final Iterator<Resource> delegate;
131
            
132
            CosFileSetIterator(final Iterator<Resource> delegate) {
133
                delegate.getClass();
134
                this.delegate = delegate;
135
            }
136
137
            @Override
138
            public boolean hasNext() {
139
                return delegate.hasNext();
140
            }
141
142
            @Override
143
            public Resource next() {
144
                return new CosResource(delegate.next());
145
            }            
146
        }
147
        
148
        private static final class CosResource extends Resource {
149
            
150
            private final Resource delegate;
151
            
152
            CosResource(final Resource delegate) {
153
                delegate.getClass();
154
                this.delegate = delegate;
155
            }
156
157
            @Override
158
            public void setRefid(Reference r) {
159
                throw tooManyAttributes();
160
            }
161
162
            @Override
163
            public String getName() {
164
                final String name = delegate.getName();
165
                return name.replace(".sig", ".class");  //NOI18N
166
            }
167
168
            @Override
169
            public boolean isExists() {
170
                return delegate.isExists();
171
            }
172
173
            @Override
174
            public long getLastModified() {
175
                return delegate.getLastModified();
176
            }
177
178
            @Override
179
            public boolean isDirectory() {
180
                return delegate.isDirectory();
181
            }
182
183
            @Override
184
            public long getSize() {
185
                return delegate.getSize();
186
            }
187
188
            @Override
189
            public InputStream getInputStream() throws IOException {
190
                return delegate.getInputStream();
191
            }
192
193
            @Override
194
            public OutputStream getOutputStream() throws IOException {
195
                return delegate.getOutputStream();
196
            }
197
198
            @Override
199
            public int compareTo(Resource another) {
200
                if (another instanceof CosResource) {
201
                    return delegate.compareTo(((CosResource)another).delegate);
202
                } else {
203
                    return toString().compareTo(String.valueOf(another));
204
                }
205
            }
206
207
            @Override
208
            public int hashCode() {
209
                int hash = 3;
210
                hash = 71 * hash + Objects.hashCode(this.delegate);
211
                return hash;
212
            }
213
214
            @Override
215
            public boolean equals(Object obj) {
216
                if (this == obj) {
217
                    return true;
218
                }
219
                if (obj == null) {
220
                    return false;
221
                }
222
                if (getClass() != obj.getClass()) {
223
                    return false;
224
                }
225
                return Objects.equals(this.delegate, ((CosResource)obj).delegate);
226
            }
227
228
            @Override
229
            public String toString() {
230
                return delegate.toString();
231
            }
232
233
            @Override
234
            public boolean isFilesystemOnly() {
235
                return false;
236
            }
237
        }
238
    }
239
}
(-)a/java.source.ant/antsrc/org/netbeans/modules/java/source/ant/antlib.xml (+1 lines)
Lines 47-50 Link Here
47
    <taskdef name="javac" classname="org.netbeans.modules.java.source.ant.JavacTask"/>
47
    <taskdef name="javac" classname="org.netbeans.modules.java.source.ant.JavacTask"/>
48
    <taskdef name="delete" classname="org.netbeans.modules.java.source.ant.DeleteTask"/>
48
    <taskdef name="delete" classname="org.netbeans.modules.java.source.ant.DeleteTask"/>
49
    <taskdef name="translate-classpath" classname="org.netbeans.modules.java.source.ant.TranslateClassPath"/>
49
    <taskdef name="translate-classpath" classname="org.netbeans.modules.java.source.ant.TranslateClassPath"/>
50
    <taskdef name="cos-updated" classname="org.netbeans.modules.java.source.ant.CosUpdated"/>
50
</antlib>
51
</antlib>
(-)a/java.source.base/nbproject/project.xml (-1 / +1 lines)
Lines 203-209 Link Here
203
                    <build-prerequisite/>
203
                    <build-prerequisite/>
204
                    <compile-dependency/>
204
                    <compile-dependency/>
205
                    <run-dependency>
205
                    <run-dependency>
206
                        <specification-version>1.22</specification-version>
206
                        <specification-version>1.41</specification-version>
207
                    </run-dependency>
207
                    </run-dependency>
208
                </dependency>
208
                </dependency>
209
                <dependency>
209
                <dependency>
(-)a/java.source.base/src/org/netbeans/modules/java/source/indexing/COSSynchronizingIndexer.java (-2 / +7 lines)
Lines 54-59 Link Here
54
import java.util.logging.Level;
54
import java.util.logging.Level;
55
import java.util.logging.Logger;
55
import java.util.logging.Logger;
56
import org.netbeans.api.java.classpath.ClassPath;
56
import org.netbeans.api.java.classpath.ClassPath;
57
import org.netbeans.modules.java.preprocessorbridge.spi.CompileOnSaveAction;
57
import org.netbeans.modules.java.source.usages.BuildArtifactMapperImpl;
58
import org.netbeans.modules.java.source.usages.BuildArtifactMapperImpl;
58
import org.netbeans.modules.parsing.impl.indexing.IndexerCache;
59
import org.netbeans.modules.parsing.impl.indexing.IndexerCache;
59
import org.netbeans.modules.parsing.impl.indexing.IndexerCache.IndexerInfo;
60
import org.netbeans.modules.parsing.impl.indexing.IndexerCache.IndexerInfo;
Lines 80-86 Link Here
80
        if (FileUtil.getArchiveFile(rootURL) != null) {
81
        if (FileUtil.getArchiveFile(rootURL) != null) {
81
            return;
82
            return;
82
        }
83
        }
83
        if (!BuildArtifactMapperImpl.isUpdateResources(BuildArtifactMapperImpl.getTargetFolder(rootURL))) {
84
        if (!BuildArtifactMapperImpl.isUpdateResources(rootURL)) {
84
            return ;
85
            return ;
85
        }
86
        }
86
87
Lines 156-162 Link Here
156
157
157
        @Override
158
        @Override
158
        public void filesDeleted(Iterable<? extends Indexable> deleted, Context context) {
159
        public void filesDeleted(Iterable<? extends Indexable> deleted, Context context) {
159
            if (BuildArtifactMapperImpl.getTargetFolder(context.getRootURI()) == null) {
160
            final File target = CompileOnSaveAction.Context.getTarget(context.getRootURI());
161
            if (target == null) {
162
                return;
163
            }            
164
            if (!BuildArtifactMapperImpl.isUpdateClasses(context.getRootURI())) {
160
                return ;
165
                return ;
161
            }
166
            }
162
167
(-)a/java.source.base/src/org/netbeans/modules/java/source/usages/BuildArtifactMapperImpl.java (-205 / +275 lines)
Lines 68-84 Link Here
68
68
69
import javax.swing.event.ChangeEvent;
69
import javax.swing.event.ChangeEvent;
70
import javax.swing.event.ChangeListener;
70
import javax.swing.event.ChangeListener;
71
import org.netbeans.api.annotations.common.NonNull;
72
import org.netbeans.api.annotations.common.NullAllowed;
71
73
72
import org.netbeans.api.java.classpath.ClassPath;
74
import org.netbeans.api.java.classpath.ClassPath;
73
import org.netbeans.api.java.queries.AnnotationProcessingQuery;
75
import org.netbeans.api.java.queries.AnnotationProcessingQuery;
74
import org.netbeans.api.java.queries.BinaryForSourceQuery;
75
import org.netbeans.api.java.queries.BinaryForSourceQuery.Result;
76
import org.netbeans.api.java.queries.SourceForBinaryQuery;
76
import org.netbeans.api.java.queries.SourceForBinaryQuery;
77
import org.netbeans.api.java.source.BuildArtifactMapper.ArtifactsUpdated;
77
import org.netbeans.api.java.source.BuildArtifactMapper.ArtifactsUpdated;
78
import org.netbeans.api.java.source.SourceUtils;
78
import org.netbeans.api.java.source.SourceUtils;
79
import org.netbeans.api.queries.FileBuiltQuery;
79
import org.netbeans.api.queries.FileBuiltQuery;
80
import org.netbeans.api.queries.FileBuiltQuery.Status;
80
import org.netbeans.api.queries.FileBuiltQuery.Status;
81
import org.netbeans.api.queries.VisibilityQuery;
81
import org.netbeans.api.queries.VisibilityQuery;
82
import org.netbeans.modules.java.preprocessorbridge.api.CompileOnSaveActionQuery;
83
import org.netbeans.modules.java.preprocessorbridge.spi.CompileOnSaveAction;
82
import org.netbeans.modules.java.source.indexing.COSSynchronizingIndexer;
84
import org.netbeans.modules.java.source.indexing.COSSynchronizingIndexer;
83
import org.netbeans.modules.java.source.indexing.JavaIndex;
85
import org.netbeans.modules.java.source.indexing.JavaIndex;
84
import org.netbeans.modules.java.source.parsing.FileObjects;
86
import org.netbeans.modules.java.source.parsing.FileObjects;
Lines 90-96 Link Here
90
import org.netbeans.modules.parsing.spi.indexing.ErrorsCache;
92
import org.netbeans.modules.parsing.spi.indexing.ErrorsCache;
91
import org.netbeans.spi.queries.FileBuiltQueryImplementation;
93
import org.netbeans.spi.queries.FileBuiltQueryImplementation;
92
import org.openide.filesystems.FileObject;
94
import org.openide.filesystems.FileObject;
93
import org.openide.filesystems.FileStateInvalidException;
94
import org.openide.filesystems.FileUtil;
95
import org.openide.filesystems.FileUtil;
95
import org.openide.util.ChangeSupport;
96
import org.openide.util.ChangeSupport;
96
import org.openide.util.Exceptions;
97
import org.openide.util.Exceptions;
Lines 99-104 Link Here
99
import org.openide.util.BaseUtilities;
100
import org.openide.util.BaseUtilities;
100
import org.openide.util.Lookup;
101
import org.openide.util.Lookup;
101
import org.openide.util.WeakSet;
102
import org.openide.util.WeakSet;
103
import org.openide.util.lookup.ServiceProvider;
102
104
103
/**
105
/**
104
 *
106
 *
Lines 175-403 Link Here
175
        }
177
        }
176
    }
178
    }
177
    
179
    
178
    private static File getTarget(URL source) {
179
        Result binaryRoots = BinaryForSourceQuery.findBinaryRoots(source);
180
        
181
        File result = null;
182
        
183
        for (URL u : binaryRoots.getRoots()) {
184
            assert u != null : "Null in BinaryForSourceQuery.Result.roots: " + binaryRoots; //NOI18N
185
            if (u == null) {
186
                continue;
187
            }
188
            File f = FileUtil.archiveOrDirForURL(u);
189
190
            try {
191
                if (FileUtil.isArchiveFile(BaseUtilities.toURI(f).toURL())) {
192
                    continue;
193
                }
194
            
195
                if (f != null && result != null) {
196
                    Logger.getLogger(BuildArtifactMapperImpl.class.getName()).log(Level.WARNING, "More than one binary directory for root: {0}", source.toExternalForm());
197
                    return null;
198
                }
199
200
                result = f;
201
            } catch (MalformedURLException ex) {
202
                Exceptions.printStackTrace(ex);
203
            }
204
        }
205
        
206
        return result;
207
    }
208
209
    @SuppressWarnings("deprecation")
180
    @SuppressWarnings("deprecation")
210
    public static Boolean ensureBuilt(URL sourceRoot, Object context, boolean copyResources, boolean keepResourceUpToDate) throws IOException {
181
    public static Boolean ensureBuilt(URL sourceRoot, Object context, boolean copyResources, boolean keepResourceUpToDate) throws IOException {
211
        File targetFolder = getTarget(sourceRoot);
182
        final CompileOnSaveAction a = CompileOnSaveActionQuery.getAction(sourceRoot);
212
        
183
        if (a != null) {
213
        if (targetFolder == null) {
184
            final CompileOnSaveAction.Context ctx = CompileOnSaveAction.Context.sync(
214
            return null;
185
                    sourceRoot,
186
                    copyResources,
187
                    keepResourceUpToDate,
188
                    context);
189
            return a.performAction(ctx);
215
        }
190
        }
216
        
191
        return null;
217
        try {
218
            SourceUtils.waitScanFinished();
219
        } catch (InterruptedException e) {
220
            //Not Important
221
            LOG.log(Level.FINE, null, e);
222
            return null;
223
        }
224
225
        if (JavaIndex.ensureAttributeValue(sourceRoot, DIRTY_ROOT, null)) {
226
            IndexingManager.getDefault().refreshIndexAndWait(sourceRoot, null);
227
        }
228
229
        if (JavaIndex.getAttribute(sourceRoot, DIRTY_ROOT, null) != null) {
230
            return false;
231
        }
232
        
233
        FileObject[][] sources = new FileObject[1][];
234
        
235
        if (!protectAgainstErrors(targetFolder, sources, context)) {
236
            return false;
237
        }
238
        
239
        File tagFile = new File(targetFolder, TAG_FILE_NAME);
240
        File tagUpdateResourcesFile = new File(targetFolder, TAG_UPDATE_RESOURCES);
241
        final boolean forceResourceCopy = copyResources && keepResourceUpToDate && !tagUpdateResourcesFile.exists();
242
        final boolean cosActive = tagFile.exists();
243
        if (cosActive && !forceResourceCopy) {
244
            return true;
245
        }
246
247
        if (!cosActive) {
248
            delete(targetFolder, false/*#161085: cleanCompletely*/);
249
        }
250
251
        if (!targetFolder.exists() && !targetFolder.mkdirs()) {
252
            throw new IOException("Cannot create destination folder: " + targetFolder.getAbsolutePath());
253
        }
254
255
        sources(targetFolder, sources);
256
257
        for (int i = sources[0].length - 1; i>=0; i--) {
258
            final FileObject sr = sources[0][i];
259
            if (!cosActive) {
260
                URL srURL = sr.toURL();
261
                File index = JavaIndex.getClassFolder(srURL, true);
262
263
                if (index == null) {
264
                    //#181992: (not nice) ignore the annotation processing target directory:
265
                    if (srURL.equals(AnnotationProcessingQuery.getAnnotationProcessingOptions(sr).sourceOutputDirectory())) {
266
                        continue;
267
                    }
268
269
                    return null;
270
                }
271
272
                copyRecursively(index, targetFolder);
273
            }
274
275
            if (copyResources) {
276
                Set<String> javaMimeTypes = COSSynchronizingIndexer.gatherJavaMimeTypes();
277
                String[]    javaMimeTypesArr = javaMimeTypes.toArray(new String[0]);
278
279
                copyRecursively(sr, targetFolder, javaMimeTypes, javaMimeTypesArr);
280
            }
281
        }
282
283
        if (!cosActive) {
284
            new FileOutputStream(tagFile).close();
285
        }
286
287
        if (keepResourceUpToDate)
288
            new FileOutputStream(tagUpdateResourcesFile).close();
289
        
290
        return true;
291
    }
192
    }
292
    
193
    
293
    @SuppressWarnings("deprecation")
194
    @SuppressWarnings("deprecation")
294
    public static Boolean clean(URL sourceRoot) throws IOException {
195
    public static Boolean clean(URL sourceRoot) throws IOException {
295
        File targetFolder = getTarget(sourceRoot);
196
        final CompileOnSaveAction a = CompileOnSaveActionQuery.getAction(sourceRoot);
296
197
        if (a != null) {
297
        if (targetFolder == null) {
198
            final CompileOnSaveAction.Context ctx = CompileOnSaveAction.Context.clean(sourceRoot);
298
            return null;
199
            return a.performAction(ctx);
299
        }
200
        }
300
301
        File tagFile = new File(targetFolder, TAG_FILE_NAME);
302
303
        if (!tagFile.exists()) {
304
            return null;
305
        }
306
307
        try {
308
            SourceUtils.waitScanFinished();
309
        } catch (InterruptedException e) {
310
            //Not Important
311
            LOG.log(Level.FINE, null, e);
312
            return false;
313
        }
314
315
        delete(targetFolder, false);
316
        delete(tagFile, true);
317
318
        return null;
201
        return null;
319
    }
202
    }
320
203
321
    public static File getTargetFolder(URL sourceRoot) {
204
    public static boolean isUpdateClasses(URL sourceRoot) {
322
        File targetFolder = getTarget(sourceRoot);
205
        final CompileOnSaveAction a = CompileOnSaveActionQuery.getAction(sourceRoot);
323
206
        return a != null ? 
324
        if (targetFolder == null) {
207
                a.isUpdateClasses():
325
            return null;
208
                false;        
326
        }
327
328
        if (!new File(targetFolder, TAG_FILE_NAME).exists()) {
329
            return null;
330
        }
331
        
332
        return targetFolder;
333
    }
209
    }
334
210
335
    public static boolean isUpdateResources(File targetFolder) {
211
    public static boolean isUpdateResources(URL srcRoot) {
336
        return targetFolder != null && new File(targetFolder, TAG_UPDATE_RESOURCES).exists();
212
        final CompileOnSaveAction a = CompileOnSaveActionQuery.getAction(srcRoot);
213
        return a != null ? 
214
                a.isUpdateResources():
215
                false;                
337
    }
216
    }
338
217
339
    public static void classCacheUpdated(URL sourceRoot, File cacheRoot, Iterable<File> deleted, Iterable<File> updated, boolean resource) {
218
    public static void classCacheUpdated(URL sourceRoot, File cacheRoot, Iterable<File> deleted, Iterable<File> updated, boolean resource) {
340
        if (!deleted.iterator().hasNext() && !updated.iterator().hasNext()) {
219
        final CompileOnSaveAction a = CompileOnSaveActionQuery.getAction(sourceRoot);
341
            return ;
220
        if (a != null) {
342
        }
343
344
        File targetFolder = getTargetFolder(sourceRoot);
345
346
        if (targetFolder == null) {
347
            return ;
348
        }
349
350
        if (resource && !isUpdateResources(targetFolder)) {
351
            return ;
352
        }
353
        
354
        List<File> updatedFiles = new LinkedList<File>();
355
356
        for (File deletedFile : deleted) {
357
            final String relPath = relativizeFile(cacheRoot, deletedFile);
358
            if (relPath == null) {
359
                throw new IllegalArgumentException (String.format(
360
                    "Deleted file: %s is not under cache root: %s, (normalized file: %s).", //NOI18N
361
                    deletedFile.getAbsolutePath(),
362
                    cacheRoot.getAbsolutePath(),
363
                    FileUtil.normalizeFile(deletedFile).getAbsolutePath()));
364
            }
365
            File toDelete = resolveFile(targetFolder, relPath);
366
            
367
            toDelete.delete();
368
            updatedFiles.add(toDelete);
369
        }
370
371
        for (File updatedFile : updated) {
372
            final String relPath = relativizeFile(cacheRoot, updatedFile);
373
            if (relPath == null) {
374
                throw new IllegalArgumentException (String.format(
375
                    "Updated file: %s is not under cache root: %s, (normalized file: %s).", //NOI18N
376
                    updatedFile.getAbsolutePath(),
377
                    cacheRoot.getAbsolutePath(),
378
                    FileUtil.normalizeFile(updatedFile).getAbsolutePath()));
379
            }
380
            File target = resolveFile(targetFolder, relPath);                        
381
382
            try {
221
            try {
383
                copyFile(updatedFile, target);
222
                final CompileOnSaveAction.Context ctx = CompileOnSaveAction.Context.update(
384
                updatedFiles.add(target);
223
                        sourceRoot,
224
                        resource,
225
                        cacheRoot,
226
                        updated,
227
                        deleted,
228
                        (updatedFiles) -> fire(sourceRoot, updatedFiles));
229
                a.performAction(ctx);
385
            } catch (IOException ex) {
230
            } catch (IOException ex) {
386
                Exceptions.printStackTrace(ex);
231
                Exceptions.printStackTrace(ex);
387
            }
232
            }
388
        }
233
        }
389
234
    }
390
        if (updatedFiles.size() > 0) {
235
    
236
    private static void fire(
237
            @NonNull final URL sourceRoot,
238
            @NonNull final Iterable<File> updatedFiles) {
239
        if (updatedFiles.iterator().hasNext()) {
391
            Set<ArtifactsUpdated> listeners;
240
            Set<ArtifactsUpdated> listeners;
392
393
            synchronized (BuildArtifactMapperImpl.class) {
241
            synchronized (BuildArtifactMapperImpl.class) {
394
                listeners = source2Listener.get(sourceRoot);
242
                listeners = source2Listener.get(sourceRoot);
395
396
                if (listeners != null) {
243
                if (listeners != null) {
397
                    listeners = new HashSet<ArtifactsUpdated>(listeners);
244
                    listeners = new HashSet<>(listeners);
398
                }
245
                }
399
            }
246
            }
400
401
            if (listeners != null) {
247
            if (listeners != null) {
402
                for (ArtifactsUpdated listener : listeners) {
248
                for (ArtifactsUpdated listener : listeners) {
403
                    listener.artifactsUpdated(updatedFiles);
249
                    listener.artifactsUpdated(updatedFiles);
Lines 405-411 Link Here
405
            }
251
            }
406
        }
252
        }
407
    }
253
    }
408
254
    
409
    private static void copyFile(File updatedFile, File target) throws IOException {
255
    private static void copyFile(File updatedFile, File target) throws IOException {
410
        final File parent = target.getParentFile();
256
        final File parent = target.getParentFile();
411
        if (parent != null && !parent.exists()) {
257
        if (parent != null && !parent.exists()) {
Lines 692-698 Link Here
692
                    return delegate;
538
                    return delegate;
693
                }
539
                }
694
540
695
                File target = getTarget(owner.getURL());
541
                File target = CompileOnSaveAction.Context.getTarget(owner.toURL());
696
                File tagFile = FileUtil.normalizeFile(new File(target, TAG_FILE_NAME));
542
                File tagFile = FileUtil.normalizeFile(new File(target, TAG_FILE_NAME));
697
543
698
                synchronized(this) {
544
                synchronized(this) {
Lines 713-722 Link Here
713
                    }
559
                    }
714
560
715
                    return result;
561
                    return result;
716
                }
562
                }            
717
            } catch (FileStateInvalidException ex) {
718
                Exceptions.printStackTrace(ex);
719
                return null;
720
            } finally {
563
            } finally {
721
                recursive.remove();
564
                recursive.remove();
722
            }
565
            }
Lines 794-798 Link Here
794
                }
637
                }
795
            });
638
            });
796
        }
639
        }
640
    }       
641
642
    private static final class DefaultCompileOnSaveAction implements CompileOnSaveAction {
643
        private final URL root;
644
        
645
        DefaultCompileOnSaveAction(@NonNull final URL root) {
646
            this.root = root;
647
        }
648
        
649
        @Override
650
        public boolean isUpdateClasses() {
651
            return isUpdateClasses(CompileOnSaveAction.Context.getTarget(root));
652
        }
653
        
654
        @Override
655
        public boolean isUpdateResources() {
656
            return isUpdateResources(CompileOnSaveAction.Context.getTarget(root));
657
        }
658
659
        @Override
660
        public Boolean performAction(@NonNull final Context ctx) throws IOException {
661
            assert root.equals(ctx.getSourceRoot());
662
            switch (ctx.getOperation()) {
663
                case CLEAN:
664
                    return performClean(ctx);
665
                case SYNC:
666
                    return performSync(ctx);
667
                case UPDATE:
668
                    return performUpdate(ctx);
669
                default:
670
            }       throw new IllegalArgumentException(String.valueOf(ctx.getOperation()));
671
        }
672
        
673
        private Boolean performClean(@NonNull final Context ctx) throws IOException {
674
            final File targetFolder = ctx.getTarget();
675
676
            if (targetFolder == null) {
677
                return null;
678
            }
679
680
            File tagFile = new File(targetFolder, TAG_FILE_NAME);
681
682
            if (!tagFile.exists()) {
683
                return null;
684
            }
685
686
            try {
687
                SourceUtils.waitScanFinished();
688
            } catch (InterruptedException e) {
689
                //Not Important
690
                LOG.log(Level.FINE, null, e);
691
                return false;
692
            }
693
694
            delete(targetFolder, false);
695
            delete(tagFile, true);
696
697
            return null;
698
        }
699
        
700
        private Boolean performSync(@NonNull final Context ctx) throws IOException {
701
            final URL sourceRoot = ctx.getSourceRoot();
702
            final File targetFolder = ctx.getTarget();
703
            final boolean copyResources = ctx.isCopyResources();
704
            final boolean keepResourceUpToDate = ctx.isKeepResourcesUpToDate();
705
            final Object context = ctx.getOwner();
706
        
707
            if (targetFolder == null) {
708
                return null;
709
            }
710
        
711
            try {
712
                SourceUtils.waitScanFinished();
713
            } catch (InterruptedException e) {
714
                //Not Important
715
                LOG.log(Level.FINE, null, e);
716
                return null;
717
            }
718
719
            if (JavaIndex.ensureAttributeValue(sourceRoot, DIRTY_ROOT, null)) {
720
                IndexingManager.getDefault().refreshIndexAndWait(sourceRoot, null);
721
            }
722
723
            if (JavaIndex.getAttribute(sourceRoot, DIRTY_ROOT, null) != null) {
724
                return false;
725
            }
726
        
727
            FileObject[][] sources = new FileObject[1][];
728
        
729
            if (!protectAgainstErrors(targetFolder, sources, context)) {
730
                return false;
731
            }
732
        
733
            File tagFile = new File(targetFolder, TAG_FILE_NAME);
734
            File tagUpdateResourcesFile = new File(targetFolder, TAG_UPDATE_RESOURCES);
735
            final boolean forceResourceCopy = copyResources && keepResourceUpToDate && !tagUpdateResourcesFile.exists();
736
            final boolean cosActive = tagFile.exists();
737
            if (cosActive && !forceResourceCopy) {
738
                return true;
739
            }
740
741
            if (!cosActive) {
742
                delete(targetFolder, false/*#161085: cleanCompletely*/);
743
            }
744
745
            if (!targetFolder.exists() && !targetFolder.mkdirs()) {
746
                throw new IOException("Cannot create destination folder: " + targetFolder.getAbsolutePath());
747
            }
748
749
            sources(targetFolder, sources);
750
751
            for (int i = sources[0].length - 1; i>=0; i--) {
752
                final FileObject sr = sources[0][i];
753
                if (!cosActive) {
754
                    URL srURL = sr.toURL();
755
                    File index = JavaIndex.getClassFolder(srURL, true);
756
757
                    if (index == null) {
758
                        //#181992: (not nice) ignore the annotation processing target directory:
759
                        if (srURL.equals(AnnotationProcessingQuery.getAnnotationProcessingOptions(sr).sourceOutputDirectory())) {
760
                            continue;
761
                        }
762
763
                        return null;
764
                    }
765
766
                    copyRecursively(index, targetFolder);
767
                }
768
769
                if (copyResources) {
770
                    Set<String> javaMimeTypes = COSSynchronizingIndexer.gatherJavaMimeTypes();
771
                    String[]    javaMimeTypesArr = javaMimeTypes.toArray(new String[0]);
772
773
                    copyRecursively(sr, targetFolder, javaMimeTypes, javaMimeTypesArr);
774
                }
775
            }
776
777
            if (!cosActive) {
778
                new FileOutputStream(tagFile).close();
779
            }
780
781
            if (keepResourceUpToDate)
782
                new FileOutputStream(tagUpdateResourcesFile).close();
783
        
784
            return true;
785
        }
786
        
787
        private Boolean performUpdate(@NonNull final Context ctx) throws IOException {
788
            final Iterable<? extends File> deleted = ctx.getDeleted();
789
            final Iterable<? extends File> updated = ctx.getUpdated();
790
            final boolean resource = ctx.isCopyResources();
791
            final File cacheRoot = ctx.getCacheRoot();
792
            if (!deleted.iterator().hasNext() && !updated.iterator().hasNext()) {
793
                return null;
794
            }
795
            File targetFolder = ctx.getTarget();
796
            if (targetFolder == null) {
797
                return null;
798
            }
799
            if (!isUpdateClasses(targetFolder)) {
800
                return null;
801
            }
802
803
            if (resource && !isUpdateResources(targetFolder)) {
804
                return null;
805
            }
806
        
807
            List<File> updatedFiles = new LinkedList<>();
808
809
            for (File deletedFile : deleted) {
810
                final String relPath = relativizeFile(cacheRoot, deletedFile);
811
                if (relPath == null) {
812
                    throw new IllegalArgumentException (String.format(
813
                        "Deleted file: %s is not under cache root: %s, (normalized file: %s).", //NOI18N
814
                        deletedFile.getAbsolutePath(),
815
                        cacheRoot.getAbsolutePath(),
816
                        FileUtil.normalizeFile(deletedFile).getAbsolutePath()));
817
                }
818
                File toDelete = resolveFile(targetFolder, relPath);
819
820
                toDelete.delete();
821
                updatedFiles.add(toDelete);
822
            }
823
824
            for (File updatedFile : updated) {
825
                final String relPath = relativizeFile(cacheRoot, updatedFile);
826
                if (relPath == null) {
827
                    throw new IllegalArgumentException (String.format(
828
                        "Updated file: %s is not under cache root: %s, (normalized file: %s).", //NOI18N
829
                        updatedFile.getAbsolutePath(),
830
                        cacheRoot.getAbsolutePath(),
831
                        FileUtil.normalizeFile(updatedFile).getAbsolutePath()));
832
                }
833
                File target = resolveFile(targetFolder, relPath);                        
834
835
                try {
836
                    copyFile(updatedFile, target);
837
                    updatedFiles.add(target);
838
                } catch (IOException ex) {
839
                    Exceptions.printStackTrace(ex);
840
                }
841
            }
842
            ctx.filesUpdated(updatedFiles);
843
            return true;
844
        }
845
        
846
        private boolean isUpdateClasses(@NullAllowed final File targetFolder) {
847
            if (targetFolder == null) {
848
                return false;
849
            }
850
            return new File(targetFolder, TAG_FILE_NAME).exists();
851
        }
852
        
853
        private boolean isUpdateResources(@NullAllowed final File targetFolder) {
854
            if (targetFolder == null) {
855
                return false;
856
            }
857
            return new File(targetFolder, TAG_UPDATE_RESOURCES).exists();
858
        }
859
    }
860
    
861
    @ServiceProvider(service = CompileOnSaveAction.Provider.class, position = Integer.MAX_VALUE)
862
    public static final class Provider implements CompileOnSaveAction.Provider {
863
        @Override
864
        public CompileOnSaveAction forRoot(@NonNull final URL root) {
865
            return new DefaultCompileOnSaveAction(root);
866
        }        
797
    }
867
    }
798
}
868
}

Return to bug 252595