Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolaiparlog committed Nov 22, 2014
2 parents 892d3f9 + 722fcec commit 998aefa
Show file tree
Hide file tree
Showing 81 changed files with 7,022 additions and 762 deletions.
30 changes: 27 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ This somewhat vague sentiment does not translate to quality! The code is clean,

These features are present in the latest release:

* [ControlPropertyListener](https://github.com/CodeFX-org/LibFX/wiki/ControlPropertyListener): creating listeners for the property map of JavaFX controls
* [ListenerHandle](https://github.com/CodeFX-org/LibFX/wiki/ListenerHandle): encapsulating an observable and a listener for easier add/remove of the listener
* [Nestings](https://github.com/CodeFX-org/LibFX/wiki/Nestings): using all the power of JavaFX' properties for nested object aggregations
* [SerializableOptional](https://github.com/CodeFX-org/LibFX/wiki/SerializableOptional): serializable wrapper for `Optional`
* [WebViewHyperlinkListener](https://github.com/CodeFX-org/LibFX/wiki/WebViewHyperlinkListener): add hyperlink listeners to JavaFX' `WebView`


## Documentation

Expand All @@ -22,7 +27,25 @@ License details can be found in the *LICENSE* file in the project's root folder.

## Releases

Releases are published [here](https://github.com/CodeFX-org/LibFX/releases). The release notes also contain the Maven coordinates for each version available in Maven Central.
Releases are published [on GitHub](https://github.com/CodeFX-org/LibFX/releases). The release notes also contain a link to the artifact in Maven Central and its coordinates.

The current version is [0.2.0](http://search.maven.org/#artifactdetails|org.codefx.libfx|LibFX|0.2.0|jar):

**Maven**:

``` XML
<dependency>
<groupId>org.codefx.libfx</groupId>
<artifactId>LibFX</artifactId>
<version>0.2.0</version>
</dependency>
```

**Gradle**:

```
compile 'org.codefx.libfx:LibFX:0.2.0'
```

## Development

Expand All @@ -42,7 +65,7 @@ The library has its home on [GitHub](https://github.com/CodeFX-org/LibFX) where

I have a blog at [codefx.org](http://blog.codefx.org) where I might occasionally blog about **LibFX**. Those posts are filed under [their own tag](http://blog.codefx.org/tag/libfx/).

I use Eclipse and my project settings (like compiler warnings, formatter and save actions) can be found in the repository folder **.settings**. I do this to make it easier for contributors to cope with my obsession for warning free and consistently formatted code.
I use Eclipse and my project settings (like compiler warnings, formatter and save actions) can be found in the repository folder **.settings**. I know this is a little unusual but it makes it easier for contributors to cope with my obsession for warning free and consistently formatted code.

## Contact

Expand All @@ -51,4 +74,5 @@ CodeFX

Web: http://codefx.org <br>
Mail: [email protected] <br>
Key: http://keys.gnupg.net/pks/lookup?op=vindex&search=0xA47A795BA5BF8326 <br>
Twitter: https://twitter.com/nipafx<br>
PGP-Key: http://keys.gnupg.net/pks/lookup?op=vindex&search=0xA47A795BA5BF8326 <br>
35 changes: 31 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>org.codefx.libfx</groupId>
<artifactId>LibFX</artifactId>
<version>0.1.1</version>
<version>0.2.0</version>
<packaging>jar</packaging>

<!-- PROJECT META INFORMATION -->
Expand Down Expand Up @@ -67,17 +67,25 @@

<dependencies>
<dependency>
<!-- JUNIT for automated tests -->
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<!-- MOCKITO to mock instances for automated tests -->
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
<dependency>
<!-- NEKOHTML to parse HTML to DOM Documents for tests -->
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
<version>1.9.21</version>
</dependency>
</dependencies>

<build>
Expand All @@ -95,7 +103,7 @@
<!-- specify using Java 8 -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
Expand All @@ -105,7 +113,7 @@
<!-- create sources.jar -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.3</version>
<version>2.4</version>
<executions>
<execution>
<id>attach-sources</id>
Expand All @@ -119,7 +127,7 @@
<!-- create javadoc.jar -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<version>2.10.1</version>
<executions>
<execution>
<id>attach-javadocs</id>
Expand All @@ -128,6 +136,25 @@
</goals>
</execution>
</executions>
<configuration>
<!-- force links to Java 8 documentation (should happen automatically
but doesn't) -->
<javaApiLinks>
<property>
<name>api_1.8</name>
<value>https://docs.oracle.com/javase/8/docs/api/</value>
</property>
</javaApiLinks>
<!-- add license notice -->
<bottom>
<![CDATA[
This documentation is licensed under
<a href="https://creativecommons.org/licenses/by/4.0/" title="CC-BY 4.0">CC-BY 4.0</a>,
attributed to Nicolai Parlog from
<a href="http://codefx.org" title="CodeFX">CodeFX</a>.
]]>
</bottom>
</configuration>
</plugin>
<plugin>
<!-- sign all jars and pom -->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
package org.codefx.libfx.control;

import java.util.function.Consumer;

import javafx.collections.FXCollections;
import javafx.collections.ObservableMap;

import org.codefx.libfx.control.properties.ControlProperties;
import org.codefx.libfx.control.properties.ControlPropertyListenerHandle;

/**
* Demonstrates how to use the {@link ControlPropertyListenerHandle} and its builder.
*/
@SuppressWarnings("static-method")
public class ControlPropertyListenerDemo {

// #region CONSTRUCTION & MAIN

/**
* Creates a new demo.
*/
private ControlPropertyListenerDemo() {
// nothing to do
}

/**
* Runs this demo.
*
* @param args
* command line arguments (will not be used)
*/
public static void main(String[] args) {
ControlPropertyListenerDemo demo = new ControlPropertyListenerDemo();

demo.simpleCase();
demo.attachAndDetach();

demo.timeNoTypeCheck();
demo.timeWithTypeCheck();

demo.castVsTypeChecking();
}

// #end CONSTRUCTION & MAIN

// #region DEMOS

/**
* Demonstrates the simple case, in which a value processor is added for some key.
*/
private void simpleCase() {
ObservableMap<Object, Object> properties = FXCollections.observableHashMap();

// build and attach the listener
ControlProperties.<String> on(properties)
.forKey("Key")
.processValue(value -> System.out.println(" -> " + value))
.buildAttached();

// set values of the correct type for the correct key
System.out.print("Set \"Value\" for the correct key for the first time: ");
properties.put("Key", "Value");
System.out.print("Set \"Value\" for the correct key for the second time: ");
properties.put("Key", "Value");

// set values of the wrong type:
System.out.println("Set an Integer for the correct key: ... (nothing will happen)");
properties.put("Key", 5);

// set values for the wrong key
System.out.println("Set \"Value\" for another key: ... (nothing will happen)");
properties.put("OtherKey", "Value");

System.out.println();
}

/**
* Demonstrates how a listener can be attached and detached.
*/
private void attachAndDetach() {
ObservableMap<Object, Object> properties = FXCollections.observableHashMap();

// build the listener (but don't attach it yet) and assign it to a variable
ControlPropertyListenerHandle listener = ControlProperties.<String> on(properties)
.forKey("Key")
.processValue(value -> System.out.println(" -> " + value))
.buildDetached();

// set a value when the listener is not yet attached
System.out.println(
"Set \"ExistingValue\" before attaching the listener: ... (nothing will happen)");
properties.put("Key", "ExistingValue");

// now attach the listener
System.out.print("When the listener is set, \"ExistingValue\" is processed and removed: ");
listener.attach();

System.out.print("Set \"Value\": ");
properties.put("Key", "Value");

// detach the listener
listener.detach();
System.out.println("Set \"UnnoticedValue\" when the listener is detached: ... (nothing will happen)");

System.out.println();
}

/**
* Measures the time it takes to get a lot of {@link ClassCastException}.
*/
private void timeNoTypeCheck() {
ObservableMap<Object, Object> properties = FXCollections.observableHashMap();

Consumer<String> unreached = value -> {
throw new RuntimeException("Should not be executed!");
};

// build and a attach a listener which does no type check before cast
ControlProperties.<String> on(properties)
.forKey("Key")
.processValue(unreached)
.buildAttached();

// add a couple of values of the wrong type to average the time that takes
Integer valueOfWrongType = 5;
int runs = (int) 1e5;
long startTimeInNS = System.nanoTime();

for (int i = 0; i < runs; i++)
properties.put("Key", valueOfWrongType);

long endTimeInNS = System.nanoTime();
long timePerRunInNS = (endTimeInNS - startTimeInNS) / runs;
System.out.println("For unchecked casts, adding a value of the wrong type takes ~" + timePerRunInNS + " ns.");

System.out.println();
}

/**
* Demonstrates how type checking increases performance if values of an incorrect type are added frequently.
*/
private void timeWithTypeCheck() {
ObservableMap<Object, Object> properties = FXCollections.observableHashMap();

Consumer<String> unreached = value -> {
throw new RuntimeException("Should not be executed!");
};

// build and a attach a listener which does a type check before cast
ControlProperties.<String> on(properties)
.forKey("Key")
.forValueType(String.class)
.processValue(unreached)
.buildAttached();

// add a couple of values of the wrong type to average the time that takes
Integer valueOfWrongType = 5;
int runs = (int) 1e5;
long startTimeInNS = System.nanoTime();

for (int i = 0; i < runs; i++)
properties.put("Key", valueOfWrongType);

long endTimeInNS = System.nanoTime();
long timePerRunInNS = (endTimeInNS - startTimeInNS) / runs;
System.out.println("For checked casts, adding a value of the wrong type takes ~" + timePerRunInNS + " ns.");

System.out.println();
}

// #end DEMOS

/**
* TODO (nipa): I don't get it. The simple test below clearly shows that raising an exception takes about 6.000 ns.
* So why the hell does {@link #timeNoTypeCheck()} run way faster than that?!
* <p>
* Some days later: I ran this again and discovered that the time difference is now very measurable and looks
* correct. Perhaps some JVM optimization because I ran it so often?
*/
private void castVsTypeChecking() {
int runs = (int) 1e5;
Object integer = 3;

// CAST
long start = System.nanoTime();
for (int i = 0; i < runs; i++)
try {
String string = (String) integer;
System.out.println(string);
} catch (ClassCastException e) {
// do nothing
}
long end = System.nanoTime();
System.out.println("Each unchecked cast took ~" + (end - start) / runs + " ns.");

// TYPE CHECK
start = System.nanoTime();
for (int i = 0; i < runs; i++)
if (String.class.isInstance(integer)) {
String bar = (String) integer;
System.out.println(bar);
}
end = System.nanoTime();
System.out.println("Each type check took ~" + (end - start) / runs + " ns.");
}
}
Loading

0 comments on commit 998aefa

Please sign in to comment.