package org.ovirt.engine.ui.uicommonweb.validation;
import java.util.ArrayList;
import java.util.List;
import org.ovirt.engine.ui.uicompat.ConstantsManager;
import com.google.gwt.dom.client.ImageElement;
/**
* It validates icons size, dimensions and file format.
*/
public class IconValidation implements IValidation {
private final ImageElement imageElement;
/**
* @param imageElement the 'src' attribute of which is already set to string that will be validated from previous
* event loop run
*/
public IconValidation(ImageElement imageElement) {
this.imageElement = imageElement;
}
@Override
public ValidationResult validate(Object iconObject) {
if (!(iconObject instanceof String)) {
throw new IllegalArgumentException("Illegal argument type: " //$NON-NLS-1$
+ (iconObject == null ? "null" : iconObject.getClass().toString())); //$NON-NLS-1$
}
final String iconUrl = (String) iconObject;
if (!iconUrl.equals(imageElement.getSrc())) {
throw new IllegalArgumentException("Validated url (" + iconUrl //$NON-NLS-1$
+ ") doesn't match url of the image widget (" + imageElement.getSrc() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
}
return validate(iconUrl);
}
private ValidationResult validate(String icon) {
ValidationResult typeValidation = validateType(icon);
if (!typeValidation.getSuccess()) {
return typeValidation;
}
ValidationResult dimensionsValidation = validateBrowserParsabilityAndDimensions(icon);
ValidationResult sizeValidation = validateSize(icon);
if (dimensionsValidation.getSuccess() && sizeValidation.getSuccess()) {
return ValidationResult.ok();
}
final List<String> reasons = new ArrayList<>(dimensionsValidation.getReasons());
reasons.addAll(sizeValidation.getReasons());
return new ValidationResult(false, reasons);
}
/**
* HTMLImageElement.complete and Max width 150px, max height 120px
*/
private ValidationResult validateBrowserParsabilityAndDimensions(String icon) {
final int maxWidth = 150;
final int maxHeight = 120;
boolean complete = isComplete(imageElement);
if (!complete) {
return ValidationResult.fail(ConstantsManager.getInstance().getConstants().iconIsNotParsable());
}
boolean valid = imageElement.getWidth() <= maxWidth
&& imageElement.getHeight() <= maxHeight;
if (valid) {
return ValidationResult.ok();
}
return ValidationResult.fail(
ConstantsManager.getInstance().getMessages().iconDimensionsTooLarge(
imageElement.getWidth(), imageElement.getWidth(), maxWidth, maxHeight));
}
/**
*
* @param element has to be instance of HTMLImageElement
* @return {@code true} if image is successfully loaded and parsed, {@code false} otherwise
*/
private native boolean isComplete(ImageElement element) /*-{
return element.complete;
}-*/;
/**
* The dataUri string has to fit in 32kB.
* Ratio base64encoded/raw data is approx. 4/3.
*/
private ValidationResult validateSize(String icon) {
final int maxEncodedSize = 32 * 1024;
final int maxRawSize = (int) (maxEncodedSize * (3d/4d) / 1024); // just estimate for users, in kB
boolean valid = maxEncodedSize > icon.length();
if (valid) {
return ValidationResult.ok();
}
return ValidationResult.fail(
ConstantsManager.getInstance().getMessages().iconFileTooLarge(maxRawSize));
}
/**
* Magic numbers
* <pre>
* png 89 50 4e 47 0d 0a 1a 0a
* jpg ff d8 ff
* gif 'GIF87a' or 'GIF89a'
* </pre>
*/
private ValidationResult validateType(String icon) {
final String iconBase64Data = dataUriToBase64Data(icon);
final String iconRawData = atob(iconBase64Data);
final String[] magicNumbers = new String[] {
"\u0089\u0050\u004e\u0047\r\n\u001a\n", //$NON-NLS-1$ // png
"\u00ff\u00d8\u00ff", //$NON-NLS-1$ // png
"GIF87a", //$NON-NLS-1$ // gif
"GIF89a" //$NON-NLS-1$ // gif
};
for (String magicNumber : magicNumbers) {
if (iconRawData.startsWith(magicNumber)) {
return ValidationResult.ok();
}
}
return ValidationResult.fail(ConstantsManager.getInstance().getMessages().invalidIconFormat("png, jpg, gif")); //$NON-NLS-1$
}
private native String atob(String encodedString) /*-{
return atob(encodedString);
}-*/;
/**
* Datauri format:
* <pre>
* data:[<MIME-type>][;charset=<encoding>][;base64],<data>
* </pre>
* @return base46 part of datauri
*/
private String dataUriToBase64Data(String dataUri) {
final int commaIndex = dataUri.indexOf(","); //$NON-NLS-1$
return dataUri.substring(commaIndex + 1);
}
}