/** * personium.io * Copyright 2014 FUJITSU LIMITED * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.fujitsu.dc.core.utils; import java.net.URI; import java.net.URISyntaxException; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.core4j.Enumerable; import org.odata4j.edm.EdmComplexType; import org.odata4j.edm.EdmDataServices; import org.odata4j.edm.EdmEntitySet; import org.odata4j.edm.EdmEntityType; import org.odata4j.edm.EdmMultiplicity; import org.odata4j.edm.EdmNavigationProperty; import org.odata4j.edm.EdmProperty; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fujitsu.dc.core.DcCoreException; import com.fujitsu.dc.core.model.ctl.Common; import com.fujitsu.dc.core.model.impl.es.doc.EntitySetDocHandler; /** * OData系ユーティリティ関数を集めたクラス. */ public final class ODataUtils { static Logger log = LoggerFactory.getLogger(ODataUtils.class); /** URI最大長. */ static final int URI_MAX_LENGTH = 1024; /** DateTime型の最小値(1753-01-01 T00:00:00.000). */ public static final long DATETIME_MIN = -6847804800000L; /** DateTime型の最大値(9999-12-31 T23:59:59.999). */ public static final long DATETIME_MAX = 253402300799999L; /** ODataで定義されているEdm.Double型の正の最小値. */ public static final double DOUBLE_POSITIVE_MIN_VALUE = 2.23e-308; /** ODataで定義されているEdm.Double型の正の最大値. */ public static final double DOUBLE_POSITIVE_MAX_VALUE = 1.79e+308; /** ODataで定義されているEdm.Double型の負の最小値. */ public static final double DOUBLE_NEGATIVE_MIN_VALUE = -1.79e+308; /** ODataで定義されているEdm.Double型の負の最大値. */ public static final double DOUBLE_NEGATIVE_MAX_VALUE = -2.23e-308; private ODataUtils() { } /** * スキーマからCardinalityを調べるユーティリティ. */ public static class Cardinality { /** * 多対多. */ public static final int MANY_MANY = 4; /** * 多対一. */ public static final int MANY_ONE = 3; /** * 一対多. */ public static final int ONE_MANY = 2; /** * 一対一. */ public static final int ONE_ONE = 1; /** * @param navProp EdmNavigationProperty * @return Cardinalityの定数 (MANY_MANY等) */ public static int forEdmNavigationProperty(EdmNavigationProperty navProp) { EdmMultiplicity fromM = navProp.getFromRole().getMultiplicity(); EdmMultiplicity toM = navProp.getToRole().getMultiplicity(); if (EdmMultiplicity.MANY.equals(fromM)) { if (EdmMultiplicity.MANY.equals(toM)) { // NN return MANY_MANY; } else { // N1 return MANY_ONE; } } else { if (EdmMultiplicity.MANY.equals(toM)) { // 1N return ONE_MANY; } else { // 11 return ONE_ONE; } } } } /** * If-MatchヘッダとEtagの値が等しいかチェック. * @param etag Etag * @param oedhExisting 存在するドキュメント情報 */ public static void checkEtag(final String etag, EntitySetDocHandler oedhExisting) { if (etag == null) { return; } // IfMatchヘッダに「*」が指定されている場合は無条件に実行 if (("*").equals(etag)) { return; } // IfMatchヘッダのEtagからバージョンとUpdatedの値を取得 long ifMatchVersion = 0; long ifMatchUpdated = 0; Pattern pattern = Pattern.compile("(^[0-9]+)-([0-9]+)"); Matcher m = pattern.matcher(etag); try { ifMatchVersion = Long.parseLong(m.replaceAll("$1")); ifMatchUpdated = Long.parseLong(m.replaceAll("$2")); } catch (NumberFormatException e) { throw DcCoreException.OData.ETAG_NOT_MATCH.reason(e); } // バージョンチェック if (ifMatchVersion != oedhExisting.getVersion() || ifMatchUpdated != oedhExisting.getUpdated()) { throw DcCoreException.OData.ETAG_NOT_MATCH; } } /** * 引数で与えられたMapをマージする. * @param baseProperty マージのベースにするプロパティ群 * @param addProperty マージで更新するプロパティ群 * @return マージ結果 */ public static Map<String, Object> getMergeFields(Map<String, Object> baseProperty, Map<String, Object> addProperty) { Map<String, Object> mergeFields = new HashMap<String, Object>(); mergeFields.putAll(baseProperty); addComplexPropertyMergeFields(mergeFields, addProperty); return mergeFields; } @SuppressWarnings("unchecked") private static void addComplexPropertyMergeFields(Map<String, Object> baseProperty, Map<String, Object> addProperty) { for (Map.Entry<String, Object> property : addProperty.entrySet()) { String key = property.getKey(); Object value = property.getValue(); if (!(value instanceof Map)) { baseProperty.put(key, value); } else { Map<String, Object> nestMap = (Map<String, Object>) baseProperty.get(key); if (nestMap == null) { // MERGE前の値がnullであった場合にはMERGEしようとしている値をputできない→空のHashMapを作成 nestMap = new HashMap<String, Object>(); baseProperty.put(key, nestMap); } addComplexPropertyMergeFields(nestMap, (Map<String, Object>) value); } } } /** * Stringの値チェック. * @param value チェック対象値 * @return boolean */ public static boolean validateString(String value) { if (value.getBytes().length > Common.MAX_USERDATA_VALUE_LENGTH) { return false; } return true; } /** * Booleanの値チェック. * @param value チェック対象値 * @return boolean */ public static boolean validateBoolean(String value) { if (!value.equals("true") && !value.equals("false")) { return false; } return true; } /** * Int32の値チェック. * @param value チェック対象値 * @return boolean */ public static boolean validateInt32(String value) { try { // 整数型 Integer.parseInt(value); return true; } catch (NumberFormatException e) { return false; } } /** * DateTimeの値チェック. * @param value チェック対象値 * @return boolean */ public static boolean validateDateTime(String value) { // DateTimeのチェックをする // SYSUTCDATETIME() または、/Date(【long型】)/ try { if (Common.SYSUTCDATETIME.equals(value)) { return true; } Pattern pattern = Pattern.compile("^/Date\\((.+)\\)/$"); Matcher match = pattern.matcher(value); if (match.matches()) { String date = match.replaceAll("$1"); long longDate = Long.parseLong(date); if (longDate < DATETIME_MIN || longDate > DATETIME_MAX) { return false; } return true; } return false; } catch (NumberFormatException e) { return false; } } /** * Singleの値チェック. * @param value チェック対象値 * @return boolean */ public static boolean validateSingle(String value) { try { if (value.contains(".")) { // 小数型 Pattern pattern = Pattern.compile(Common.PATTERN_DECIMAL); Matcher matcher = pattern.matcher(value); if (!matcher.matches()) { return false; } } Float.parseFloat(value); return true; } catch (NumberFormatException e) { return false; } } /** * ODataで定義されているDoubleの有効範囲値チェック. * 有効範囲 ± 2.23e -308 から ± 1.79e +308 * @param value チェック対象値 * @return boolean */ public static boolean validateDouble(String value) { try { Double doubleValue = Double.parseDouble(value); return validateDouble(doubleValue); } catch (NumberFormatException e) { return false; } } /** * ODataで定義されているDoubleの有効範囲値チェック. * 有効範囲 ± 2.23e -308 から ± 1.79e +308 * @param value チェック対象値 * @return boolean */ public static boolean validateDouble(Double value) { if (0 == value || (DOUBLE_NEGATIVE_MIN_VALUE <= value && value <= DOUBLE_NEGATIVE_MAX_VALUE) || (DOUBLE_POSITIVE_MIN_VALUE <= value && value <= DOUBLE_POSITIVE_MAX_VALUE)) { return true; } return false; } /** * プロパティ項目の値をURIかチェックする. * @param propValue チェック値 * @return true:バリデートOK、falseバリデートNG */ public static boolean isValidUri(String propValue) { URI uri; try { uri = new URI(propValue); String scheme = uri.getScheme(); // Scheme check if (uri.getScheme() == null || (!(scheme.equals(UriUtils.SCHEME_HTTP)) && !(scheme.equals(UriUtils.SCHEME_HTTPS)) && !(scheme.equals(UriUtils.SCHEME_URN))) && !(scheme.equals(UriUtils.SCHEME_LOCALUNIT))) { return false; } // 文字列長チェック if (uri.toString().length() > URI_MAX_LENGTH) { return false; } } catch (URISyntaxException e) { return false; } return true; } /** * Check if string is valid Schema URI. * @param str Input string * @return true if valid */ public static boolean isValidSchemaUri(String str) { return isValidUrn(str) || isValidCellUrl(str); } /** * Check if string is valid Schema Urn. * @param str Input string * @return true if valid */ private static boolean isValidUrn(String str) { boolean isValidLength = str.length() <= URI_MAX_LENGTH; URI uri; try { uri = new URI(str); } catch (URISyntaxException e) { return false; } String scheme = uri.getScheme(); boolean isUrn = scheme != null && (scheme.equals(UriUtils.SCHEME_URN)); return isValidLength && isUrn; } /** * Check if string is valid Cell URL. * @param str Input string * @return true if valid */ public static boolean isValidCellUrl(String str) { boolean isValidLength = str.length() <= URI_MAX_LENGTH; URI uri; try { uri = new URI(str); } catch (URISyntaxException e) { return false; } String scheme = uri.getScheme(); boolean isValidScheme = scheme != null && ((scheme.equals(UriUtils.SCHEME_HTTP)) || (scheme.equals(UriUtils.SCHEME_HTTPS)) || (scheme.equals(UriUtils.SCHEME_LOCALUNIT))); boolean isNormalized = uri.normalize().toString().equals(str); boolean hasTrailingSlash = str.endsWith("/"); return isValidLength && isValidScheme && isNormalized && hasTrailingSlash; } /** * スキーマ定義されたプロパティ数を取得する. * @param metadata スキーマ情報 * @param entitySetName 対象のエンティティセット名 * @return プロパティ数 */ public static int getStaticPropertyCount(EdmDataServices metadata, String entitySetName) { EdmEntitySet edmEntitySet = metadata.findEdmEntitySet(entitySetName); EdmEntityType edmEntityType = edmEntitySet.getType(); return getStaticPropertyCount(metadata, edmEntityType.getProperties()); } private static int getStaticPropertyCount(EdmDataServices metadata, Enumerable<EdmProperty> properties) { int count = 0; for (EdmProperty ep : properties) { count++; if (!ep.getType().isSimple()) { EdmComplexType edmComplexType = metadata.findEdmComplexType(ep.getType().getFullyQualifiedTypeName()); count += getStaticPropertyCount(metadata, edmComplexType.getProperties()); } } return count; } }