Monday, October 8, 2018

Migrating from Dragula to Shopify/Draggable/Sortable

In the following code snippets the variable draggable refers to a Draggable.Sortable instance.
You can also check out the fiddle.

Add revertOnSpill functionality to Sortable

var outContainer;
draggable.on('drag:out:container', (e) => {
 outContainer = e.data.overContainer;
});
draggable.on('sortable:stop', (e) => {
 var newContainer = e.data.newContainer;
  var spill = outContainer && outContainer === newContainer;
  if (spill) {
    var oldContainer = e.data.oldContainer;
    var oldContainerChildren = draggable.getDraggableElementsForContainer(oldContainer);
    var emptyOldContainer = !oldContainerChildren.length;
    var source = e.data.dragEvent.data.source;
    if (emptyOldContainer) {
     oldContainer.appendChild(source);
    } else {
      var oldIndex = e.data.oldIndex;
     oldContainer.insertBefore(source, oldContainer.children[oldIndex]);
    }    
  }
});

Figuring out Dragula library usage

The library in use: https://cdnjs.cloudflare.com/ajax/libs/dragula/3.7.2/dragula.js
plus its CSS: https://cdnjs.cloudflare.com/ajax/libs/dragula/3.7.2/dragula.css

Default configuration

HTML body content

<ul class="container">
<li class="draggable">1</li>
<li class="draggable">2</li>
<li class="draggable">3</li>
</ul>
<ul class="container">
<li class="draggable">1</li>
<li class="draggable">2</li>
<li class="draggable">3</li>
</ul>
<ul class="container">
<li class="draggable">1</li>
<li class="draggable">2</li>
<li class="draggable">3</li>
</ul>

JavaScript content

var containers = Array.prototype.slice.call(document.querySelectorAll('.container'));
var drake = window.dragula(containers);

How it looks like

With its CSSWithout its CSS
See the fiddle for yourself!

Figuring out Shopify/Draggable/Sortable library usage

The library in use: https://cdn.jsdelivr.net/npm/@shopify/draggable@1.0.0-beta.8/lib/draggable.bundle.js

Default configuration

The following plugins come with Draggable by default:
  • Announcement - announcing draggable events for a screenreader.
  • Focusable - adds tabindex to all draggable and container elements and thus makes them focusable
  • Mirror - creates a similar element to the original that will follow the cursor
  • Scrollable - scrolls the container while dragging when container edge is reached
Sortable is built on top of Draggable, so it has also all of this.

HTML body content

<ul class="container">
<li class="draggable">1</li>
<li class="draggable">2</li>
<li class="draggable">3</li>
</ul>
<ul class="container">
<li class="draggable">1</li>
<li class="draggable">2</li>
<li class="draggable">3</li>
</ul>
<ul class="container">
<li class="draggable">1</li>
<li class="draggable">2</li>
<li class="draggable">3</li>
</ul>

JavaScript content

var containers = document.querySelectorAll('.container');
var draggable = new window.Draggable.Sortable(containers, {
  draggable: '.draggable'
});

How it looks like

See the fiddle for yourself!

Friday, August 3, 2018

Developing Gramps on Ubuntu 16.04

Gramps is an open-source genealogy research tool written in Python3.

If you want to develop it on Windows, here are the instructions to do that.

To get started you'll need to read through a lot of documentation.
As for Python3 these can help:

Dependencies needed for Gramps

...are listed in the README.md:

EDIT: I just noticed this in the Getting Started and it might work instead of all those individual installations below, but you have to enable source code sources before you can do it: apt-get build-dep gramps

Instructions for Ubuntu 16.04:
  • Python 3.2 or greater: already installed
    • install python3-pip for further usage: sudo apt-get install python3-pip
  • GTK 3.10 or greater: already installed
  • pygobject 3.12 or greater: install it by running the following command from terminal:
    sudo apt install python-gi python-gi-cairo python3-gi python3-gi-cairo gir1.2-gtk-3.0
  • GObject Introspection bindings:
    • cairo 1.13.1 or greater: already installed (libcairo2)
    • Pycairo 1.13.3 or greater: already installed (python-cairo)
    • pango: already installed (libpango-1.0-0, libpango1.0-0)
    • pangocairo: already installed (libpangocairo-1.0-0)
    • librsvg2: already installed (librsvg2-2)
    • xdg-utils: already installed (xdg-utils)
    • bsddb3:
      • install BerkeleyDB dev package: sudo apt-get install libdb5.3-dev
      • install bsddb3: pip3 install bsddb3 --user
    • sqlite3: should be already installed with python.
  • language-pack-gnome-xx: i.e.: sudo apt-get install language-pack-gnome-en
  • osmgpsmap: 
    • install osmgpsmap: sudo apt-get install libosmgpsmap-1.0-1
    • and the python bindings: sudo apt-get install gir1.2-osmgpsmap-1.0
  • Graphviz: sudo apt-get install graphviz
  • PyICU: 
    • install ICU dev package: sudo apt-get install libicu-dev
    • and PyICU: pip3 install pyicu --user
  • Ghostscript: already installed
  • GExiv2: to get rid of the error popup when running Gramps
    • sudo apt-get install libgexiv2-2
    • sudo apt-get install gir1.2-gexiv2-0.10

Try what you cooked

  • download: git clone https://github.com/gramps-project/gramps.git Gramps
  • build: python3 setup.py build
  • DON'T INSTALL IT! (that would be python3 setup.py install)
  • run: python3 Gramps.py

Tuesday, July 31, 2018

How to subtract number of years, months and day from dates before 1900 in Google Spreadsheets?

Apparently Google Spreadsheet in incapable of handling dates before 1900 in the DATE, YEAR, MONTH, DAY functions.
So while in LibreOffice you can do this:
=DATE(YEAR(A1)-B1, MONTH(A1)-C1, DAY(A1)-D1)
where A1 is a date before 1900 and B1, C1, D1 are integers for the years, months, days to subtract
in Google Spreadsheet this results in a #NUM! error.

A workaround would be:
YEAR: =DATEDIF(-693990,A1,"Y")
MONTH: =DATEDIF(-693990,A1,"YM")
but this does not work properly with days.
A different way to put it is:
YEAR: =YEAR(A1+693961)-1900
MONTH: =MONTH(A1+693961)
DAY: =DAY(A1+693961)

However this does not help with subtracting and reconstructing the date.
So my workaround was to add a new spreadsheet function, because that can be written in javascript and these limitations do not apply.

From Tools > Script editor create a new script. Paste the following function and from Run > Test as add-on... add it to the sheet you need it in.

function dateSubtract(originalDate, yearsToSubtract, monthsToSubtract, daysToSubtract) {
  var date = new Date(originalDate);  
  date.setDate(date.getDate() - daysToSubtract);
  date.setMonth(date.getMonth() - monthsToSubtract);
  date.setFullYear(date.getFullYear() - yearsToSubtract);
  return date;
}
Than in the spreadsheet you can use this function like this:
=DATESUBTRACT(A1, B1, C1, D1)

What did I need this for?
In genealogy research the death records might indicate the age of the person in a years, months, days format. I collected some of these records in a spreadsheet and wanted to calculate the supposed birth date of the deceased.

Friday, June 1, 2018

Maven dependencies basics

Source: https://maven.apache.org/pom.html

Always required in a POM:
  • groupId
  • artifactId
  • version
Dependency hierarchy:
  • All POMs inherit from Maven's Super POM.
    This is why some properties you specify but not use have effect. For example you can set the compiler version with these properties:
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  • To set up a POM hierarchy within your project, all POMs that are specified as <parent> in other POMs or that have <modules> (aggregation aka. multi-module projects) must use <packaging>pom</packaging>.
  • Note:  A POM project may be inherited from - but does not necessarily have any modules that it aggregates. Conversely, a POM project may aggregate projects that do not inherit from it.
Dependency scope:
  • compile - this is the default scope, used if none is specified. Compile dependencies are available in all classpaths. Furthermore, those dependencies are propagated to dependent projects.
  • provided - this is much like compile, but indicates you expect the JDK or a container to provide it at runtime. It is only available on the compilation and test classpath, and is not transitive.
  • runtime - this scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath.
  • test - this scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases. It is not transitive.
  • system - this scope is similar to provided except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository.
Exclusion of transitive dependencies:
  • One by one: specify them one by one
  • All: use the * wildcard for both the groupId and artifactId

Show README and CHANGELOG on Maven Site

It is common that the project's README contains valuable information about the project. For example on a repository's webpage in Gitlab or Github, the README is displayed by default.
Here's a way to publish the README and CHANGELOG markdown files on the project's Maven Site:

In the Site descriptor (/src/site/site.xml) reference the files you want to publish:
<?xml version="1.0" encoding="UTF-8"?>
<project>
  <body>
    <menu name="Documentation">
      <item name="README" href="docs/README.html"/>
      <item name="CHANGELOG" href="docs/CHANGELOG.html" />
    </menu>
  </body>
</project>

In the pre-site build phase copy the files to the markdown resource directory. The site plugin transforms the documentation to HTML and outputs it to the corresponding target directory.
After the site building finished, clean up the duplicate files:
<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <configuration>
          <encoding>${project.build.sourceEncoding}</encoding>
        </configuration>
        <executions>
          <execution>
            <!-- Copy the readme and such files to the site source files so that a page is generated from it. -->
            <id>copy-docs</id>
            <phase>pre-site</phase>
            <goals>
              <goal>copy-resources</goal>
            </goals>
            <configuration>
              <outputDirectory>${basedir}/src/site/markdown/docs</outputDirectory>
              <resources>
                <resource>
                  <directory>${basedir}</directory>
                  <includes>
                    <include>README.md</include>
                    <include>CHANGELOG.md</include>
                  </includes>
                </resource>
              </resources>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-clean-plugin</artifactId>
        <!-- Delete the markdown/docs directory to remove the readme and such duplicate files from the site source files. -->
        <executions>
          <execution>
            <id>clear-docs</id>
            <phase>site</phase>
            <goals>
              <goal>clean</goal>
            </goals>
            <configuration>
              <filesets>
                <fileset>
                  <directory>src/site/markdown/docs</directory>
                  <followSymlinks>false</followSymlinks>
                </fileset>
              </filesets>
              <excludeDefaultDirectories>true</excludeDefaultDirectories>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>

To show the process in terms of file structure:
The normal state:
.
+-- src 
| +-- site
| | +-- site.xml
After the files were copied:
.
+-- src 
| +-- site
| | +-- markdown
| | | +-- docs
| | | | +-- README.md
| | | | +-- CHANGELOG.md
| | +-- site.xml
The output in site target:
.
+-- docs
| +-- README.html
| +-- CHANGELOG.html
+-- index.html

A bonus note regarding the deployment url: it can be changed it the distributionManagement section:
<project>
  ...
  <distributionManagement>
    <site>
      <url>${maven.reports.deployBase}/${project.groupId}</url>
    </site>
  </distributionManagement>
  ...
</project>

Friday, May 18, 2018

What you absolutely need to know about JavaScript

When it comes to making websites HTML, CSS and JS are the three core technologies you need to know.
Both JS and CSS rely on the tree structure of the web page. The tree structure is represented with the Document Object Model (DOM).

Browser compatibility

Websites are rendered by the web browsers thus JS is also interpreted by the browsers, and this is why there are differences between their capabilities in interpreting JS.
You have two options here:
  • check for browser compatibility yourself e.g. in the MDN JS Reference
  • use a compiler/polyfill e.g. Babel to take care of compatibility for you

The standardized version of JavaScript is called ECMAScript. It has several editions since 1997. The 6th edition called ES6 and ES2015 added a significant new syntax and new libraries that generally make development easier, however it is not supported by all browsers.

The main browser compatibility issue is with Internet Explorer. Currently (2018/05) the only supported Internet Explorer version is IE11 and it only gets technical support and security updates from Microsoft - instead of being developed further it is replaced by Microsoft Edge.
If you want to support IE11 you have two options here:

Why you want to use the enhanced Javascript syntax

Probably the most important new feature of ES2015 is the use of promises instead of callbacks. This was then further simplified by the introduction of the async/await keywords in ES2017. Here is a demonstration of the above, and here is a guide to picking the right asynchronous way out of the three. 
If this is not enough see what else is in ES2015.

Frameworks and Libraries

There are many frameworks and libraries to help developers implement frontend applications. If you read How it feels to learn JavaScript in 2016 you'll get a general idea of how fast the frontend technology is changing.
Here are some recent trends:

Running Javascript outside of the browser

It is possible to use JS outside of the context of a web browser, for example it can be used to write server code or desktop applications. The most well known JS runtime environment is NodeJS. It has a package manager called npm.
If you want to run tests, compile code, do style checks it is a convenient way to run these commands and maintain these dependencies through npm.

Further readings

Documentation

Thursday, May 17, 2018

Some random notes about Jenkins pipelines

First of all, there are two type of syntax:
  • Declarative
    • has a single pipeline{} block on the top level
    • it is possible to use Scripted pipeline syntax within a script{} step
  • Scripted
    • has stage('name'){} or node{} blocks on the top level
There are subtle differences:
  • Changing directories
    • in Scripted you can use the dir(){} block
    • in Declarative you cannot. One way to change directories is to change directory on every line of your commands.

Declarative pipeline

Workspace

Apparently the workspace is something that is shared within the pipeline. It doesn't matter how many different docker images you run your steps in, they will all work in the exact same directory and whatever they change will be inherited by the next stage too.

Example: Same and different agents across multiple stages modifying the same workspace

pipeline {
    agent none
    stages {
        stage ('Node build') {
            agent {
                docker {
                    image 'node:8.7.0'
                }
            }
            steps {
                sh 'npm install'
                sh 'npm run build'
            }
        }
        stage ('Node test') {
            agent {
                docker {
                    image 'node:8.7.0'
                }
            }
            steps {
                sh 'npm run test'
            }
        }
        stage ('Maven build') {
            agent {
                docker {
                    image 'maven:3.3.9'
                }
            }
            steps {
                sh 'mvn clean install'
            }
        }
    }
}

Sunday, May 6, 2018

Some common HSQLDB stored procedures

HSQLDB is the default database of LibreOffice Base.
Here are some of the stored procedures that might come in handy:

Numerical built-in Functions / Stored Procedures
ABS(d) returns the absolute value of a double value
CEILING(d) returns the smallest integer that is not less than d
FLOOR(d) returns the largest integer that is not greater than d
MOD(a,b) returns a modulo b
POWER(a,b) returns a raised to the power of b
RAND() returns a random number x bigger or equal to 0.0 and smaller than 1.0
ROUND(a,b) rounds a to b digits after the decimal point
SQRT(d) returns the square root
String built-in Functions / Stored Procedures
CONCAT(str1,str2) returns str1 + str2
LENGTH(s) returns the number of characters in s
LOWER(s) converts s to lower case
REPEAT(s,count) returns s repeated count times
REPLACE(s,replace,s2) replaces all occurrences of replace in s with s2
SUBSTRING(s,start[,len]) returns the substring starting at start (1=left) with length len
TRIM( LEADING ... FROM ...) TRIM([{LEADING | TRAILING | BOTH}] FROM <string expression>)
UPPER(s) converts s to upper case
Date/Time built-in Functions / Stored Procedures
CURRENT_DATE returns the current date
CURRENT_TIME returns the current time
CURRENT_TIMESTAMP returns the current timestamp
DATEDIFF(string, datetime1, datetime2) returns the count of units of time elapsed from datetime1 to datetime2.
The string indicates the unit of time and can have the following values (both the long and short form of the strings can be used):
  • 'ms'='millisecond', 
  • 'ss'='second',
  • 'mi'='minute',
  • 'hh'='hour', 
  • 'dd'='day', 
  • 'mm'='month', 
  • 'yy' = 'year'
DAYOFMONTH(date) returns the day of the month (1-31)
DAYOFWEEK(date) returns the day of the week (1 means Sunday)
HOUR(time) return the hour (0-23)
MINUTE(time) returns the minute (0-59)
MONTH(date) returns the month (1-12)
SECOND(time) returns the second (0-59)
YEAR(date) returns the year
System built-in Functions / Stored Procedures
CAST(term AS type)
CONVERT(term,type)
converts exp to another data type
COALESCE(expr1,expr2,expr3,...) if expr1 is not null then it is returned else, expr2 is evaluated and if not null it is returned and so on
CASE v1 WHEN... CASE v1 WHEN v2 THEN v3 [ELSE v4] END
when v1 equals v2 return v3 [otherwise v4 or null if there is no ELSE]
CASE WHEN... CASE WHEN expr1 THEN v1[WHEN expr2 THEN v2] [ELSE v4] END
when expr1 is true return v1 [optionally repeated for more cases] [otherwise v4 or null if there is no ELSE]


Notational Conventions used above
  • [A] means A is optional.
  • { B | C } means either B or C must be used.
  • [{ B | C }] means either B or C may optionally be used, or nothing at all.
  • ( and ) are the actual characters '(' and ')' used in statements.
  • UPPERCASE words are keywords

Java basics - random numbers

Math.random()

This is as simple as it gets:
Returns a double value with a positive sign, greater than or equal to 0.0 and less than 1.0.

Usage:
double d = Math.random();

Random class methods

An instance of this class is used to generate a stream of pseudorandom numbers.
Here are some of the methods:
Method Return type Return value
Get the next value in the stream:
nextBoolean() boolean true or false
nextDouble() double between 0.0d (inclusive) and 1.0d (exclusive)
nextFloat() float between 0.0f (inclusive) and 1.0f (exclusive)
nextInt() int any int within int range
nextLong() long any long within long range
Generate a random integer in a given range:
nextInt(int n) int between 0 (inclusive) and the specified value (exclusive) 
Usage:
Random r = new Random();
double d = r.nextDouble();

ThreadLocalRandom class methods

A random number generator isolated to the current thread.
It's a subclass of java.util.Random, so all methods that are in Random can be used here too.
Get the instance with ThreadLocalRandom.current()
Method Return type Return value
nextDouble(double n) double between 0 (inclusive) and the specified value (exclusive)
nextDouble(double least, double bound) double between the given least value (inclusive) and bound (exclusive)
nextInt(int least, int bound) int between the given least value (inclusive) and bound (exclusive)
nextLong(long n) long between 0 (inclusive) and the specified value (exclusive)
nextLong(long least, long bound) long between the given least value (inclusive) and bound (exclusive)
Usage:
double d = ThreadLocalRandom.current().nextDouble(0.0, 1.0);

Wednesday, May 2, 2018

Java basics - Enum

An enum type is a special data type that enables for a variable to be a set of predefined constants.
An enum is static by default. There is an Enum class too, read about the differences here.

The simplest use case

public enum Status {
 TODO, DOING, DONE
}

Properties and more

The enum values can have properties assigned. The type may contain other methods that calculate using the values. It may also contain a main method.
Note: If the values have properties, these conditions must be fulfilled:
  • Semicolon after the last value declaration
  • A field for each property
  • Required arguments constructor
public enum Status {
 TODO(0, "A"), 
 DOING(1, "B"), 
 DONE(2, "C");
 
 private final int code;
 private final String variant;
 
 private Status(int code, String variant) {
  this.code = code;
  this.variant = variant;
 }

 public int getCode() {
  return code;
 }

 public String getVariant() {
  return variant;
 }
}

EnumMap

It is recommended to use EnumMap if the key is enum. The syntax to create one is:
Map<Status, Integer> map = new EnumMap<Status, Integer>(Status.class);
Then it can be used like any map with put and get.

Monday, April 30, 2018

Comparing objects in Java

This is our example class:
public class Person {
 private final int age;
 private final String name;
 public Person(int age, String name) {
  this.age = age;
  this.name = name;
 }
 public int getAge() {
  return age;
 }
 public String getName() {
  return name;
 }
}
When implementing this.compareTo(that) or compare(this, that) follow these rules:
  • return -1 if this is less than that
  • return 0 if this equals to that, also should give the same result as Object.equals()
  • return 1 if this is greater than that
The two basic ways to achieve comparison are the followings:

Implementing the Comparable interface

public class Person implements Comparable<Person> {
 // ...
 @Override
 public int compareTo(Person that) {
  if (this.getName().compareTo(that.getName()) == 0) {
   if (this.getAge() < that.getAge()) return -1;
   else if (this.getAge() > that.getAge()) return 1;
   else return 0;
  }
  else return this.getName().compareTo(that.getName());
 }
 // ...
}

Using a Comparator

The Comparator interface can be implemented by an anonymous class in the class or at the place of usage like when giving it to Collections.sort() as an argument.
If only the compare() method is implemented that's enough.
public class Person {
 // ...
 public static Comparator<Person> byAge() {
  return new Comparator<Person>() {
   @Override
   public int compare(Person p1, Person p2) {
    if (p1.getAge() < p2.getAge()) return -1;
    else if (p1.getAge() > p2.getAge()) return 1;
    else return p1.getName().compareTo(p2.getName());
   }
  };
 }

 public static Comparator<Person> byName() {
  return new Comparator<Person>() {
   @Override
   public int compare(Person p1, Person p2) {
    if (p1.getName().equals(p2.getName())) {
     if (p1.getAge() < p2.getAge()) return -1;
     else if (p1.getAge() > p2.getAge()) return 1;
     else return 0;
    }
    else return p1.getName().compareTo(p2.getName());
   }
  };
 }
 // ...
}

Sorting Objects

Now that we have ways to compare the object, we can use that for sorting:
private Person a = new Person(20, "Amanda");
private Person b = new Person(20, "Anna");
private Person c = new Person(21, "Anna");
private Person d = new Person(19, "Beata");
private List<Person> list = Arrays.asList(b, a, d, c);

// using the Comparable implementation
Collections.sort(list);
assertEquals(Arrays.asList(a, b, c, d), list);
Collections.reverse(list);
assertEquals(Arrays.asList(d, c, b, a), list);
Collections.sort(list, Collections.reverseOrder());
assertEquals(Arrays.asList(d, c, b, a), list);

// using the Comparator implementation
Collections.sort(list, Person.byAge());
assertEquals(Arrays.asList(d, a, b, c), list);
Collections.sort(list, Collections.reverseOrder(Person.byAge()));
assertEquals(Arrays.asList(c, b, a, d), list);
Further readings:

Sunday, April 22, 2018

Eclipse: frequently used keyboard shortcuts

File
Save Ctrl+S
Save All Shift+Ctrl+S
Refactor - Java
Extract Local Variable Shift+Alt+L
Extract Method Shift+Alt+M
Inline Shift+Alt+I
Rename - Refactoring Shift+Alt+R
Source
Format Shift+Ctrl+F
Organize Imports Shift+Ctrl+O
Toggle Comment Shift+Ctrl+C
Ctrl+/
Ctrl+7
Toggle Mark Occurrences Shift+Alt+O
Text Editing
Copy Lines Ctrl+Alt+Down
Delete Line Ctrl+D
Duplicate Lines Ctrl+Alt+Up
Insert Line Above Current Line Shift+Ctrl+Enter
Insert Line Below Current Line Shift+Enter
Join Lines Ctrl+Alt+J
Line End End
Line Start Home
Move Lines Down Alt+Down
Move Lines Up Alt+Up
Toggle Block Selection Shift+Alt+A

Saturday, April 21, 2018

Java basics - localized Strings

Formatting numbers with Formatter in (Hungarian) locale

Java provides two ways to format text: using Formatter or using Format. This part presents the use of Formatter.

Formatter syntax: %[argument_index$][flags][width][.precision]conversion
Some useful flags:
  • , for including locale-specific grouping separators
  • 0 for zero-padding
  • + for always including a sign
Default behavior: 
  • The output is right-justified within the width
  • Negative numbers begin with a '-' ('\u002d')
  • Positive numbers and zero do not include a sign or extra leading space
  • No grouping separators are included
  • The decimal separator will only appear if a digit follows it

In the following example we'll use this locale:
Locale huLocale = Locale.forLanguageTag("hu-HU");

Non floating-point decimal numbers

remember d for decimal
assertEquals("0009", String.format(huLocale, "%0,4d", 9));
assertEquals("01 000", String.format(huLocale, "%0,6d", 1000));

assertEquals("100000000", String.format(huLocale, "%d", 100000000));
assertEquals("100 000 000", String.format(huLocale, "%,d", 100000000));
assertEquals("+100000000", String.format(huLocale, "%+d", 100000000));

Floating point decimal numbers

remember f for floating point
assertEquals("1000,246800",String.format(huLocale, "%f", 1000.2468));
assertEquals("1 000,246800", String.format(huLocale, "%,f", 1000.2468));
assertEquals("1 000,25", String.format(huLocale, "%,.2f", 1000.2468));

Scientific decimal numbers

assertEquals("1,000247e+03", String.format(huLocale, "%e", 1000.2468));
assertEquals("1,00024680e+03", String.format(huLocale, "%.8e", 1000.2468));

Automatic switching between scientific and floating point formatting

assertEquals("123,456", String.format(huLocale, "%g", 123.456));
assertEquals("123,5", String.format(huLocale, "%.4g", 123.456));
assertEquals("123", String.format(huLocale, "%.3g", 123.456));
assertEquals("1,2e+02", String.format(huLocale, "%.2g", 123.456));

Sources:

Parsing localized (Hungarian) string into double

Locale huLocale = Locale.forLanguageTag("hu-HU");
NumberFormat nf = NumberFormat.getInstance(huLocale);
assertEquals(1.0, nf.parse("1,0").doubleValue(), 1);

Sources:

Sorting in (Hungarian) localized order

To sort in (localized) natural language order one must use a Collator.
Once there is a Collator, it can be used to sort like this:
Collections.sort(list, myCollator);
For preconstructed locales eg. en-US one can use Collator.getInstance(Locale.US);
For other languages one can try Collator.getInstance(Locale.forLanguageTag("hu-HU"));
But if that does not work as expected one has to specify the sorting rules for the language themselves:
List<String> list = Arrays.asList("az", "áll", "CT", "csomag", "ez", "ég", "itt", "így",
  "Ozora", "óvoda", "ötös", "őriz", "uzsonna", "út", "ütő", "űr");

Collator huCollator = Collator.getInstance(Locale.forLanguageTag("hu-HU"));
Collections.sort(list, huCollator);
assertEquals(Arrays.asList("áll", "az", "CT", "csomag", "ég", "ez", "így", "itt",
  "óvoda", "Ozora", "ötös", "őriz", "út", "uzsonna", "ütő", "űr"), list);

String hungarian = "< a,A < á,Á < b,B < c,C < cs,Cs,CS < d,D < dz,Dz,DZ" +
      " < dzs,Dzs,DZS < e,E < é,É < f,F < g,G < gy,Gy,GY < h,H < i,I " +
      "< í,Í < j,J < k,K < l,L < ly,Ly,LY < m,M < n,N < ny,Ny,NY < o,O " +
      "< ó,Ó < ö,Ö < ő,Ő < p,P < q,Q < r,R < s,S < sz,Sz,SZ < t,T < ty,Ty,TY " +
      "< u,U < ú,Ú < ü,Ü < ű,Ű < v,V < w,W < x,X < y,Y < z,Z < zs,Zs,ZS";
Collator collator = new RuleBasedCollator(hungarian);
Collections.sort(list, collator);
assertEquals(Arrays.asList("az", "áll", "CT", "csomag", "ez", "ég", "itt", "így",
  "Ozora", "óvoda", "ötös", "őriz", "uzsonna", "út", "ütő", "űr"), list);

Sources:

Sunday, April 15, 2018

Java basics - working with text

Sources:

About Unicode in Java

Java is using Unicode 16-bit representation internally.
Unicode distinguishes between the association of characters as abstract concepts (e.g., "Greek capital letter omega Ω") to a subset of the natural numbers, called code point on the one hand, and the representation of code points by values stored in units of a computer's memory. The Unicode standard defines seven of these character encoding schemes.
In Java, the 65536 numeric values of Unicode are UTF-16 code units, values that are used in the UTF-16 encoding of Unicode texts. Any representation of Unicode must be capable of representing the full range of code points, its upper bound being 0x10FFFF. Thus, code points beyond 0xFFFF need to be represented by pairs of UTF-16 code units, and the values used with these so-called surrogate pairs are exempt from being used as code points themselves.
The full range of Unicode code points can only be stored in a variable of type int. The actual number of Unicode characters cannot be represented in a char variable.

When to be cautious with Characters?

It is possible that a String value contains surrogate pairs intermingled with individual code units.
In such cases one character can take up two indices in the string.
To verify if the string consists of only individual code units one can use:
s.lenght() == s.codePointCount(0, s.length())
...because String.length() method returns the number of code units, or 16-bit char values, in the string, while the String.codePointCount() method returns the count of the number of characters (including supplementary characters).
If you have to process strings containing surrogate pairs, there's an implementation of a unicode charAt method in this article (using offsetByCodePoints and codePointAt methods of string).

Regarding conversion to uppercase and lowercase, use the String.toUpperCase() and String.toLowerCase() methods only because those handle all cases of conversions correctly compared to the Character implementations.

Working with text in Java

If your editor and file system allow it, you can use Java's native UTF-16 characters directly in your code.
Always use 'single quotes' for char literals and "double quotes" for String literals.
Escape sequences for char and String literals: \b (backspace), \t (tab), \n (line feed), \f (form feed), \r (carriage return), \" (double quote), \' (single quote), and \\ (backslash).

Primitive type: char

char is a single 16-bit Unicode character. It has a minimum value of '\u0000' (or 0) and a maximum value of '\uffff' (or 65,535 inclusive).
Default value: '\u0000'
Check for default value: ch == Character.MIN_VALUE or ch == 0

Non-primitive types: Character and String

How to decide if something is a letter, a digit, or whitespace?

Use Java's built in Character class for that:
char ch = '\u0041';
assertEquals('A', ch );
assertFalse(Character.isDigit(ch));
assertTrue(Character.isLetter(ch));
assertTrue(Character.isLetterOrDigit(ch));
assertFalse(Character.isLowerCase(ch));
assertTrue(Character.isUpperCase(ch));
assertFalse(Character.isSpaceChar(ch));
assertTrue(Character.isDefined(ch));

How to decide if some letter is in the English Alphabet?

char ch = 'A';
assertTrue(((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')));
ch = 'á';
assertFalse(((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')));
Or using regex:
Pattern p = Pattern.compile("[A-Za-z]");
assertTrue(p.matcher("a").find());
assertFalse(p.matcher("Á").find()); 

Sorting text

The default String comparator compares based on the unicode values of characters. (putting uppercase before lowercase, etc.)
To sort in (localized) natural language order one must use a Collator. An example usage in shown is this article, here's a lengthier demonstration, and here's some information on how customize sorting rules.
List list = Arrays.asList("alma", "Anna", "Ági", "ágy");
Collections.sort(list);
assertEquals(Arrays.asList("Anna", "alma", "Ági", "ágy"), list);

Collections.sort(list, String.CASE_INSENSITIVE_ORDER);
assertEquals(Arrays.asList("alma", "Anna", "Ági", "ágy"), list);

Collator huCollator = Collator.getInstance(Locale.forLanguageTag("hu-HU"));
Collections.sort(list, huCollator);
assertEquals(Arrays.asList("Ági", "ágy", "alma", "Anna"), list);

Splitting and joining text

Conversion between String and char

String str = "My fancy text";
char[] chars = str.toCharArray();
String joined = new String(chars);
assertEquals(str, joined);

Splitting and joining Strings

String str = "My fancy text";
String[] splitted = str.split(" ");
String joined = String.join(" ", splitted);
assertEquals(str, joined );

Parsing text

Scanner

A Scanner breaks its input into tokens using a delimiter pattern.
Default delimiter: whitespace. Set it with Scanner.useDelimiter()
Localization for reading numbers: via the Scanner.useLocale(locale) method.
Reset to defaults with Scanner.reset() method.
Delimiters:
  • regex
Navigate with Scanner.next() returns Object between the current and the next delimiter.

BreakIterator

The BreakIterator class implements methods for finding the location of boundaries in text. Instances of BreakIterator maintain a current position and scan over text returning the index of characters where boundaries occur.
Boundaries:
  • Character
  • Word
  • Sentence
  • Line
Navigate with BreakIterator.next() and BreakIterator.previous() - returns next int index of boundary.
For info on usage see the Java Tutorial.

StringTokenizer

StringTokenizer is a legacy class that is retained for compatibility reasons although its use is discouraged in new code. It is recommended that anyone seeking this functionality use the split method of String or the java.util.regex package instead.

String.split()

Splits a string around matches of the given regular expression. Returns an array.
With the regex match it works like Scanner but parses the whole text at once.
Delimiter defaults to whitespace.

Pattern matching

java.util.regex contains classes for matching character sequences against patterns specified by regular expressions.
An instance of the Pattern class represents a regular expression that is specified in string form in a syntax similar to that used by Perl.
Instances of the Matcher class are used to match character sequences against a given pattern. Input is provided to matchers via the CharSequence interface in order to support matching against characters from a wide variety of input sources.

The different matching methods of Matcher

Pattern pattern = Pattern.compile("foo");

// find: all occurrences one by one
assertTrue(pattern.matcher("afooo").find());
assertFalse(pattern.matcher("aooo").find());
// find starting at given index
assertTrue(pattern.matcher("afooo").find(0));
assertTrue(pattern.matcher("afooo").find(1));
assertFalse(pattern.matcher("afooo").find(2));

// lookingAt: like String.startsWith() but with regex
assertTrue(pattern.matcher("fooo").lookingAt());
assertFalse(pattern.matcher("afooo").lookingAt());

// matches: like String.equals() but with regex
assertTrue(pattern.matcher("foo").matches());
assertFalse(pattern.matcher("fooo").matches());

Retrieving matched subsequences 

The explicit state of a matcher includes the start and end indices of the most recent successful match. It also includes the start and end indices of the input subsequence captured by each capturing group in the pattern as well as a total count of such subsequences. This can be used to retrieve what is matched:
Pattern pattern = Pattern.compile("f.o");
Matcher matcher = pattern.matcher("afaoofeoofo");
assertTrue(matcher.find()); // finds the first match
assertEquals("fao", matcher.group()); 
assertTrue(matcher.find()); // finds the second match
assertEquals("feo", matcher.group());
assertFalse(matcher.find()); // no more to find
matcher.reset(); // resets the matcher
assertTrue(matcher.find()); // finds the first match again
assertEquals("fao", matcher.group());

Iterating over the matches

while(matcher.find()) {
 String group = matcher.group();
}

Using capturing groups

Pattern pattern = Pattern.compile("(f(.)o)");
Matcher matcher = pattern.matcher("afaoofeoofuo");
assertEquals(2, matcher.groupCount()); // groups specified in pattern
assertTrue(matcher.find()); // finds the first match again
assertEquals("fao", matcher.group(1)); //referencing the capturing group
assertEquals("a", matcher.group(2)); //referencing the capturing group

Making replacements

Replace the first substring of a string that matches the given regular expression with the given replacement:
  • str.replaceFirst(regex, repl) yields exactly the same result as 
  • Pattern.compile(regex).matcher(str).replaceFirst(repl)
Replace each substring of this string that matches the given regular expression with the given replacement:
  • str.replaceAll(regex, repl) yields exactly the same result as 
  • Pattern.compile(regex).matcher(str).replaceAll(repl)

Making complex replacements

To have more control on the replacement, use Matcher.appendReplacement() with Matcher.appendTail().
The most basic case: replace with fixed string
Pattern p = Pattern.compile("f.o");
Matcher m = p.matcher("afaoofeoofuo");
StringBuffer sb = new StringBuffer(); // the buffer to write the result to
while (m.find()) {
 m.appendReplacement(sb, "-"); // replace the whole match with the given string
}
m.appendTail(sb); // write the rest of the string after the last match to the buffer.
assertEquals("a-o-o-", sb.toString());
A more complex case: replace with multiple capturing groups
Pattern p = Pattern.compile("(f)(.)(o)");
Matcher m = p.matcher("afaoofeoofuo");
StringBuffer sb = new StringBuffer();
while (m.find()) {
 m.appendReplacement(sb, "$1-$3"); // replace only the second group
}
m.appendTail(sb);
assertEquals("af-oof-oof-o", sb.toString());
A more complex case: replace with value from map
Map map = new HashMap<>();
map.put("a", "1"); map.put("e", "2"); map.put("u", "3");
Pattern p = Pattern.compile("(f)(.)(o)");
Matcher m = p.matcher("afaoofeoofuo");
StringBuffer sb = new StringBuffer();
while (m.find()) {
 m.appendReplacement(sb, "$1" + map.get(m.group(2)) + "$3"); // replace only the second group
}
m.appendTail(sb);
assertEquals("af1oof2oof3o", sb.toString());
Note: If you want the replacement to contain $ or \ literals, wrap it in Matcher.quoteReplacement().

Escape special characters with double backslash

Regex patterns are specified within String literals. Java has some reserved escapes like \n for line break, so the regex escapes like \s need to be escaped with an extra \ resulting in \\s for matching a whitespace character.
The string literal "\b", for example, matches a single backspace character when interpreted as a regular expression, while "\\b" matches a word boundary.


Sources:


Friday, April 13, 2018

Some random notes on Java basics

Arrays and Lists

How to initialize an Array?

// Initialize by specifying the length (values default to 0)
int[] a = new int[5];
a[0] = 1;
// Initialize by specifying the values
int[] b = new int[]{1, 0, 0, 0, 0};

How to copy an Array?

int[] source = new int[]{1,2,3,4,5};
// Copy the whole array using Object.clone()
int[] a = source.clone();
// Copy from the beginning to a given index
int[] b = Arrays.copyOf(source, source.length);
// Copy from a given index to a given index
int[] c = Arrays.copyOfRange(source, 0, source.length);
// Copy from source from index to destination from index the given amount of items (void method)
int[] d = new int[source.length];
System.arraycopy(source, 0, d, 0, source.length);

How to initialize a List?

// Initialize with the new keyword
List a = new ArrayList<>();
a.add(1); a.add(0); a.add(0); a.add(0); a.add(0);
// Initialize with Arrays.asList
List b = Arrays.asList(1, 0, 0, 0, 0);

How to copy a List?

List newList = new ArrayList(otherList);

How to convert an Array of primitives to a List?

int[] source = new int[]{1, 0, 0, 0, 0};
// convert with for-each loop
List a = new ArrayList<>();
for (int n : source) {
a.add(n);
}
// convert with streams
List b = Arrays.stream(source).boxed().collect(Collectors.toList());

How to convert an Array of non-primitives to a List?

String[] strings = new String[]{"a", "b", "c"};
List<String> l = Arrays.asList(strings);

Scanner

If you want to read with Scanner from console input (System.in) in multiple methods within a single class you should only have one instance of that scanner. Because "after calling Scanner.close() on a Scanner which is associated with System.in, the System.in stream is closed and not available anymore."
Scanner s = new Scanner(System.in);
function1(s);
function2(s);
function3(s);
s.close();

Sunday, April 1, 2018

Java I/O basics - easy ways

Read from console (IDE compatible):

Scanner scanner = new Scanner(System.in);

System.out.print("Enter your nationality: ");
String nationality = scanner.nextLine();

// automatically parse primitives:
System.out.print("Enter your age: ");
int age = scanner.nextInt();
Source: CodeJava: 3 ways for reading input from the user in the console
Testing: StackOverflow: Unit testing for user input using Scanner

Write to console:

System.out.println("Some string..."); // with automatic line break
System.out.print("Some string..."); // no line break added
Source: Oracle docs
Testing: StackOverflow: JUnit test for System.out.println()

Read all lines from a file (java.nio):

List lines = Files.readAllLines(Paths.get("input.txt"));
Source: Java2S.com, example in my previous post

Write all lines to a file (java.nio):

Files.write(Paths.get("output.txt"), lines);
Source: Java2S.comexample in my previous post

Monday, March 26, 2018

Java 7 easy file read and write

If you don't care about buffering, you could use this way to read and write fileswith Java 7:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;

public class ReadWrite {

 public static void main(String[] args) throws IOException {
  List lines = readAll("input.txt");
  writeAll("output.txt", lines);
 }

 private static List readAll(String input) throws IOException {
  return Files.readAllLines(Paths.get(input));
 }
 
 private static void writeAll(String output, List lines) throws IOException {
  Files.write(Paths.get(output), lines);
 }
}