fixed export of inline tables

This commit is contained in:
anhefti 2019-11-14 09:12:55 +01:00
parent 5f52a2b215
commit 943017073b
11 changed files with 292 additions and 102 deletions

View file

@ -25,6 +25,7 @@ public final class Constants {
public static final long DAY_IN_MILLIS = 24 * HOUR_IN_MILLIS;
public static final Character LIST_SEPARATOR_CHAR = ',';
public static final Character COMPLEX_VALUE_SEPARATOR = ':';
public static final String LIST_SEPARATOR = ",";
public static final String EMBEDDED_LIST_SEPARATOR = "|";
public static final String NO_NAME = "NONE";

View file

@ -285,8 +285,7 @@ public class GridTable extends Composite {
return null;
}
final String[] split = StringUtils.split(string, ':');
final String[] split = StringUtils.split(string, Constants.COMPLEX_VALUE_SEPARATOR);
final AttributeType attributeType = AttributeType.valueOf(split[2]);
if (!SUPPORTED_TYPES.contains(attributeType)) {
throw new UnsupportedOperationException(

View file

@ -81,6 +81,10 @@ public interface AttributeValueConverter {
}
}
/** Used to expand a "compressed" attribute like kioskMode -> createNewDesktop + killExplorerShell
*
* @param attr
* @return */
default Stream<ConfigurationAttribute> convertAttribute(final ConfigurationAttribute attr) {
return Stream.of(attr);
}

View file

@ -8,15 +8,28 @@
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
/** Interface of a SEB Exam Configuration XML conversion service */
public interface AttributeValueConverterService {
/** Use this to get a XMLValueConverter for a given ConfigurationAttribute.
/** Use this to get a AttributeValueConverter for a given ConfigurationAttribute.
*
* @param attribute The ConfigurationAttribute instance
* @return a XMLValueConverter for a given ConfigurationAttribute */
* @return a AttributeValueConverter for a given ConfigurationAttribute */
AttributeValueConverter getAttributeValueConverter(ConfigurationAttribute attribute);
/** Use this to get a AttributeValueConverter for a given AttributeType.
*
* @param attribute The ConfigurationAttribute instance
* @return a AttributeValueConverter for a given AttributeType */
AttributeValueConverter getAttributeValueConverter(final AttributeType attributeType);
/** Get Use this to get a AttributeValueConverter for a given ConfigurationAttribute.
*
* @param attributeName the name of the attribute
* @return a AttributeValueConverter for a given ConfigurationAttribute */
AttributeValueConverter getAttributeValueConverter(String attributeName);
}

View file

@ -67,4 +67,14 @@ public class AttributeValueConverterServiceImpl implements AttributeValueConvert
throw new IllegalStateException("No XMLValueConverter found for attribute: " + attribute);
}
@Override
public AttributeValueConverter getAttributeValueConverter(final AttributeType attributeType) {
return this.convertersByAttributeType.get(attributeType);
}
@Override
public AttributeValueConverter getAttributeValueConverter(final String attributeName) {
return this.convertersByAttributeName.get(attributeName);
}
}

View file

@ -15,6 +15,7 @@ import java.util.Stack;
import java.util.function.Consumer;
import java.util.function.Function;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -27,6 +28,7 @@ import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.ExamConfigImportHandler.PListNode.Type;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.converter.KioskModeConverter;
public class ExamConfigImportHandler extends DefaultHandler {
@ -46,6 +48,9 @@ public class ExamConfigImportHandler extends DefaultHandler {
private final Stack<PListNode> stack = new Stack<>();
private Boolean killExplorerShell = null;
private Boolean createNewDesktop = null;
protected ExamConfigImportHandler(
final Long institutionId,
final Long configId,
@ -258,40 +263,6 @@ public class ExamConfigImportHandler extends DefaultHandler {
}
}
private void saveValue(
final String name,
final ConfigurationAttribute attribute,
final int listIndex,
final String value) {
if (attribute == null) {
log.warn("Import of unknown attribute. name={} value={}", name, value);
return;
}
if (value == null) {
log.debug("*********************** Save null value: {}", name);
} else if (StringUtils.isBlank(value)) {
log.debug("*********************** Save blank value: {}", name);
} else {
log.debug("*********************** Save value value: {} : {}", name, value);
}
final ConfigurationValue configurationValue = new ConfigurationValue(
null,
this.institutionId,
this.configId,
attribute.id,
listIndex,
value);
if (log.isDebugEnabled()) {
log.debug("Save imported value: {} : {}", name, configurationValue);
}
this.valueConsumer.accept(configurationValue);
}
@Override
public void characters(
final char[] ch,
@ -311,6 +282,80 @@ public class ExamConfigImportHandler extends DefaultHandler {
}
}
private void saveValue(
final String name,
final ConfigurationAttribute attribute,
final int listIndex,
final String value) {
final ConfigurationValue configurationValue = createConfigurationValue(
name,
attribute,
listIndex,
value);
if (configurationValue != null) {
if (log.isDebugEnabled()) {
log.debug("Save imported value: {} : {}", name, configurationValue);
}
this.valueConsumer.accept(configurationValue);
}
}
private ConfigurationValue createConfigurationValue(
final String name,
final ConfigurationAttribute attribute,
final int listIndex,
final String value) {
if (attribute == null) {
if (KioskModeConverter.NAMES.contains(name)) {
return handleKioskMode(name, listIndex, value);
}
log.warn("Import of unknown attribute. name={} value={}", name, value);
return null;
}
return new ConfigurationValue(
null,
this.institutionId,
this.configId,
attribute.id,
listIndex,
value);
}
private ConfigurationValue handleKioskMode(final String name, final int listIndex, final String value) {
if (KioskModeConverter.ATTR_NAME_KILL_SHELL.equals(name)) {
this.killExplorerShell = BooleanUtils.toBoolean(value);
} else if (KioskModeConverter.ATTR_NAME_CREATE_NEW_DESKTOP.equals(name)) {
this.createNewDesktop = BooleanUtils.toBoolean(value);
}
if (this.killExplorerShell != null && this.createNewDesktop != null) {
final ConfigurationAttribute kioskMode = this.attributeResolver.apply(
KioskModeConverter.ATTR_NAME_KIOSK_MODE);
final String val = (this.createNewDesktop)
? "0"
: (this.killExplorerShell)
? "1"
: "2";
return new ConfigurationValue(
null,
this.institutionId,
this.configId,
kioskMode.id,
listIndex,
val);
}
return null;
}
final static class PListNode {
enum Type {

View file

@ -0,0 +1,157 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.impl.converter;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Function;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.AttributeType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationAttribute;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationValue;
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
import ch.ethz.seb.sebserver.gbl.util.Utils;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.AttributeValueConverter;
import ch.ethz.seb.sebserver.webservice.servicelayer.sebconfig.AttributeValueConverterService;
@Lazy
@Component
@WebServiceProfile
public class InlineTableConverter implements AttributeValueConverter {
private static final String XML_KEY_TEMPLATE = "<key>%s</key>";
private static final byte[] XML_ARRAY_START = Utils.toByteArray("<array>");
private static final byte[] XML_ARRAY_END = Utils.toByteArray("</array>");
private static final byte[] XML_DICT_START = Utils.toByteArray("<dict>");
private static final byte[] XML_DICT_END = Utils.toByteArray("</dict>");
private static final byte[] XML_EMPTY_ARRAY = Utils.toByteArray("<array />");
private static final String JSON_KEY_TEMPLATE = "\"%s\":";
private static final byte[] JSON_ARRAY_START = Utils.toByteArray("[");
private static final byte[] JSON_ARRAY_END = Utils.toByteArray("]");
private static final byte[] JSON_DICT_START = Utils.toByteArray("{");
private static final byte[] JSON_DICT_END = Utils.toByteArray("}");
private static final byte[] JSON_EMPTY_ARRAY = Utils.toByteArray("[]");
public static final Set<AttributeType> SUPPORTED_TYPES = Collections.unmodifiableSet(
new HashSet<>(Arrays.asList(AttributeType.INLINE_TABLE)));
private AttributeValueConverterService attributeValueConverterService;
@Override
public void init(final AttributeValueConverterService attributeValueConverterService) {
this.attributeValueConverterService = attributeValueConverterService;
}
@Override
public Set<AttributeType> types() {
return SUPPORTED_TYPES;
}
@Override
public void convertToXML(
final OutputStream out,
final ConfigurationAttribute attribute,
final Function<ConfigurationAttribute, ConfigurationValue> valueSupplier) throws IOException {
convert(out, attribute, valueSupplier.apply(attribute), true);
}
@Override
public void convertToJSON(
final OutputStream out,
final ConfigurationAttribute attribute,
final Function<ConfigurationAttribute, ConfigurationValue> valueSupplier) throws IOException {
convert(out, attribute, valueSupplier.apply(attribute), false);
}
private void convert(
final OutputStream out,
final ConfigurationAttribute attribute,
final ConfigurationValue value,
final boolean xml) throws IOException {
out.write(Utils.toByteArray(String.format(
(xml) ? XML_KEY_TEMPLATE : JSON_KEY_TEMPLATE,
extractName(attribute))));
if (StringUtils.isBlank(value.value)) {
out.write((xml) ? XML_EMPTY_ARRAY : JSON_EMPTY_ARRAY);
out.flush();
return;
}
out.write((xml) ? XML_ARRAY_START : JSON_ARRAY_START);
final String[] rows = StringUtils.split(value.value, Constants.LIST_SEPARATOR);
final String[] columns = StringUtils.split(attribute.getResources(), Constants.EMBEDDED_LIST_SEPARATOR);
StringUtils.split(attribute.resources, Constants.LIST_SEPARATOR);
for (int i = 0; i < rows.length; i++) {
final String[] values = StringUtils.split(rows[i], Constants.EMBEDDED_LIST_SEPARATOR);
out.write((xml) ? XML_DICT_START : JSON_DICT_START);
for (int j = 0; j < columns.length; j++) {
final String[] val = StringUtils.split(values[j], Constants.FORM_URL_ENCODED_NAME_VALUE_SEPARATOR);
final String[] column = StringUtils.split(columns[j], Constants.COMPLEX_VALUE_SEPARATOR);
final AttributeType attributeType = AttributeType.valueOf(column[2]);
final AttributeValueConverter attributeValueConverter = this.attributeValueConverterService
.getAttributeValueConverter(attributeType);
final ConfigurationAttribute configurationAttribute = new ConfigurationAttribute(
-1L,
attribute.id,
val[0],
attributeType,
StringUtils.EMPTY,
StringUtils.EMPTY,
StringUtils.EMPTY,
StringUtils.EMPTY);
final ConfigurationValue configurationValue = new ConfigurationValue(
-1L,
value.institutionId,
value.configurationId,
configurationAttribute.id,
0,
val[1]);
if (xml) {
attributeValueConverter.convertToXML(
out,
configurationAttribute,
a -> configurationValue);
} else {
attributeValueConverter.convertToJSON(
out,
configurationAttribute,
a -> configurationValue);
}
}
out.write((xml) ? XML_DICT_END : JSON_DICT_END);
}
out.write((xml) ? XML_ARRAY_END : JSON_ARRAY_END);
}
}

View file

@ -47,7 +47,6 @@ public class TableConverter implements AttributeValueConverter {
public static final Set<AttributeType> SUPPORTED_TYPES = Collections.unmodifiableSet(
new HashSet<>(Arrays.asList(
AttributeType.TABLE,
AttributeType.INLINE_TABLE,
AttributeType.COMPOSITE_TABLE)));
private static final String XML_KEY_TEMPLATE = "<key>%s</key>";
@ -214,39 +213,6 @@ public class TableConverter implements AttributeValueConverter {
out.flush();
}
// final Iterator<List<ConfigurationValue>> irows = values.iterator();
//
// while (irows.hasNext()) {
// final List<ConfigurationValue> rowValues = irows.next();
// out.write((xml) ? XML_DICT_START : JSON_DICT_START);
//
// final Iterator<ConfigurationValue> ivalue = rowValues.iterator();
//
// while (ivalue.hasNext()) {
// final ConfigurationValue value = ivalue.next();
// final ConfigurationAttribute attr = attributeMap.get(value.attributeId);
// final AttributeValueConverter converter =
// attributeValueConverterService.getAttributeValueConverter(attr);
//
// if (xml) {
// converter.convertToXML(out, attr, a -> value);
// } else {
// converter.convertToJSON(out, attr, a -> value);
// }
//
// if (!xml && ivalue.hasNext()) {
// out.write(Utils.toByteArray(Constants.LIST_SEPARATOR));
// }
// }
// out.write((xml) ? XML_DICT_END : JSON_DICT_END);
//
// if (!xml && irows.hasNext()) {
// out.write(Utils.toByteArray(Constants.LIST_SEPARATOR));
// }
//
// out.flush();
// }
}
private List<ConfigurationAttribute> getSortedChildAttributes(final ConfigurationAttribute attribute) {

View file

@ -235,8 +235,8 @@ INSERT IGNORE INTO configuration_attribute VALUES
(403, 'insideSebEnableStartTaskManager', 'CHECKBOX', null, null, null, null, 'false'),
(404, 'insideSebEnableLogOff', 'CHECKBOX', null, null, null, null, 'false'),
(405, 'insideSebEnableShutDown', 'CHECKBOX', null, null, null, null, 'false'),
(406, 'insideSebEnableEaseOfAccess', 'CHECKBOX', null, null, null, null, 'false'),
(407, 'insideSebEnableVmWareClientShade', 'CHECKBOX', null, null, null, null, 'false'),
(406, 'insideSebEnableVmWareClientShade', 'CHECKBOX', null, null, null, null, 'false'),
(407, 'insideSebEnableEaseOfAccess', 'CHECKBOX', null, null, null, null, 'false'),
(408, 'insideSebEnableNetworkConnectionSelector', 'CHECKBOX', null, null, null, null, 'false'),
(500, 'enableEsc', 'CHECKBOX', null, null, null, null, 'false'),
@ -273,11 +273,9 @@ INSERT IGNORE INTO configuration_attribute VALUES
(808, 'monitorProcesses', 'CHECKBOX', null, null, null, null, 'false'),
(809, 'blacklistURLFilter', 'TEXT_FIELD', null, null, null, null, ''),
(810, 'whitelistURLFilter', 'TEXT_FIELD', null, null, null, null, ''),
(811, 'killExplorerShell', 'CHECKBOX', null, null, null, null, 'false'),
(812, 'allowWlan', 'CHECKBOX', null, null, null, null, 'false'),
(813, 'hookKeys', 'CHECKBOX', null, null, null, null, 'true'),
(1000, 'originatorVersion', 'TEXT_FIELD', null, null, null, null, 'SEB_Server_0.3.0'),
(1001, 'sebConfigPurpose', 'RADIO_SELECTION', null, '0,1', null, null, '0')
@ -339,12 +337,12 @@ INSERT IGNORE INTO orientation VALUES
(50, 50, 0, 3, null, 7, 1, 5, 1, 'TOP'),
(51, 51, 0, 3, 'userAgentDesktop', 7, 2, 5, 2, 'NONE'),
(52, 52, 0, 3, 'userAgentDesktop', 7, 3, 5, 1, 'NONE'),
(53, 53, 0, 3, 'userAgentTouch', 7, 4, 5, 3, 'NONE'),
(54, 54, 0, 3, 'userAgentTouch', 7, 8, 5, 1, 'NONE'),
(55, 55, 0, 3, 'userAgentMac', 7, 9, 5, 2, 'NONE'),
(56, 56, 0, 3, 'userAgentMac', 7, 11, 5, 1, 'NONE'),
(57, 57, 0, 3, null, 0, 14, 6, 1, 'NONE'),
(58, 58, 0, 3, null, 7, 14, 5, 1, 'TOP'),
(53, 53, 0, 3, 'userAgentTouch', 7, 4, 5, 2, 'NONE'),
(54, 54, 0, 3, 'userAgentTouch', 7, 6, 5, 1, 'NONE'),
(55, 55, 0, 3, 'userAgentMac', 7, 8, 5, 2, 'NONE'),
(56, 56, 0, 3, 'userAgentMac', 7, 10, 5, 1, 'NONE'),
(57, 57, 0, 3, null, 0, 13, 6, 1, 'NONE'),
(58, 58, 0, 3, null, 7, 13, 5, 1, 'TOP'),
(59, 59, 0, 4, null, 0, 0, 8, 1, 'NONE'),
(60, 60, 0, 4, null, 3, 1, 5, 1, 'LEFT_SPAN'),

View file

@ -208,8 +208,8 @@ INSERT IGNORE INTO configuration_attribute VALUES
(403, 'insideSebEnableStartTaskManager', 'CHECKBOX', null, null, null, null, 'false'),
(404, 'insideSebEnableLogOff', 'CHECKBOX', null, null, null, null, 'false'),
(405, 'insideSebEnableShutDown', 'CHECKBOX', null, null, null, null, 'false'),
(406, 'insideSebEnableEaseOfAccess', 'CHECKBOX', null, null, null, null, 'false'),
(407, 'insideSebEnableVmWareClientShade', 'CHECKBOX', null, null, null, null, 'false'),
(406, 'insideSebEnableVmWareClientShade', 'CHECKBOX', null, null, null, null, 'false'),
(407, 'insideSebEnableEaseOfAccess', 'CHECKBOX', null, null, null, null, 'false'),
(408, 'insideSebEnableNetworkConnectionSelector', 'CHECKBOX', null, null, null, null, 'false'),
(500, 'enableEsc', 'CHECKBOX', null, null, null, null, 'false'),
@ -246,11 +246,9 @@ INSERT IGNORE INTO configuration_attribute VALUES
(808, 'monitorProcesses', 'CHECKBOX', null, null, null, null, 'false'),
(809, 'blacklistURLFilter', 'TEXT_FIELD', null, null, null, null, ''),
(810, 'whitelistURLFilter', 'TEXT_FIELD', null, null, null, null, ''),
(811, 'killExplorerShell', 'CHECKBOX', null, null, null, null, 'false'),
(812, 'allowWlan', 'CHECKBOX', null, null, null, null, 'false'),
(813, 'hookKeys', 'CHECKBOX', null, null, null, null, 'true'),
(1000, 'originatorVersion', 'TEXT_FIELD', null, null, null, null, 'SEB_Server_0.3.0'),
(1001, 'sebConfigPurpose', 'RADIO_SELECTION', null, '0,1', null, null, '0')
@ -312,12 +310,12 @@ INSERT IGNORE INTO orientation VALUES
(50, 50, 0, 3, null, 7, 1, 5, 1, 'TOP'),
(51, 51, 0, 3, 'userAgentDesktop', 7, 2, 5, 2, 'NONE'),
(52, 52, 0, 3, 'userAgentDesktop', 7, 3, 5, 1, 'NONE'),
(53, 53, 0, 3, 'userAgentTouch', 7, 4, 5, 3, 'NONE'),
(54, 54, 0, 3, 'userAgentTouch', 7, 8, 5, 1, 'NONE'),
(55, 55, 0, 3, 'userAgentMac', 7, 9, 5, 2, 'NONE'),
(56, 56, 0, 3, 'userAgentMac', 7, 11, 5, 1, 'NONE'),
(57, 57, 0, 3, null, 0, 14, 6, 1, 'NONE'),
(58, 58, 0, 3, null, 7, 14, 5, 1, 'TOP'),
(53, 53, 0, 3, 'userAgentTouch', 7, 4, 5, 2, 'NONE'),
(54, 54, 0, 3, 'userAgentTouch', 7, 6, 5, 1, 'NONE'),
(55, 55, 0, 3, 'userAgentMac', 7, 8, 5, 2, 'NONE'),
(56, 56, 0, 3, 'userAgentMac', 7, 10, 5, 1, 'NONE'),
(57, 57, 0, 3, null, 0, 13, 6, 1, 'NONE'),
(58, 58, 0, 3, null, 7, 13, 5, 1, 'TOP'),
(59, 59, 0, 4, null, 0, 0, 8, 1, 'NONE'),
(60, 60, 0, 4, null, 3, 1, 5, 1, 'LEFT_SPAN'),
@ -466,4 +464,5 @@ INSERT IGNORE INTO orientation VALUES
(518, 518, 0, 11, 'functionKeys', 3, 10, 3, 1, 'NONE'),
(519, 519, 0, 11, 'functionKeys', 3, 11, 3, 1, 'NONE'),
(520, 520, 0, 11, 'functionKeys', 3, 12, 3, 1, 'NONE')
;

View file

@ -216,8 +216,8 @@ INSERT IGNORE INTO configuration_attribute VALUES
(403, 'insideSebEnableStartTaskManager', 'CHECKBOX', null, null, null, null, 'false'),
(404, 'insideSebEnableLogOff', 'CHECKBOX', null, null, null, null, 'false'),
(405, 'insideSebEnableShutDown', 'CHECKBOX', null, null, null, null, 'false'),
(406, 'insideSebEnableEaseOfAccess', 'CHECKBOX', null, null, null, null, 'false'),
(407, 'insideSebEnableVmWareClientShade', 'CHECKBOX', null, null, null, null, 'false'),
(406, 'insideSebEnableVmWareClientShade', 'CHECKBOX', null, null, null, null, 'false'),
(407, 'insideSebEnableEaseOfAccess', 'CHECKBOX', null, null, null, null, 'false'),
(408, 'insideSebEnableNetworkConnectionSelector', 'CHECKBOX', null, null, null, null, 'false'),
(500, 'enableEsc', 'CHECKBOX', null, null, null, null, 'false'),
@ -254,11 +254,9 @@ INSERT IGNORE INTO configuration_attribute VALUES
(808, 'monitorProcesses', 'CHECKBOX', null, null, null, null, 'false'),
(809, 'blacklistURLFilter', 'TEXT_FIELD', null, null, null, null, ''),
(810, 'whitelistURLFilter', 'TEXT_FIELD', null, null, null, null, ''),
(811, 'killExplorerShell', 'CHECKBOX', null, null, null, null, 'false'),
(812, 'allowWlan', 'CHECKBOX', null, null, null, null, 'false'),
(813, 'hookKeys', 'CHECKBOX', null, null, null, null, 'true'),
(1000, 'originatorVersion', 'TEXT_FIELD', null, null, null, null, 'SEB_Server_0.3.0'),
(1001, 'sebConfigPurpose', 'RADIO_SELECTION', null, '0,1', null, null, '0')
@ -320,12 +318,12 @@ INSERT IGNORE INTO orientation VALUES
(50, 50, 0, 3, null, 7, 1, 5, 1, 'TOP'),
(51, 51, 0, 3, 'userAgentDesktop', 7, 2, 5, 2, 'NONE'),
(52, 52, 0, 3, 'userAgentDesktop', 7, 3, 5, 1, 'NONE'),
(53, 53, 0, 3, 'userAgentTouch', 7, 4, 5, 3, 'NONE'),
(54, 54, 0, 3, 'userAgentTouch', 7, 8, 5, 1, 'NONE'),
(55, 55, 0, 3, 'userAgentMac', 7, 9, 5, 2, 'NONE'),
(56, 56, 0, 3, 'userAgentMac', 7, 11, 5, 1, 'NONE'),
(57, 57, 0, 3, null, 0, 14, 6, 1, 'NONE'),
(58, 58, 0, 3, null, 7, 14, 5, 1, 'TOP'),
(53, 53, 0, 3, 'userAgentTouch', 7, 4, 5, 2, 'NONE'),
(54, 54, 0, 3, 'userAgentTouch', 7, 6, 5, 1, 'NONE'),
(55, 55, 0, 3, 'userAgentMac', 7, 8, 5, 2, 'NONE'),
(56, 56, 0, 3, 'userAgentMac', 7, 10, 5, 1, 'NONE'),
(57, 57, 0, 3, null, 0, 13, 6, 1, 'NONE'),
(58, 58, 0, 3, null, 7, 13, 5, 1, 'TOP'),
(59, 59, 0, 4, null, 0, 0, 8, 1, 'NONE'),
(60, 60, 0, 4, null, 3, 1, 5, 1, 'LEFT_SPAN'),