Total Pageviews

2017/11/30

2017/11 Tokyo

Tokyo Tower
DSC02546

DSC02558

DSC02707


六本木
DSC02630

DSC02636


本川越、蔵の街.
DSC02649

DSC02657

DSC02673

DSC02692

DSC02680


台場
DSC02751

DSC02768

DSC02776


清澄庭園 / Kiyosumi Garden.
DSC02566

DSC02579

2017/11/29

2017/11 Tokyo

東京車站
DSC02426

DSC02427


輕井澤
DSC02432

DSC02438

DSC02454

DSC02456

DSC02467

DSC02496

DSC02497

DSC02507

DSC02529


2017/11/16

[Windows 10] 點陣式印表機 (EPSON LQ-310) 無法列印

Problem
一早發現家中的點陣式印表機 (EPSON LQ-310) 無法使用,系統送出列印指令,印表機完全沒有反應。

How-To
經查,由於 11/15 晚上安裝了一個 windows update (KB4048954),導致 11/16 早上開始,點陣式印表機無法列印,處理步驟如下:





2017/11/11

[Fortify] Fix Locale Dependent Comparison

Problem


Before
The original code snippet is the following:
1
2
3
    if (value.toUpperCase().equals("TRUE")) {
        return true;
    }


After
Add dependency in your pom.xml
1
2
3
4
5
6
    <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.0</version>
    </dependency>


The updated code snippet are as bellows:
1
2
3
4
    String valueUpperCase = StringUtils.upperCase(value, Locale.ENGLISH);
    if(valueUpperCase.equals("TRUE")) {
        return true;
    }



2017/11/10

[Bootbox] How to change default button and button color in confirmation dialog?

Problem
The confirmation dialog looks like:

I would like to change the default button in confirmation dialog from "確認"(confirm) to "取消"(cancel). 
Owing to confirm to do delete is a dangerous action, so I would like to change it color to red.

How to meet the two requirements?


The original code snippet is as following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    $scope.remove = function() {
        bootbox.confirm({
            message:'是否確定要刪除', 
            buttons: {
                confirm: { label: '確認' },
                cancel:  { label: '取消' }
            },
            callback:function(isConfirmed) {
                if(isConfirmed) {
                    // ....
                }
            }
        });
    };



How-to
Add btn-danger class name to confirm button, then the button color will change to red.
Add btn-primary class to cancel button, then the default button will be cancel button.

The updated code snippet is as bellows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    $scope.remove = function() {
        bootbox.confirm({
            message:'是否確定要刪除', 
            buttons: {
                confirm: { label: '確認', className: 'btn-danger' },
                cancel:  { label: '取消', className: 'btn-primary' }
            },
            callback:function(isConfirmed) {
                if(isConfirmed) {
                    // ....
                }
            }
        });
    };

Updated screenshot:


2017/11/09

[Bootbox] How to change button name in Booxbox confirm dialog

Problem
I am using Bootbox to implement confirm dialog, the screenshot looks like:

The code snippet is as bellows:
1
2
3
4
5
6
7
    $scope.remove = function() {
        bootbox.confirm('是否確定要刪除', function(isConfirmed) {
            if(isConfirmed) {
                // ....
            }
        });
    };

How can I change OK label to "確定"? And how do I change Cancel label to "取消"?

How-to

Here has sample code to fulfill this requirement:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    $scope.remove = function() {
        bootbox.confirm({
            message:'是否確定要刪除', 
            buttons: {
                confirm: { label: '確認' },
                cancel:  { label: '取消' }
            },
            callback:function(isConfirmed) {
                if(isConfirmed) {
                    // ....
                }
            }
        });
    };


The confirmation dialog looks like:





2017/11/08

[MapStruct] How to use Decorator in MapStruct?

Problem
Assume I have a DeliveryAddressDto Java bean, it has three attributes:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package albert.practice.mapStruct;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
public class DeliveryAddressDto {
    private String receiver;
    private String addressString;
    private String totalString;
}

I have three Java beans, they provide information which DeliveryAddressDto needed.
1. Person bean
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package albert.practice.mapStruct;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
public class Person {
    private String firstName;
    private String lastName;
}


2. Address bean
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package albert.practice.mapStruct;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
public class Address {
    private String addressString;
}


3. ShoppingItems bean
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package albert.practice.mapStruct;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
public class ShoppingItems {
    private Long total;
}


The three attributes values in DeliveryAddressDto Java bean will copy from Person, Address, and ShoppingItems bean
(1) DeliveryAddressDto.receiver = Person.firstName + " " + Person.lastName
(2) DeliveryAddressDto.addressString = Address.addressString
(3) DeliveryAddressDto.totalString = ShoppingItems.total (Apply format: NT$#,###,###,##0)




How to use decorator to fulfill this requirement?
 

How-To
1. create a mapper class
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package albert.practice.mapStruct.mapper;

import org.mapstruct.DecoratedWith;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

import albert.practice.mapStruct.Address;
import albert.practice.mapStruct.DeliveryAddressDto;
import albert.practice.mapStruct.Person;
import albert.practice.mapStruct.ShoppingItems;
import albert.practice.mapStruct.decorator.AddressDecorator;

@Mapper
@DecoratedWith(AddressDecorator.class)
public interface AddressMapper {
    // By convention, a mapper interface should define a member called INSTANCE
    // which holds a single instance of the mapper type:
    AddressMapper INSTANCE = Mappers.getMapper(AddressMapper.class);

    @Mapping(target = "totalString", source = "shoppingItems.total")
    @Mapping(target = "receiver", source = "person.lastName")
    @Mapping(target = "addressString", source = "address.addressString")
    DeliveryAddressDto covertPersonAndAddressToDeliveryAddressDto(Person person, Address address,
      ShoppingItems shoppingItems);

}

2. create a decorator class
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package albert.practice.mapStruct.decorator;

import java.text.DecimalFormat;

import albert.practice.mapStruct.Address;
import albert.practice.mapStruct.DeliveryAddressDto;
import albert.practice.mapStruct.Person;
import albert.practice.mapStruct.ShoppingItems;
import albert.practice.mapStruct.mapper.AddressMapper;

public class AddressDecorator implements AddressMapper {

    private final AddressMapper delegate;
    private DecimalFormat decimalFormat = new DecimalFormat("NT$#,###,###,##0");

    public AddressDecorator(AddressMapper delegate) {
        this.delegate = delegate;
    }

    @Override
    public DeliveryAddressDto covertPersonAndAddressToDeliveryAddressDto(Person person, Address address,
      ShoppingItems shoppingItems) {
        DeliveryAddressDto dto = delegate.covertPersonAndAddressToDeliveryAddressDto(person, address, shoppingItems);
        dto.setReceiver(person.getFirstName() + " " + person.getLastName());
        dto.setTotalString(decimalFormat.format(shoppingItems.getTotal()));
        return dto;
    }

}




2017/11/07

Using MapStruct to simplify the implementation of mappings between Java bean types

Problem
Assume I have two java bean classes, Car and CarDto, I would like to copy Car attributes to CarDto attributes, and vice versa.

The Car class is as bellows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package albert.practice.mapStruct;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
public class Car {
    private String manufacturer;
    private int seatCount;
    private String mfgDateString;
}


The CarDto class is as following:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package albert.practice.mapStruct;

import java.util.Date;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
public class CarDto {
    private String make;
    private int numberOfSeats;
    private Date mfgDate;
}



The two Java bean classes have two differenet name and even have different data type, how to fulfill this requirement conveniently?




How-To
You can make good use of MapStruct to simplify the implementation of mappings between Java bean types.

Add the following configuration in pom.xml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    <properties>
        <org.mapstruct.version>1.1.0.Final</org.mapstruct.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-jdk8</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
            <version>${org.mapstruct.version}</version>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.5.1</version>
                <configuration>
                    <source>1.8</source> <!-- or higher, depending on your project -->
                    <target>1.8</target> <!-- or higher, depending on your project -->  
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${org.mapstruct.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>


Create a CarMapper to do column mapping
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package albert.practice.mapStruct.mapper;

import java.util.List;

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

import albert.practice.mapStruct.Car;
import albert.practice.mapStruct.CarDto;

@Mapper(uses = MfgDateMapper.class)
public interface CarMapper {
    // By convention, a mapper interface should define a member called INSTANCE
    // which holds a single instance of the mapper type:
    CarMapper CarMapper = Mappers.getMapper(CarMapper.class);

    @Mapping(target = "mfgDate", source = "mfgDateString")
    @Mapping(target = "make", source = "manufacturer")
    @Mapping(target = "numberOfSeats", source = "seatCount")
    CarDto convertCarToCarDto(Car car);

    @Mapping(target = "mfgDateString", source = "mfgDate")
    @Mapping(target = "manufacturer", source = "make")
    @Mapping(target = "seatCount", source = "numberOfSeats")
    Car convertCarDtoToCar(CarDto carDto);
    
}




Create a MfgDateMapper class to convert date string to java.util.Date, and vice versa 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package albert.practice.mapStruct.mapper;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import com.google.common.base.Strings;

public class MfgDateMapper {

    private DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

    public Date toDate(String dateString) throws ParseException {

        Date date = null;
        if (!Strings.isNullOrEmpty(dateString)) {
            try {
                date = dateFormat.parse(dateString);
            } catch (ParseException e) {
                throw e;
            }
        }
        return date;
    }

    public String toDateString(Date date) {
        String dateString = "";
        if (date != null) {
           dateString = dateFormat.format(date);
        }
        return dateString;
    }

}


Create a test class
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package albert.practice.mapStruct;

import static org.junit.Assert.assertTrue;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import org.junit.Before;
import org.junit.Test;
import org.mapstruct.factory.Mappers;

import albert.practice.mapStruct.mapper.CarMapper;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class CarMapperTest {

    CarMapper carMapper;

    @Before
    public void setup() {
        carMapper = Mappers.getMapper(CarMapper.class);
    }

    @Test
    public void testConvertCarToCarDto() {
        Car car = new Car("Toyota Camry", 5, "2017-04-01");
        CarDto carDto = carMapper.convertCarToCarDto(car);

        log.debug("[testConvertCarToCarDto] car = " + car.toString());
        log.debug("[testConvertCarToCarDto] carDto = " + carDto.toString());

        assertTrue(carDto.getMake().equals(car.getManufacturer()));
        assertTrue(carDto.getNumberOfSeats() == car.getSeatCount());
    }

    @Test
    public void testConvertCarDtoToCar() {
        CarDto carDto = new CarDto("MINI Cooper S", 4, new Date());
        Car car = carMapper.convertCarDtoToCar(carDto);

        log.debug("[testConvertCarDtoToCar] carDto = " + carDto.toString());
        log.debug("[testConvertCarDtoToCar] car = " + car.toString());

        assertTrue(car.getManufacturer().equals(carDto.getMake()));
        assertTrue(car.getSeatCount() == carDto.getNumberOfSeats());
    }

}






2017/11/06

[Angular] How to implement pagination in ng-grid

Problem
How to implement pagination in ng-grid?

How-to

Here has code snippet:

1. enable pagination function, and set pagingOptions & totalServerItems in ng-grid

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
    $scope.gridCols  = [ 
        {  field : 'no',              displayName : '序號',         width : '5%',   cellClass : 'text-right' }, 
        {  field : 'exceptionGroup',  displayName : '異常類別',     width : '15%',  cellClass : 'text-left' }, 
        {  field : 'exceptionDesc',   displayName : '異常類別說明', width : '25%',  cellClass : 'text-left' }, 
        {  field : 'deptName',        displayName : '部門別',       width : '15%',  cellClass : 'text-left' }, 
        {  field : 'userName',        displayName : '通報人員姓名',  width : '10%',  cellClass : 'text-left' }, 
        {  field : 'emailAddr',       displayName : '收件Email',    width : '30%',  cellClass : 'text-left' } 
    ];
        
    $scope.dataGrid = {
        multiSelect : false,
        data : 'itemData',
        columnDefs : 'gridCols',
        enableColumnResize : true,
        keepLastSelected : false,
        enablePaging : true,
        showFooter : true,
        pagingOptions : $scope.pagingOptions,
        totalServerItems : "totalServerItems"
    };


2. set initial values when page loaded
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
    // query result (current page)
    $scope.itemData = [];
    // query result (complete data)
    $scope.resultData = [];     
        
    // set page sizes, page size and current page
    $scope.pagingOptions = {
        pageSizes : [ 25, 50, 100 ],
        pageSize : 25,
        currentPage : 1
    };
    // total items are on the server (initial value)
    $scope.totalServerItems = 0;        

3. set data to current page
1
2
3
4
5
6
7
8
9
    // set paging data
    $scope.setPagingData = function(data, page, pageSize) {
        var pagedData = data.slice((page - 1) * pageSize, page * pageSize);
        $scope.itemData = pagedData;
        $scope.totalServerItems = data.length;
        if (!$scope.$$phase) {
            $scope.$apply();
        }
    };


4. Registers a listener callback to be executed whenever the pagingOptions changes.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
    // Registers a listener callback to be executed whenever the pagingOptions changes.
    $scope.$watch('pagingOptions', function(newVal, oldVal) {
        // if page size change, then set current page to 1 and set paging data
        if(newVal.pageSize !== oldVal.pageSize) {
            $scope.pagingOptions.currentPage = 1;
            $scope.setPagingData($scope.resultData, $scope.pagingOptions.currentPage, newVal.pageSize);
        }
   
        // if current page change, then set paging data
        if (newVal !== oldVal && newVal.currentPage !== oldVal.currentPage) {
            $scope.setPagingData($scope.resultData, newVal.currentPage, newVal.pageSize);
        }
    }, true);     


Screenshot is as bellows:



Reference

[1] https://angular-ui.github.io/ui-grid/