text:
package org.kuali.rice.krad.lookup;

import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.kuali.rice.core.api.CoreApiServiceLocator;
import org.kuali.rice.core.api.config.property.ConfigurationService;
import org.kuali.rice.core.api.encryption.EncryptionService;
import org.kuali.rice.core.api.search.SearchOperator;
import org.kuali.rice.core.api.util.RiceKeyConstants;
import org.kuali.rice.core.api.util.type.TypeUtils;
import org.kuali.rice.krad.bo.ExternalizableBusinessObject;
import org.kuali.rice.krad.datadictionary.BusinessObjectEntry;
import org.kuali.rice.krad.datadictionary.DataObjectEntry;
import org.kuali.rice.krad.datadictionary.validation.constraint.ValidCharactersConstraint;
import org.kuali.rice.krad.service.DataObjectAuthorizationService;
import org.kuali.rice.krad.service.DocumentDictionaryService;
import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
import org.kuali.rice.krad.service.LookupService;
import org.kuali.rice.krad.service.ModuleService;
import org.kuali.rice.krad.uif.UifConstants;
import org.kuali.rice.krad.uif.UifParameters;
import org.kuali.rice.krad.uif.control.Control;
import org.kuali.rice.krad.uif.control.FilterableLookupCriteriaControl;
import org.kuali.rice.krad.uif.control.FilterableLookupCriteriaControlPostData;
import org.kuali.rice.krad.uif.control.HiddenControl;
import org.kuali.rice.krad.uif.control.ValueConfiguredControl;
import org.kuali.rice.krad.uif.element.Link;
import org.kuali.rice.krad.uif.field.InputField;
import org.kuali.rice.krad.uif.lifecycle.ViewPostMetadata;
import org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl;
import org.kuali.rice.krad.uif.util.ComponentUtils;
import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
import org.kuali.rice.krad.uif.util.ScriptUtils;
import org.kuali.rice.krad.util.BeanPropertyComparator;
import org.kuali.rice.krad.util.ErrorMessage;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.KRADConstants;
import org.kuali.rice.krad.util.KRADUtils;
import org.kuali.rice.krad.util.MessageMap;
import org.kuali.rice.krad.util.UrlFactory;

import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * View helper service that implements {@link Lookupable} and executes a search using the
 * {@link org.kuali.rice.krad.service.LookupService}.
 *
 * @author Kuali Rice Team (rice.collab@kuali.org)
 * @see LookupForm
 * @see LookupView
 * @see org.kuali.rice.krad.service.LookupService
 */
public class LookupableImpl extends ViewHelperServiceImpl implements Lookupable {
    private static final long serialVersionUID = 1885161468871327740L;
    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(LookupableImpl.class);

    private Class<?> dataObjectClass;

    private transient DataObjectAuthorizationService dataObjectAuthorizationService;
    private transient DocumentDictionaryService documentDictionaryService;
    private transient LookupService lookupService;
    private transient EncryptionService encryptionService;

    /**
     * {@inheritDoc}
     */
    @Override
    public Collection<?> performSearch(LookupForm form, Map<String, String> searchCriteria, boolean bounded) {
        // removed blank search values and decrypt any encrypted search values
        Map<String, String> adjustedSearchCriteria = processSearchCriteria(form, searchCriteria);

        boolean isValidCriteria = validateSearchParameters(form, adjustedSearchCriteria);
        if (!isValidCriteria) {
            return new ArrayList<Object>();
        }

        List<String> wildcardAsLiteralSearchCriteria = identifyWildcardDisabledFields(form, adjustedSearchCriteria);

        // return empty search results (none found) when the search doesn't have any adjustedSearchCriteria although
        // a filtered search criteria is specified
        if (adjustedSearchCriteria == null) {
            MessageMap messageMap = GlobalVariables.getMessageMap();
            messageMap.putInfoForSectionId(UifConstants.MessageKeys.LOOKUP_RESULT_MESSAGES,
                    RiceKeyConstants.INFO_LOOKUP_RESULTS_NONE_FOUND);
            return new ArrayList<Object>();
        }

        // if this class is an EBO, just call the module service to get the results
        if (ExternalizableBusinessObject.class.isAssignableFrom(getDataObjectClass())) {
            return getSearchResultsForEBO(adjustedSearchCriteria, !bounded);
        }

        // if any of the properties refer to an embedded EBO, call the EBO lookups first and apply to the local lookup
        try {
            if (LookupUtils.hasExternalBusinessObjectProperty(getDataObjectClass(), adjustedSearchCriteria)) {
                adjustedSearchCriteria = LookupUtils.adjustCriteriaForNestedEBOs(getDataObjectClass(),
                        adjustedSearchCriteria, !bounded);

                if (LOG.isDebugEnabled()) {
                    LOG.debug("Passing these results into the lookup service: " + adjustedSearchCriteria);
                }
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Error trying to check for nested external business objects", e);
        } catch (InstantiationException e1) {
            throw new RuntimeException("Error trying to check for nested external business objects", e1);
        }

        Integer searchResultsLimit = null;
        if (bounded) {
            searchResultsLimit = LookupUtils.getSearchResultsLimit(getDataObjectClass(), form);
        }

        // invoke the lookup search to carry out the search
        Collection<?> searchResults = executeSearch(adjustedSearchCriteria, wildcardAsLiteralSearchCriteria, bounded,
                searchResultsLimit);

        generateLookupResultsMessages(adjustedSearchCriteria, searchResults, bounded, searchResultsLimit);

        Collection<?> sortedResults;
        if (searchResults != null) {
            sortedResults = new ArrayList<Object>(searchResults);

            sortSearchResults(form, (List<?>) sortedResults);
        } else {
            sortedResults = new ArrayList<Object>();
        }

        return sortedResults;
    }

    /**
     * Invoked to execute the search with the given criteria and restrictions.
     *
     * @param adjustedSearchCriteria map of criteria that has been adjusted (encyrption, ebos, etc)
     * @param wildcardAsLiteralSearchCriteria map of criteria to treat as literals (wildcards disabled)
     * @param bounded indicates whether the search should be bounded
     * @param searchResultsLimit for bounded searches, the result limit
     * @return Collection<?> collection of data object instances from the search results
     */
    protected Collection<?> executeSearch(Map<String, String> adjustedSearchCriteria,
            List<String> wildcardAsLiteralSearchCriteria, boolean bounded, Integer searchResultsLimit) {
        return getLookupService().findCollectionBySearchHelper(getDataObjectClass(), adjustedSearchCriteria,
                wildcardAsLiteralSearchCriteria, !bounded, searchResultsLimit);
    }

    /**
     * Filters the search criteria to be used with the lookup.
     *
     *
Processing entails primarily of the removal of filtered and unused/blank search criteria.  Encrypted field
     * values are decrypted, and date range fields are combined into a single criteria entry.

     *
     *
In special cases additional non-valid criteria may be included. E.g. with the KIM User Control as a criteria
     * the principal name may be passed so that it is displayed on the control.  The filtering removes these values
     * based on the viewPostMetadata.  When calling the search directly (methodToCall=search) the viewPostMetadata is
     * not set before filtering therefore non-valid criteria are not supported in these cases.

     *
     * @param lookupForm lookup form instance containing the lookup data
     * @param searchCriteria map of criteria to process
     * @return map of processed criteria
     */
    protected Map<String, String> processSearchCriteria(LookupForm lookupForm, Map<String, String> searchCriteria) {
        Map<String, InputField> criteriaFields = new HashMap<String, InputField>();
        if (lookupForm.getView() != null) {
            criteriaFields = getCriteriaFieldsForValidation((LookupView) lookupForm.getView(), lookupForm);
        }

        // combine date range criteria
        Map<String, String> filteredSearchCriteria = LookupUtils.preprocessDateFields(searchCriteria);

        // allow lookup inputs to filter the criteria
        for (String fieldName : searchCriteria.keySet()) {
            InputField inputField = criteriaFields.get(fieldName);

            if ((inputField == null) || !(inputField instanceof LookupInputField)) {
                continue;
            }

            LookupInputField lookupInputField = (LookupInputField) inputField;
            String propertyName = lookupInputField.getPropertyName();

            // get the post data for the filterable controls
            ViewPostMetadata viewPostMetadata = lookupForm.getViewPostMetadata();
            if (viewPostMetadata != null) {
                Object componentPostData = viewPostMetadata.getComponentPostData(lookupForm.getViewId(),
                        UifConstants.PostMetadata.FILTERABLE_LOOKUP_CRITERIA);
                Map<String, FilterableLookupCriteriaControlPostData> filterableLookupCriteria =
                        (Map<String, FilterableLookupCriteriaControlPostData>) componentPostData;

                // first filter the results using the filter on the control
                if (filterableLookupCriteria != null && filterableLookupCriteria.containsKey(propertyName)) {
                    FilterableLookupCriteriaControlPostData postData = filterableLookupCriteria.get(propertyName);
                    Class<? extends FilterableLookupCriteriaControl> controlClass = postData.getControlClass();
                    FilterableLookupCriteriaControl control = KRADUtils.createNewObjectFromClass(controlClass);

                    filteredSearchCriteria = control.filterSearchCriteria(propertyName, filteredSearchCriteria,
                            postData);
                }

                // second filter the results using the filter in the input field
                filteredSearchCriteria = lookupInputField.filterSearchCriteria(filteredSearchCriteria);

                // early return if we have no results
                if (filteredSearchCriteria == null) {
                    return null;
                }
            }
        }

        // decryption any encrypted search values
        Map<String, String> processedSearchCriteria = new HashMap<String, String>();
        for (String fieldName : filteredSearchCriteria.keySet()) {
            String fieldValue = filteredSearchCriteria.get(fieldName);

            // do not add hidden or blank criteria
            InputField inputField = criteriaFields.get(fieldName);
            if (((inputField != null) && (inputField.getControl() instanceof HiddenControl)) || StringUtils.isBlank(
                    fieldValue)) {
                continue;
            }

            if (fieldValue.endsWith(EncryptionService.ENCRYPTION_POST_PREFIX)) {
                String encryptedValue = StringUtils.removeEnd(fieldValue, EncryptionService.ENCRYPTION_POST_PREFIX);
                try {
                    fieldValue = getEncryptionService().decrypt(encryptedValue);
                } catch (GeneralSecurityException e) {
                    throw new RuntimeException("Error decrypting value for business object class " +
                            getDataObjectClass() + " attribute " + fieldName, e);
                }
            }

            processedSearchCriteria.put(fieldName, fieldValue);
        }

        return processedSearchCriteria;
    }

    /**
     * Determines which searchCriteria have been configured with wildcard characters disabled.
     *
     * @param lookupForm form used to collect search criteria
     * @param searchCriteria Map of property names and values to use as search parameters
     * @return List of property names which have wildcard characters disabled
     */
    protected List<String> identifyWildcardDisabledFields(LookupForm lookupForm, Map<String, String> searchCriteria) {
        List<String> wildcardAsLiteralPropertyNames = new ArrayList<String>();

        if (searchCriteria != null) {
            Map<String, InputField> criteriaFields = new HashMap<String, InputField>();
            if (lookupForm.getView() != null) {
                criteriaFields = getCriteriaFieldsForValidation((LookupView) lookupForm.getView(), lookupForm);
            }

            for (String fieldName : searchCriteria.keySet()) {
                InputField inputField = criteriaFields.get(fieldName);
                if ((inputField == null) || !(inputField instanceof LookupInputField)) {
                    continue;
                }

                if ((LookupInputField.class.isAssignableFrom(inputField.getClass())) && (((LookupInputField) inputField)
                        .isDisableWildcardsAndOperators())) {
                    wildcardAsLiteralPropertyNames.add(fieldName);
                }
            }
        }

        return wildcardAsLiteralPropertyNames;
    }

    /**
     * Invoked to perform validation on the search criteria before the search is performed.
     *
     *
Check required criteria have a value

     *
Check that criteria data type supports wildcards/operators

     *
Check that wildcards/operators are not used on a secure criteria

     *
Display info message when wildcards/operators are disabled

     *
Throw exception when invalid criteria are specified

     *
     * @param form lookup form instance containing the lookup data
     * @param searchCriteria map of criteria where key is search property name and value is
     * search value (which can include wildcards)
     * @return boolean true if validation was successful, false if there were errors and the search
     * should not be performed
     */
    protected boolean validateSearchParameters(LookupForm form, Map<String, String> searchCriteria) {
        boolean valid = true;

        if (searchCriteria == null) {
            return valid;
        }

        // The form view can't be relied upon since the complete lifecycle hasn't ran against it.  Instead
        // the viewPostMetadata is being used for the validation.
        // If the view was not previously posted then it's impossible to validate the search parameters because
        // of the missing viewPostMetadata.  When this happens we assume the search parameters are correct.
        // (Calling the search controller method directly without displaying the lookup first can cause
        // this situation.)
        if (form.getViewPostMetadata() == null) {
            return valid;
        }

        Set<String> unprocessedSearchCriteria = new HashSet<String>(searchCriteria.keySet());
        for (Map.Entry<String, Map<String, Object>> lookupCriteria : form.getViewPostMetadata().getLookupCriteria()
                .entrySet()) {
            String propertyName = lookupCriteria.getKey();
            Map<String, Object> lookupCriteriaAttributes = lookupCriteria.getValue();

            unprocessedSearchCriteria.remove(propertyName);

            if (isCriteriaRequired(lookupCriteriaAttributes) && StringUtils.isBlank(searchCriteria.get(propertyName))) {
                GlobalVariables.getMessageMap().putError(propertyName, RiceKeyConstants.ERROR_REQUIRED,
                        getCriteriaLabel(form, (String) lookupCriteriaAttributes.get(
                                UifConstants.LookupCriteriaPostMetadata.COMPONENT_ID)));
            }

            ValidCharactersConstraint constraint = getSearchCriteriaConstraint(lookupCriteriaAttributes);
            if (constraint != null) {
                validateSearchParameterConstraint(form, propertyName, lookupCriteriaAttributes, searchCriteria.get(
                        propertyName), constraint);
            }

            if (searchCriteria.containsKey(propertyName)) {
                validateSearchParameterWildcardAndOperators(form, propertyName, lookupCriteriaAttributes,
                        searchCriteria.get(propertyName));
            }
        }

        if (!unprocessedSearchCriteria.isEmpty()) {
            throw new RuntimeException(
                    "Invalid search value sent for property name(s): " + unprocessedSearchCriteria.toString());
        }

        if (GlobalVariables.getMessageMap().hasErrors()) {
            valid = false;
        }

        return valid;
    }

    /**
     * Validates that any wildcards contained within the search value are valid wildcards and allowed for the
     * property type for which the field is searching.
     *
     * @param form lookup form instance containing the lookup data
     * @param propertyName property name of the search criteria field to be validated
     * @param searchPropertyValue value given for field to search for
     */
    protected void validateSearchParameterWildcardAndOperators(LookupForm form, String propertyName,
            Map<String, Object> lookupCriteriaAttributes, String searchPropertyValue) {
        if (StringUtils.isBlank(searchPropertyValue)) {
            return;
        }

        // make sure a wildcard/operator is in the value
        boolean found = false;
        for (SearchOperator op : SearchOperator.QUERY_CHARACTERS) {
            String queryCharacter = op.op();

            if (searchPropertyValue.contains(queryCharacter)) {
                found = true;
            }
        }

        // no query characters to validate
        if (!found) {
            return;
        }

        if (isCriteriaWildcardDisabled(lookupCriteriaAttributes)) {
            Class<?> propertyType = ObjectPropertyUtils.getPropertyType(getDataObjectClass(), propertyName);

            if (TypeUtils.isIntegralClass(propertyType) || TypeUtils.isDecimalClass(propertyType) ||
                    TypeUtils.isTemporalClass(propertyType)) {
                GlobalVariables.getMessageMap().putError(propertyName,
                        RiceKeyConstants.ERROR_WILDCARDS_AND_OPERATORS_NOT_ALLOWED_ON_FIELD, getCriteriaLabel(form, (String) lookupCriteriaAttributes.get(
                                        UifConstants.LookupCriteriaPostMetadata.COMPONENT_ID)));
            } else if (TypeUtils.isStringClass(propertyType)) {
                GlobalVariables.getMessageMap().putInfo(propertyName,
                        RiceKeyConstants.INFO_WILDCARDS_AND_OPERATORS_TREATED_LITERALLY, getCriteriaLabel(form, (String) lookupCriteriaAttributes.get(
                                        UifConstants.LookupCriteriaPostMetadata.COMPONENT_ID)));
            }
        } else if (isCriteriaSecure(lookupCriteriaAttributes)) {
            GlobalVariables.getMessageMap().putError(propertyName, RiceKeyConstants.ERROR_SECURE_FIELD,
                    getCriteriaLabel(form, (String) lookupCriteriaAttributes.get(
                            UifConstants.LookupCriteriaPostMetadata.COMPONENT_ID)));
        }
    }

    /**
     * Validates that the searchPropertyValue is a valid value based on any constraint that may exist for the property
     *
     * @param form lookup form instance containing the lookup data
     * @param propertyName property name of the search criteria field to be validated
     * @param lookupCriteriaAttributes attributes for the lookup criteria
     * @param searchPropertyValue value given for field to search for
     * @param validCharactersConstraint constraint on the lookup criteria field
     */
    protected void validateSearchParameterConstraint(LookupForm form, String propertyName,
            Map<String, Object> lookupCriteriaAttributes, String searchPropertyValue,
            ValidCharactersConstraint validCharactersConstraint) {
        if (StringUtils.isBlank(searchPropertyValue)) {
            return;
        }

        Matcher matcher = Pattern.compile(validCharactersConstraint.getValue()).matcher(searchPropertyValue);
        if (!matcher.find()) {
            String[] prefixParams = {getCriteriaLabel(form, (String) lookupCriteriaAttributes.get(
                    UifConstants.LookupCriteriaPostMetadata.COMPONENT_ID))};
            ErrorMessage errorMessage = new ErrorMessage(validCharactersConstraint.getMessageKey(),
                    validCharactersConstraint.getValidationMessageParamsArray());
            errorMessage.setMessagePrefixKey(UifConstants.Messages.PROPERTY_NAME_PREFIX);
            errorMessage.setMessagePrefixParameters(prefixParams);
            GlobalVariables.getMessageMap().putError(propertyName, errorMessage);
        }
    }

    /**
     * Returns the label of the search criteria field.
     *
     * @param form lookup form instance containing the lookup data
     * @param componentId component id of the search criteria field
     * @return label of the search criteria field
     */
    protected String getCriteriaLabel(LookupForm form, String componentId) {
        return (String) form.getViewPostMetadata().getComponentPostData(componentId, UifConstants.PostMetadata.LABEL);
    }

    /**
     * Indicator if wildcards and operators are disabled on this search criteria.
     *
     * @param lookupCriteria the viewPostMetadata with the attributes of the search criteria field
     * @return true if wildcards and operators are disabled, false otherwise
     */
    protected boolean isCriteriaWildcardDisabled(Map lookupCriteria) {
        return BooleanUtils.isTrue((Boolean) lookupCriteria.get(UifConstants.LookupCriteriaPostMetadata.DISABLE_WILDCARDS_AND_OPERATORS));
    }

    /**
     * Indicator if the search criteria is required.
     *
     * @param lookupCriteria the viewPostMetadata with the attributes of the search criteria field
     * @return true if the search criteria is required, false otherwise
     */
    protected boolean isCriteriaRequired(Map lookupCriteria) {
        return BooleanUtils.isTrue((Boolean) lookupCriteria.get(UifConstants.LookupCriteriaPostMetadata.REQUIRED));
    }

    /**
     * Indicator if the search criteria is on a secure field.
     *
     * @param lookupCriteria the viewPostMetadata with the attributes of the search criteria field
     * @return true if the search criteria is a secure field, false otherwise
     */
    protected boolean isCriteriaSecure(Map lookupCriteria) {
        return BooleanUtils.isTrue((Boolean) lookupCriteria.get(UifConstants.LookupCriteriaPostMetadata.SECURE_VALUE));
    }

    /**
     * Indicator if the search criteria has a valid Characters Constraint.
     *
     * @param lookupCriteria the viewPostMetadata with the attributes of the search criteria field
     * @return the ValidCharactersConstraint if the search criteria has a valid characters constraint
     */
    protected ValidCharactersConstraint getSearchCriteriaConstraint(Map lookupCriteria) {
        return (ValidCharactersConstraint) lookupCriteria.get(
                UifConstants.LookupCriteriaPostMetadata.VALID_CHARACTERS_CONSTRAINT);
    }

    /**
     * Generates messages for the user based on the search results.
     *
     *
Messages are generated for the number of results, if the results exceed the result limit, and if the
     * search was done using the primary keys for the data object.

     *
     * @param searchCriteria map of search criteria that was used for the search
     * @param searchResults list of result data objects from the search
     * @param bounded whether the search was bounded
     * @param searchResultsLimit maximum number of search results to return
     */
    protected void generateLookupResultsMessages(Map<String, String> searchCriteria, Collection<?> searchResults,
            boolean bounded, Integer searchResultsLimit) {
        MessageMap messageMap = GlobalVariables.getMessageMap();

        Long searchResultsSize = Long.valueOf(0);
        if (searchResults instanceof CollectionIncomplete
                && ((CollectionIncomplete<?>) searchResults).getActualSizeIfTruncated() > 0) {
            searchResultsSize = ((CollectionIncomplete<?>) searchResults).getActualSizeIfTruncated();
        } else if (searchResults != null) {
            searchResultsSize = Long.valueOf(searchResults.size());
        }

        if (searchResultsSize == 0) {
            messageMap.putInfoForSectionId(UifConstants.MessageKeys.LOOKUP_RESULT_MESSAGES,
                    RiceKeyConstants.INFO_LOOKUP_RESULTS_NONE_FOUND);
        } else if (searchResultsSize == 1) {
            messageMap.putInfoForSectionId(UifConstants.MessageKeys.LOOKUP_RESULT_MESSAGES,
                    RiceKeyConstants.INFO_LOOKUP_RESULTS_DISPLAY_ONE);
        } else if (searchResultsSize > 1) {
            boolean resultsExceedsLimit =
                    bounded && (searchResultsLimit != null) && (searchResultsSize > searchResultsLimit);

            if (resultsExceedsLimit) {
                messageMap.putInfoForSectionId(UifConstants.MessageKeys.LOOKUP_RESULT_MESSAGES,
                        RiceKeyConstants.INFO_LOOKUP_RESULTS_EXCEEDS_LIMIT, searchResultsSize.toString(),
                        searchResultsLimit.toString());
            } else {
                messageMap.putInfoForSectionId(UifConstants.MessageKeys.LOOKUP_RESULT_MESSAGES,
                        RiceKeyConstants.INFO_LOOKUP_RESULTS_DISPLAY_ALL, searchResultsSize.toString());
            }
        }

        Boolean usingPrimaryKey = getLookupService().allPrimaryKeyValuesPresentAndNotWildcard(getDataObjectClass(),
                searchCriteria);

        if (usingPrimaryKey) {
            List<String> pkNames = getLegacyDataAdapter().listPrimaryKeyFieldNames(getDataObjectClass());

            List<String> pkLabels = new ArrayList<String>();
            for (String pkName : pkNames) {
                pkLabels.add(getDataDictionaryService().getAttributeLabel(getDataObjectClass(), pkName));
            }

            messageMap.putInfoForSectionId(UifConstants.MessageKeys.LOOKUP_RESULT_MESSAGES,
                    RiceKeyConstants.INFO_LOOKUP_RESULTS_USING_PRIMARY_KEY, StringUtils.join(pkLabels, ","));
        }
    }

    /**
     * Sorts the given list of search results based on the lookup view's configured sort attributes.
     *
     *
First if the posted view exists we grab the sort attributes from it. This will take into account expressions
     * that might have been configured on the sort attributes. If the posted view does not exist (because we did a
     * search from a get request or form session storage is off), we get the sort attributes from the view that we
     * will be rendered (and was initialized before controller call). However, expressions will not be evaluated yet,
     * thus if expressions were configured we don't know the results and can not sort the list

     *
     * @param form lookup form instance containing view information
     * @param searchResults list of search results to sort
     * @TODO: revisit this when we have a solution for the posted view problem
     */
    protected void sortSearchResults(LookupForm form, List<?> searchResults) {
        List<String> defaultSortColumns = null;
        boolean defaultSortAscending = true;

        if (form.getView() != null) {
            defaultSortColumns = ((LookupView) form.getView()).getDefaultSortAttributeNames();
            defaultSortAscending = ((LookupView) form.getView()).isDefaultSortAscending();
        }

        boolean hasExpression = false;
        if (defaultSortColumns != null) {
            for (String sortColumn : defaultSortColumns) {
                if (sortColumn == null) {
                    hasExpression = true;
                }
            }
        }

        if (hasExpression) {
            defaultSortColumns = null;
        }

        if ((defaultSortColumns != null) && (!defaultSortColumns.isEmpty())) {
            BeanPropertyComparator comparator = new BeanPropertyComparator(defaultSortColumns, true);
            if (defaultSortAscending) {
                Collections.sort(searchResults, comparator);
            } else {
                Collections.sort(searchResults, Collections.reverseOrder(comparator));
            }
        }
    }

    /**
     * Performs a search against an {@link org.kuali.rice.krad.bo.ExternalizableBusinessObject} by invoking the
     * module service
     *
     * @param searchCriteria map of criteria currently set
     * @param unbounded indicates whether the complete result should be returned.  When set to false the result is
     * limited (if necessary) to the max search result limit configured.
     * @return list of result objects, possibly bounded
     */
    protected List<?> getSearchResultsForEBO(Map<String, String> searchCriteria, boolean unbounded) {
        ModuleService eboModuleService = KRADServiceLocatorWeb.getKualiModuleService().getResponsibleModuleService(
                getDataObjectClass());

        BusinessObjectEntry ddEntry = eboModuleService.getExternalizableBusinessObjectDictionaryEntry(
                getDataObjectClass());

        Map<String, String> filteredFieldValues = new HashMap<String, String>();
        for (String fieldName : searchCriteria.keySet()) {
            if (ddEntry.getAttributeNames().contains(fieldName)) {
                filteredFieldValues.put(fieldName, searchCriteria.get(fieldName));
            }
        }

        Map<String, Object> translatedValues = KRADUtils.coerceRequestParameterTypes(
                (Class<? extends ExternalizableBusinessObject>) getDataObjectClass(), filteredFieldValues);

        List<?> searchResults = eboModuleService.getExternalizableBusinessObjectsListForLookup(
                (Class<? extends ExternalizableBusinessObject>) getDataObjectClass(), (Map) translatedValues,
                unbounded);

        return searchResults;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Map<String, String> performClear(LookupForm form, Map<String, String> lookupCriteria) {
        Map<String, String> clearedLookupCriteria = new HashMap<String, String>();

        Map<String, InputField> criteriaFieldMap = new HashMap<String, InputField>();
        if (form.getView() != null) {
            criteriaFieldMap = getCriteriaFieldsForValidation((LookupView) form.getView(), form);
        }

        // fields marked as read only through the initial request should not be cleared
        List<String> readOnlyFieldsList = form.getReadOnlyFieldsList();

        for (Map.Entry<String, String> searchKeyValue : lookupCriteria.entrySet()) {
            String searchPropertyName = searchKeyValue.getKey();
            String searchPropertyValue = searchKeyValue.getValue();

            InputField inputField = criteriaFieldMap.get(searchPropertyName);

            if (readOnlyFieldsList == null || !readOnlyFieldsList.contains(searchPropertyName)) {
                if (inputField != null && inputField.getDefaultValue() != null) {
                    searchPropertyValue = inputField.getDefaultValue().toString();
                } else {
                    searchPropertyValue = "";
                }
            }

            clearedLookupCriteria.put(searchPropertyName, searchPropertyValue);
        }

        return clearedLookupCriteria;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void buildReturnUrlForResult(Link returnLink, Object model) {
        LookupForm lookupForm = (LookupForm) model;

        Map<String, Object> returnLinkContext = returnLink.getContext();
        LookupView lookupView = returnLinkContext == null ? null : (LookupView) returnLinkContext.get(
                UifConstants.ContextVariableNames.VIEW);
        Object dataObject = returnLinkContext == null ? null : returnLinkContext.get(
                UifConstants.ContextVariableNames.LINE);

        // don't render return link if the object is null or if the row is not returnable
        if ((dataObject == null) || (!isResultReturnable(dataObject))) {
            returnLink.setRender(false);

            return;
        }

        String dataReturnValue = "true";
        if (lookupForm.isReturnByScript()) {
            Map<String, String> translatedKeyValues = getTranslatedReturnKeyValues(lookupView, lookupForm, dataObject);

            dataReturnValue = ScriptUtils.translateValue(translatedKeyValues);

            returnLink.setHref("#");
        } else if (StringUtils.isBlank(returnLink.getHref())) {
            String href = getReturnUrl(lookupView, lookupForm, dataObject);

            if (StringUtils.isNotBlank(href)) {
                returnLink.setHref(href);
            } else {
                returnLink.setRender(false);
                return;
            }

            String target = lookupForm.getReturnTarget();

            if (StringUtils.isNotBlank(target)) {
                returnLink.setTarget(target);
            }
        }

        // add data attribute for attaching event handlers on the return links
        returnLink.addDataAttribute(UifConstants.DataAttributes.RETURN, dataReturnValue);

        // build return link title if not already set
        if (StringUtils.isBlank(returnLink.getTitle())) {
            String linkLabel = getConfigurationService().getPropertyValueAsString(
                    KRADConstants.Lookup.TITLE_RETURN_URL_PREPENDTEXT_PROPERTY);

            Map<String, String> returnKeyValues = getReturnKeyValues(lookupView, lookupForm, dataObject);

            String title = KRADUtils.buildAttributeTitleString(linkLabel, getDataObjectClass(), returnKeyValues);
            returnLink.setTitle(title);
        }
    }

    /**
     * Determines whether a given data object that's returned as one of the lookup's results is considered returnable,
     * which means that for single-value lookups, a "return value" link may be rendered, and for multiple
     * value lookups, a checkbox is rendered.
     *
     *
Note that this can be part of an authorization mechanism, but not the complete authorization mechanism. The
     * component that invoked the lookup/ lookup caller (e.g. document, nesting lookup, etc.) needs to check
     * that the object that was passed to it was returnable as well because there are ways around this method
     * (e.g. crafting a custom return URL).

     *
     * @param dataObject an object from the search result set
     * @return true if the row is returnable and false if it is not
     */
    protected boolean isResultReturnable(Object dataObject) {
        return true;
    }

    /**
     * Builds the URL for returning the given data object result row.
     *
     *
Note return URL will only be built if a return location is specified on the lookup form

     *
     * @param lookupView lookup view instance containing lookup configuration
     * @param lookupForm lookup form instance containing the data
     * @param dataObject data object instance for the current line and for which the return URL is being built
     * @return String return URL or blank if URL cannot be built
     */
    protected String getReturnUrl(LookupView lookupView, LookupForm lookupForm, Object dataObject) {
        Properties props = getReturnUrlParameters(lookupView, lookupForm, dataObject);

        String href = "";
        if (StringUtils.isNotBlank(lookupForm.getReturnLocation())) {
            href = UrlFactory.parameterizeUrl(lookupForm.getReturnLocation(), props);
        }

        return href;
    }

    /**
     * Builds up a {@code Properties} object that will be used to provide the request parameters for the
     * return URL link
     *
     * @param lookupView lookup view instance containing lookup configuration
     * @param lookupForm lookup form instance containing the data
     * @param dataObject data object instance for the current line and for which the return URL is being built
     * @return Properties instance containing request parameters for return URL
     */
    protected Properties getReturnUrlParameters(LookupView lookupView, LookupForm lookupForm, Object dataObject) {
        Properties props = new Properties();
        props.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, KRADConstants.RETURN_METHOD_TO_CALL);

        if (StringUtils.isNotBlank(lookupForm.getReturnFormKey())) {
            props.put(UifParameters.FORM_KEY, lookupForm.getReturnFormKey());
        }

        props.put(KRADConstants.REFRESH_CALLER, lookupView.getId());
        props.put(KRADConstants.REFRESH_DATA_OBJECT_CLASS, getDataObjectClass().getName());

        if (StringUtils.isNotBlank(lookupForm.getReferencesToRefresh())) {
            props.put(UifParameters.REFERENCES_TO_REFRESH, lookupForm.getReferencesToRefresh());
        }

        if (StringUtils.isNotBlank(lookupForm.getQuickfinderId())) {
            props.put(UifParameters.QUICKFINDER_ID, lookupForm.getQuickfinderId());
        }

        Map<String, String> returnKeyValues = getTranslatedReturnKeyValues(lookupView, lookupForm, dataObject);
        props.putAll(returnKeyValues);

        return props;
    }

    /**
     * Returns a map of the configured return keys translated to their corresponding field conversion with
     * the associated values.
     *
     * @param lookupView lookup view instance containing lookup configuration
     * @param lookupForm lookup form instance containing the data
     * @param dataObject data object instance
     * @return Map<String, String> map of translated return key/value pairs
     */
    protected Map<String, String> getTranslatedReturnKeyValues(LookupView lookupView, LookupForm lookupForm,
            Object dataObject) {
        Map<String, String> translatedKeyValues = new HashMap<String, String>();

        Map<String, String> returnKeyValues = getReturnKeyValues(lookupView, lookupForm, dataObject);

        for (String returnKey : returnKeyValues.keySet()) {
            String returnValue = returnKeyValues.get(returnKey);

            // get name of the property on the calling view to pass back the parameter value as
            if (lookupForm.getFieldConversions().containsKey(returnKey)) {
                returnKey = lookupForm.getFieldConversions().get(returnKey);
            }

            translatedKeyValues.put(returnKey, returnValue);
        }

        return translatedKeyValues;
    }

    /**
     * Returns a map of the configured return keys with their selected values.
     *
     * @param lookupView lookup view instance containing lookup configuration
     * @param lookupForm lookup form instance containing the data
     * @param dataObject data object instance
     * @return Map<String, String> map of return key/value pairs
     */
    protected Map<String, String> getReturnKeyValues(LookupView lookupView, LookupForm lookupForm, Object dataObject) {
        List<String> returnKeys;

        if (lookupForm.getFieldConversions() != null && !lookupForm.getFieldConversions().isEmpty()) {
            returnKeys = new ArrayList<String>(lookupForm.getFieldConversions().keySet());
        } else {
            returnKeys = getLegacyDataAdapter().listPrimaryKeyFieldNames(getDataObjectClass());
        }

        List<String> secureReturnKeys = lookupView.getAdditionalSecurePropertyNames();

        return KRADUtils.getPropertyKeyValuesFromDataObject(returnKeys, secureReturnKeys, dataObject);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void buildMaintenanceActionLink(Link actionLink, Object model, String maintenanceMethodToCall) {
        LookupForm lookupForm = (LookupForm) model;

        Map<String, Object> actionLinkContext = actionLink.getContext();
        Object dataObject = actionLinkContext == null ? null : actionLinkContext.get(
                UifConstants.ContextVariableNames.LINE);

        List<String> pkNames = getLegacyDataAdapter().listPrimaryKeyFieldNames(getDataObjectClass());

        // build maintenance link href if needed
        if (StringUtils.isBlank(actionLink.getHref())) {
            String href = getMaintenanceActionUrl(lookupForm, dataObject, maintenanceMethodToCall, pkNames);
            if (StringUtils.isBlank(href)) {
                actionLink.setRender(false);

                return;
            }

            actionLink.setHref(href);
        }

        // build action title if not set
        if (StringUtils.isBlank(actionLink.getTitle())) {
            String objectLabel = "";
            DataObjectEntry dataObjectEntry = getDataDictionaryService().getDataDictionary().getDataObjectEntry(
                    getDataObjectClass().getName());

            // check to see if there was a data object entry found for the class
            // if there is a data entry object, then set the object label, else let it be empty string
            if (dataObjectEntry != null && dataObjectEntry.getObjectLabel() != null) {
                objectLabel = dataObjectEntry.getObjectLabel();
            }

            ConfigurationService service = getConfigurationService();
            String val = service.getPropertyValueAsString(KRADConstants.Lookup.TITLE_ACTION_URL_PREPENDTEXT_PROPERTY);
            String prependTitleText = actionLink.getLinkText() + " " + objectLabel + " " +
                    getConfigurationService().getPropertyValueAsString(
                            KRADConstants.Lookup.TITLE_ACTION_URL_PREPENDTEXT_PROPERTY);

            Map<String, String> primaryKeyValues = KRADUtils.getPropertyKeyValuesFromDataObject(pkNames, dataObject);
            String title = KRADUtils.buildAttributeTitleString(prependTitleText, getDataObjectClass(),
                    primaryKeyValues);

            actionLink.setTitle(title);
        }
    }

    /**
     * Generates a URL to perform a maintenance action on the given result data object.
     *
     *
Will build a URL containing keys of the data object to invoke the given maintenance action method
     * within the maintenance controller

     *
     * @param lookupForm lookup form
     * @param dataObject data object instance for the line to build the maintenance action link for
     * @param methodToCall method name on the maintenance controller that should be invoked
     * @param pkNames list of primary key field names for the data object whose key/value pairs will be added to
     * the maintenance link
     * @return String URL link for the maintenance action
     */
    protected String getMaintenanceActionUrl(LookupForm lookupForm, Object dataObject, String methodToCall,
            List<String> pkNames) {
        LookupView lookupView = (LookupView) lookupForm.getView();

        Properties props = new Properties();
        props.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, methodToCall);

        Map<String, String> primaryKeyValues = KRADUtils.getPropertyKeyValuesFromDataObject(pkNames, dataObject);
        for (String primaryKey : primaryKeyValues.keySet()) {
            String primaryKeyValue = primaryKeyValues.get(primaryKey);

            props.put(primaryKey, primaryKeyValue);
        }

        if (StringUtils.isNotBlank(lookupForm.getReturnLocation())) {
            props.put(KRADConstants.RETURN_LOCATION_PARAMETER, lookupForm.getReturnLocation());
        }

        props.put(UifParameters.DATA_OBJECT_CLASS_NAME, lookupForm.getDataObjectClassName());
        props.put(UifParameters.VIEW_TYPE_NAME, UifConstants.ViewType.MAINTENANCE.name());

        String maintenanceMapping = KRADConstants.Maintenance.REQUEST_MAPPING_MAINTENANCE;
        if (lookupView != null && StringUtils.isNotBlank(lookupView.getMaintenanceUrlMapping())) {
            maintenanceMapping = lookupView.getMaintenanceUrlMapping();
        }

        return UrlFactory.parameterizeUrl(maintenanceMapping, props);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void buildMultiValueSelectField(InputField selectField, Object model) {
        LookupForm lookupForm = (LookupForm) model;

        Map<String, Object> selectFieldContext = selectField.getContext();
        Object lineDataObject = selectFieldContext == null ? null : selectFieldContext.get(
                UifConstants.ContextVariableNames.LINE);
        if (lineDataObject == null) {
            throw new RuntimeException("Unable to get data object for line from component: " + selectField.getId());
        }

        Control selectControl = selectField.getControl();
        if ((selectControl != null) && (selectControl instanceof ValueConfiguredControl)) {
            // get value for each field conversion from line and add to lineIdentifier
            List<String> fromFieldNames = new ArrayList<String>(lookupForm.getFieldConversions().keySet());
            // Per KULRICE-12125 we need to remove secure field names from this list.
            fromFieldNames = new ArrayList<String>(KRADUtils.getPropertyKeyValuesFromDataObject(fromFieldNames,
                    lineDataObject).keySet());

            Collections.sort(fromFieldNames);
            lookupForm.setMultiValueReturnFields(fromFieldNames);
            String lineIdentifier = "";
            for (String fromFieldName : fromFieldNames) {
                Object fromFieldValue = ObjectPropertyUtils.getPropertyValue(lineDataObject, fromFieldName);

                if (fromFieldValue != null) {
                    lineIdentifier += fromFieldValue;
                }

                lineIdentifier += ":";
            }
            lineIdentifier = StringUtils.removeEnd(lineIdentifier, ":");

            ((ValueConfiguredControl) selectControl).setValue(lineIdentifier);
        }
    }

    /**
     * Determines if given data object has associated maintenance document that allows new or copy
     * maintenance actions.
     *
     * @return boolean true if the maintenance new or copy action is allowed for the data object instance, false
     * otherwise
     */
    public boolean allowsMaintenanceNewOrCopyAction() {
        boolean allowsNewOrCopy = false;

        String maintDocTypeName = getMaintenanceDocumentTypeName();
        if (StringUtils.isNotBlank(maintDocTypeName)) {
            allowsNewOrCopy = getDataObjectAuthorizationService().canCreate(getDataObjectClass(),
                    GlobalVariables.getUserSession().getPerson(), maintDocTypeName);
        }

        return allowsNewOrCopy;
    }

    /**
     * Determines if given data object has associated maintenance document that allows edit maintenance
     * actions.
     *
     * @param dataObject data object
     * @return boolean true if the maintenance edit action is allowed for the data object instance, false otherwise
     */
    public boolean allowsMaintenanceEditAction(Object dataObject) {
        boolean allowsEdit = false;

        String maintDocTypeName = getMaintenanceDocumentTypeName();
        if (StringUtils.isNotBlank(maintDocTypeName)) {
            allowsEdit = getDataObjectAuthorizationService().canMaintain(dataObject,
                    GlobalVariables.getUserSession().getPerson(), maintDocTypeName);
        }

        return allowsEdit;
    }

    /**
     * Determines if given data object has associated maintenance document that allows delete maintenance
     * actions.
     *
     * @param dataObject data object
     * @return boolean true if the maintenance delete action is allowed for the data object instance, false otherwise
     */
    public boolean allowsMaintenanceDeleteAction(Object dataObject) {
        boolean allowsMaintain = false;

        String maintDocTypeName = getMaintenanceDocumentTypeName();
        if (StringUtils.isNotBlank(maintDocTypeName)) {
            allowsMaintain = getDataObjectAuthorizationService().canMaintain(dataObject,
                    GlobalVariables.getUserSession().getPerson(), maintDocTypeName);
        }

        boolean allowsDelete = getDocumentDictionaryService().getAllowsRecordDeletion(getDataObjectClass());

        return allowsDelete && allowsMaintain;
    }

    /**
     * Returns the maintenance document type associated with the business object class or null if one does not exist.
     *
     * @return String representing the maintenance document type name
     */
    protected String getMaintenanceDocumentTypeName() {
        DocumentDictionaryService dd = getDocumentDictionaryService();

        return dd.getMaintenanceDocumentTypeName(getDataObjectClass());
    }

    /**
     * Returns the criteria fields in a map keyed by the field property name.
     *
     * @param lookupView lookup view instance to pull criteria fields from
     * @param form lookup form instance containing the lookup data
     * @return map of criteria fields
     */
    protected Map<String, InputField> getCriteriaFieldsForValidation(LookupView lookupView, LookupForm form) {
        Map<String, InputField> criteriaFieldMap = new HashMap<String, InputField>();

        if (lookupView.getCriteriaFields() == null) {
            return criteriaFieldMap;
        }

        List<InputField> fields = null;
        if (lookupView.getCriteriaGroup().getItems().size() > 0) {
            fields = ComponentUtils.getNestedContainerComponents(lookupView.getCriteriaGroup(), InputField.class);
        } else if (lookupView.getCriteriaFields().size() > 0) {
            // If criteriaGroup items are empty look to see if criteriaFields has any input components.
            // This is to ensure that if initializeGroup hasn't been called on the view, the validations will still happen on criteriaFields
            fields = ComponentUtils.getComponentsOfType(lookupView.getCriteriaFields(), InputField.class);
        } else {
            fields = new ArrayList<InputField>();
        }
        for (InputField field : fields) {
            criteriaFieldMap.put(field.getPropertyName(), field);
        }

        return criteriaFieldMap;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Class<?> getDataObjectClass() {
        return this.dataObjectClass;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setDataObjectClass(Class<?> dataObjectClass) {
        this.dataObjectClass = dataObjectClass;
    }

    protected DataObjectAuthorizationService getDataObjectAuthorizationService() {
        if (dataObjectAuthorizationService == null) {
            this.dataObjectAuthorizationService = KRADServiceLocatorWeb.getDataObjectAuthorizationService();
        }
        return dataObjectAuthorizationService;
    }

    public void setDataObjectAuthorizationService(DataObjectAuthorizationService dataObjectAuthorizationService) {
        this.dataObjectAuthorizationService = dataObjectAuthorizationService;
    }

    public DocumentDictionaryService getDocumentDictionaryService() {
        if (documentDictionaryService == null) {
            documentDictionaryService = KRADServiceLocatorWeb.getDocumentDictionaryService();
        }
        return documentDictionaryService;
    }

    public void setDocumentDictionaryService(DocumentDictionaryService documentDictionaryService) {
        this.documentDictionaryService = documentDictionaryService;
    }

    protected LookupService getLookupService() {
        if (lookupService == null) {
            this.lookupService = KRADServiceLocatorWeb.getLookupService();
        }
        return lookupService;
    }

    public void setLookupService(LookupService lookupService) {
        this.lookupService = lookupService;
    }

    protected EncryptionService getEncryptionService() {
        if (encryptionService == null) {
            this.encryptionService = CoreApiServiceLocator.getEncryptionService();
        }
        return encryptionService;
    }

    public void setEncryptionService(EncryptionService encryptionService) {
        this.encryptionService = encryptionService;
    }

}


text:
layout: post
author: jbfelder
title: "Justyn Felder's Class Hack"
---

Embedded Code:

Reflection:

This assignment was very straightforward, but the requirement of having to go back and hack an already existing trinket is where I experienced much difficulty. I choose to hack my Poetry Slam as I have not worked with Turtles in a while. The examples shown in both the readings and the video help with setting up the classes. However, I had some trouble creating my "Print Poetry" sections of code in my newly made class. For some reason I could not use a For Loop to iterate through the length of a list, so I decided to use an If statement that would increase until the length was met.

redpajama_set_name: RedPajamaGithub


text:
package org.batfish.representation.aws;

import com.google.common.annotations.VisibleForTesting;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;

/** Represents a single AWS account */
@ParametersAreNonnullByDefault
public class Account implements Serializable {

  @Nonnull private final String _id;
  @Nonnull private final Map<String, Region> _regions;

  public Account(String id) {
    this(id, new HashMap<>());
  }

  private Account(String id, Map<String, Region> regions) {
    _id = id;
    _regions = regions;
  }

  @Nonnull
  public Region addOrGetRegion(String region) {
    return _regions.computeIfAbsent(region, Region::new);
  }

  @VisibleForTesting
  public void addRegion(Region region) {
    _regions.put(region.getName(), region);
  }

  public Collection<Region> getRegions() {
    return _regions.values();
  }

  @Nonnull
  public String getId() {
    return _id;
  }
}


text:
for s in "" "-noadd" "-nomult" "-limit" ; do
	export name=nonzero$s
	echo "processing $name..."
	if [ ! -e $name.txt ] ; then
		g++ -O2 -Wall $name.cpp -o $name.exe
		./$name.exe >$name.txt
	fi
done

gawk -f filter.awk       name="gen_external" lo=3 comment="non-zero test"    nonzero.txt        > ../src/gen_external.h
gawk -f filter.awk       name="gen_nomult"   lo=6 comment="anti-nomult test" nonzero-nomult.txt > ../src/gen_nomult.h
gawk -f filter-limit.awk name="gen_limit"    lo=2 comment="anti-limit test"  nonzero-limit.txt  > ../src/gen_limit.h
python conv-hand.py <hand.in >../src/gen_hand.h

grep "almost 5 6" nonzero.txt > almost56.txt


text:
package net.danlew.customviews.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;

/**
 * An ImageView that draws its contents as a circle.
 *
 * Normally you should probably make a custom Drawable, but this is just for explanatory purposes.
 */
public class CircleView extends View {

    private Bitmap mIconBitmap;
    private BitmapShader mBitmapShader;
    private Matrix mMatrix = new Matrix();
    private Paint mPaint;

    public CircleView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setBitmapResource(int iconRes) {
        mIconBitmap = BitmapFactory.decodeResource(getResources(), iconRes);
        mBitmapShader = new BitmapShader(mIconBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setShader(mBitmapShader);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        int width = getWidth();
        int height = getHeight();
        int bWidth = mIconBitmap.getWidth();
        int bHeight = mIconBitmap.getHeight();

        // Use a Matrix to scale the bitmap (naive method assumes bWidth == bHeight or aspect ratio gets messed up)
        mMatrix.setScale((float) width / bWidth, (float) height / bHeight);
        mBitmapShader.setLocalMatrix(mMatrix);

        canvas.drawCircle(width / 2, height / 2, width / 2, mPaint);
    }
}


text:
function hadoop_subproject_init
{
  if [[ -z "${HADOOP_MAPRED_ENV_PROCESSED}" ]]; then
    if [[ -e "${HADOOP_CONF_DIR}/mapred-env.sh" ]]; then
      . "${HADOOP_CONF_DIR}/mapred-env.sh"
      export HADOOP_MAPRED_ENV_PROCESSED=true
    fi
  fi

  # at some point in time, someone thought it would be a good idea to
  # create separate vars for every subproject.  *sigh*
  # let's perform some overrides and setup some defaults for bw compat
  # this way the common hadoop var's == subproject vars and can be
  # used interchangeable from here on out
  # ...
  # this should get deprecated at some point.

  hadoop_deprecate_envvar HADOOP_MAPRED_LOG_DIR HADOOP_LOG_DIR

  hadoop_deprecate_envvar HADOOP_MAPRED_LOGFILE HADOOP_LOGFILE

  hadoop_deprecate_envvar HADOOP_MAPRED_NICENESS HADOOP_NICENESS

  hadoop_deprecate_envvar HADOOP_MAPRED_STOP_TIMEOUT HADOOP_STOP_TIMEOUT

  hadoop_deprecate_envvar HADOOP_MAPRED_PID_DIR HADOOP_PID_DIR

  hadoop_deprecate_envvar HADOOP_MAPRED_ROOT_LOGGER HADOOP_ROOT_LOGGER

  HADOOP_MAPRED_HOME="${HADOOP_MAPRED_HOME:-$HADOOP_PREFIX}"

  hadoop_deprecate_envvar HADOOP_MAPRED_IDENT_STRING HADOOP_IDENT_STRING
}

if [[ -z "${HADOOP_LIBEXEC_DIR}" ]]; then
  _mc_this="${BASH_SOURCE-$0}"
  HADOOP_LIBEXEC_DIR=$(cd -P -- "$(dirname -- "${_mc_this}")" >/dev/null && pwd -P)
fi

if [[ -n "${HADOOP_COMMON_HOME}" ]] &&
   [[ -e "${HADOOP_COMMON_HOME}/libexec/hadoop-config.sh" ]]; then
  . "${HADOOP_COMMON_HOME}/libexec/hadoop-config.sh"
elif [[ -e "${HADOOP_LIBEXEC_DIR}/hadoop-config.sh" ]]; then
  . "${HADOOP_LIBEXEC_DIR}/hadoop-config.sh"
elif [ -e "${HADOOP_PREFIX}/libexec/hadoop-config.sh" ]; then
  . "${HADOOP_PREFIX}/libexec/hadoop-config.sh"
else
  echo "ERROR: Hadoop common not found." 2>&1
  exit 1
fi


text:
Check if one or multiple hosts listen one or multiple ports periodically and
continue once all the connections succeed. Status will be set to "false" if the
user connections succeed right away, considering that no change had occured.
Otherwise it will be set to "true".

## Return

Status is set to "true" if the first connection attempt was a failure and the
connection finaly succeeded.

## TODO

The `server` configuration property shall be renamed `address`.

## Examples

Wait for two domains on the same port.

```js
const {$status} = await nikita.network.tcp.wait({
  hosts: [ '1.domain.com', '2.domain.com' ],
  port: 80
})
console.info(`Servers listening on port 80: ${$status}`)
```

Wait for one domain on two diffents ports.

```js
const {$status} = await nikita.network.tcp.wait({
  host: 'my.domain.com',
  ports: [80, 443]
})
console.info(`Servers listening on ports 80 and 443: ${$status}`)
```

Wait for two domains on diffents ports.

```js
const {$status} = await nikita.network.tcp.wait({
  servers: [
    {host: '1.domain.com', port: 80},
    {host: '2.domain.com', port: 443}
  ]
})
console.info(`Servers listening: ${$status}`)
```

## Hooks

    on_action = ({config}) ->
      if config.server
        if Array.isArray config.server
        then config.server = utils.array.flatten config.server
        else config.server = [config.server]
      extract_servers = (config) ->
        if typeof config is 'string'
          [host, port] = config.split ':'
          config = host: host, port: port
        return [] if not config.host or not config.port
        if config.host
          config.host = [config.host] unless Array.isArray config.host
        if config.port
          config.port = [config.port] unless Array.isArray config.port
        servers = []
        for host in config.host or []
          for port in config.port or []
            servers.push host: host, port: port
        servers
      srvs = extract_servers config
      if config.server
        srvs.push ...extract_servers(srv) for srv in config.server
      config.server = srvs
      config.server = utils.array.flatten config.server

## Schema definitions

    definitions =
      config:
        type: 'object'
        properties:
          'host':
            type: 'array'
            items:
              type: 'string'
            description: '''
            One or multiple hosts, used to build or enrich the 'server' option.
            '''
          'interval':
            default: 2000
            type: 'number'
            description: '''
            Time in millisecond between each connection attempt.
            '''
          'quorum':
            type: ['boolean', 'integer']
            description: '''
            Number of minimal successful connection, 50%+1 if "true".
            '''
          'port':
            type: 'array'
            items:
              type: 'integer'
            description: '''
            One or multiple ports, used to build or enrich the 'server' option.
            '''
          'randdir':
            type: 'string'
            description: '''
            Directory where to write temporary file used internally to store state
            information. It default to a temporary location.
            '''
          'server':
            oneOf: [
              type: 'string'
            ,
              type: 'object'
              properties:
                host: $ref: '#/definitions/config/properties/host'
                port: $ref: '#/definitions/config/properties/port'
            ,
              type: 'array'
              items:
                oneOf: [
                  type: 'string'
                ,
                  type: 'object'
                  properties:
                    host: $ref: '#/definitions/config/properties/host'
                    port: $ref: '#/definitions/config/properties/port'
                ]
            ]
            description: '''
            One or multiple servers, string must be in the form of
            "{host}:{port}", object must have the properties "host" and "port".
            '''
          'timeout':
            type: 'integer'
            description: '''
            Maximum time in millisecond to wait until this function is considered
            to have failed.
            '''

## Handler

    handler = ({config, tools: {log}}) ->
      unless config.server?.length
        log message: "No connection to wait for", level: 'WARN'
        return
      # Validate servers
      config.interval = Math.round config.interval / 1000
      quorum_target = config.quorum
      if quorum_target and quorum_target is true
        quorum_target = Math.ceil config.server.length / 2
      else unless quorum_target?
        quorum_target = config.server.length
      # Note, the option is not tested and doesnt seem to work from a manual test
      config.timeout = 0 unless config.timeout > 0
      # config.timeout = Math.round config.timeout / 1000
      try
        await @execute
          bash: true
          command: """
          function compute_md5 {
            echo $1 | openssl md5 | sed 's/^.* \\([a-z0-9]*\\)$/\\1/g'
          }
          addresses=( #{config.server.map((server) -> "'" + server.host + "':'" + server.port + "'").join(' ')} )
          timeout=#{config.timeout or ''}
          md5=`compute_md5 ${addresses[@]}`
          randdir="#{config.randdir or ''}"
          if [ -z $randir ]; then
            if [ -w /dev/shm ]; then
              randdir="/dev/shm/$md5"
            else
              randdir="/tmp/$md5"
            fi
          fi
          quorum_target=#{quorum_target}
          echo "[INFO] randdir is: $randdir"
          mkdir -p $randdir
          echo 3 > $randdir/signal
          echo '' > $randdir/quorum
          function get_time {
            # Return the time since epoch in millisecond
            # Note, date +%N doesn't work on MacOS, using Python instead
            # `date +%s%N | cut -b1-13` prints `1652694375N`
            python -c 'import time; print(int(time.time() * 1000))'
          }
          function remove_randdir {
            for address in "${addresses[@]}" ; do
              host="${address%%:*}"
              port="${address##*:}"
              rm -f $randdir/`compute_md5 $host:$port`
            done
          }
          function check_quorum {
            quorum_current=`wc -l < $randdir/quorum`
            echo "[DEBUG] Check if $quorum_current gt $quorum_target"
            if [ $quorum_current -ge $quorum_target ]; then
              echo '[INFO] Quorum is reached'
              remove_randdir
            fi
          }
          function wait_connection {
            local host=$1
            local port=$2
            local randfile4conn=$3
            local count=0
            echo "[DEBUG] Start wait for $host:$port"
            isopen="echo > '/dev/tcp/$host/$port'"
            touch "$randfile4conn"
            while [[ -f "$randfile4conn" ]] && ! `bash -c "$isopen" 2>/dev/null`; do
              # Exit if timeout signal is broadcasted by any child
              if [[ $(< $randdir/signal) == '2' ]]; then exit; fi
              ((count++))
              echo "[DEBUG] Connection failed to $host:$port on attempt $count" >&2
              echo "[INFO] timeout is $timeout" >&2
              if [ ! -z "$timeout" ]; then
                current_time=`get_time`
                (( $start_time+$timeout > $current_time )) && echo 2 > $randdir/signal
              fi
              sleep #{config.interval}
            done
            if [[ -f "$randfile4conn" ]]; then
              echo "[DEBUG] Connection ready to $host:$port"
            fi
            echo $host:$port >> $randdir/quorum
            check_quorum
            if [ "$count" -gt "0" ]; then
              echo "[WARN] Status is now active, count is $count"
              echo 0 > $randdir/signal
            fi
          }
          start_time=`get_time`
          for address in "${addresses[@]}" ; do
            host="${address%%:*}"
            port="${address##*:}"
            randfile4conn=$randdir/`compute_md5 $host:$port`
            wait_connection $host $port $randfile4conn &
          done
          wait
          # Clean up
          signal=`cat $randdir/signal`
          remove_randdir
          echo "[INFO] Exit code is $signal"
          exit $signal
          """
          code: [0, 3]
          stdin_log: false
      catch err
        if err.exit_code is 2
          throw errors.NIKITA_TCP_WAIT_TIMEOUT({config})
        throw err

## Errors

    errors =
      NIKITA_TCP_WAIT_TIMEOUT: ({config}) ->
        utils.error 'NIKITA_TCP_WAIT_TIMEOUT', [
          "timeout reached after #{config.timeout}ms."
        ]

## Exports

    module.exports =
      handler: handler
      hooks:
        on_action: on_action
      metadata:
        definitions: definitions

## Dependencies

    utils = require '@nikitajs/core/lib/utils'


text:
package org.github.sekruse.cashflow.controller.sqlite;

import org.github.sekruse.cashflow.controller.CashflowControllerFactory;
import org.github.sekruse.cashflow.controller.CashflowControllerInstantiationException;

import java.io.IOException;
import java.sql.SQLException;
import java.util.Properties;

/**
 * This factory creates SQLiteCashflowController instances.
 *
 * @author Sebastian
 * @since 08.02.2015.
 */
public class SQLiteCashflowControllerFactory implements CashflowControllerFactory<SQLiteCashflowController> {

    public static final String DB_URL = "db.url";

    @Override
    public SQLiteCashflowController create(Properties properties) throws CashflowControllerInstantiationException {
        SQLiteCashflowController controller = createInstance(properties);
        try {
            controller.createSchema();
        } catch (IOException | SQLException e) {
            throw new CashflowControllerInstantiationException("Could not create the database schema.", e);
        }
        return controller;
    }

    @Override
    public SQLiteCashflowController connect(Properties properties) throws CashflowControllerInstantiationException {
        return createInstance(properties);
    }

    private SQLiteCashflowController createInstance(Properties properties) throws CashflowControllerInstantiationException {
        String dbUrl = properties.getProperty(DB_URL);
        return new SQLiteCashflowController(dbUrl);
    }
}


text:
<?php

namespace Marek\ArticlesBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class MarekArticlesBundle extends Bundle
{
}


text:
/* -*- Mode: C++; -*- */
// C&C NLP tools
// Copyright (c) Universities of Edinburgh, Oxford and Sydney
// Copyright (c) James R. Curran
//
// This software is covered by a non-commercial use licence.
// See LICENCE.txt for the full text of the licence.
//
// If LICENCE.txt is not included in this distribution
// please email candc@it.usyd.edu.au to obtain a copy.

namespace NLP {
  namespace Thesaurus {

    class MBinaryDice: public Measure {
    public:
      MBinaryDice(void): Measure("BinaryDice") {};
      ~MBinaryDice(void) {};
      float measure(const Object *obj1, const Object *obj2) const;
      bool heuristic(const Object *obj1, const Object *obj2) const;
      void explain(std::ostream &out, const Object *obj1, const Object *obj2) const;
    };

  }
}


text:
/**
 * Created by Kyle on 1/8/2016.
 */
import React, { PropTypes } from 'react';
import SwipeActionListItem from './Components/SwipeActionListItem';

const SwipeActionList = (props) => {
  const { listItems, actionEvents } = props;

  const listStyle = {
    verflow: 'hidden',
    listStyle: 'none',
    padding: 0,
    margin: 0,
  };

  return (<ul style={listStyle}>
    {listItems.map((item) => {
      return (<SwipeActionListItem
        key={item.key}
        actions={actionEvents}
        actionDirection={item.actionDirection}
        actionDistance={item.actionDistance}
      >
        {item.node}
      </SwipeActionListItem>);
    })}
  </ul>);
};

SwipeActionList.propTypes = {
  listItems: PropTypes.shape({
    key: PropTypes.number.isRequired,
    node: PropTypes.node.isRequired,
    actionDirection: PropTypes.string,
    actionDistance: PropTypes.number,
  }),
  actionEvents: PropTypes.array,
};

export default SwipeActionList;


text:
"use strict"
var debug = require('debug')('hystore:admin:AdminController'),
    Promise = require('bluebird');

exports = module.exports = function(app) {
    var m = app.models;
    app.group('/admin', function(router){
        function* welcome(){
            yield this.render('admin/welcome.dust');
        }
        router.get('',  welcome);
        router.get('/', welcome);

        router.get('/login', function* login(){
            yield this.render('admin/login.dust');
        })
    })
}


text: Rubik's Cube in Typescript


text:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace ClipUp.Sdk
{
    /// <summary>
    /// Geneate the request data for a HTTP multipart upload.
    /// </summary>
    public class MultipartUpload
    {
        private readonly string boundary;
        private readonly string boundaryCode;

        /// <summary>
        /// Gets the list of files to be uploaded as part of the request.
        /// </summary>
        /// <value>The upload files.</value>
        public IList<MultipartFile> Files { get; } = new List<MultipartFile>();

        /// <summary>
        /// Gets the list of forms to be uploaded as part of the request.
        /// </summary>
        /// <value>The upload forms.</value>
        public IDictionary<string, object> Forms { get; } = new Dictionary<string, object>();

        /// <summary>
        /// Gets the content type of the multipart upload request.
        /// </summary>
        /// <value>The upload content type.</value>
        public string ContentType => $"multipart/form-data; boundary=--------------{this.boundaryCode}";

        /// <summary>
        /// Gets the content length of the multipart upload request.
        /// </summary>
        /// <value>The upload content length.</value>
        public long ContentLength
        {
            get
            {
                var ascii = new ASCIIEncoding();
                long contentLength = ascii.GetBytes(this.boundary).Length;

                foreach (var form in this.Forms)
                {
                    contentLength += ascii.GetBytes(CreateFormBoundaryHeader(form.Key, form.Value)).Length;
                    contentLength += ascii.GetBytes(this.boundary).Length;
                }

                foreach (var file in this.Files)
                {
                    contentLength += ascii.GetBytes(CreateFileBoundaryHeader(file)).Length;
                    contentLength += file.Data.Length;
                    contentLength += ascii.GetBytes(this.boundary).Length;
                }

                contentLength += ascii.GetBytes("--").Length;

                return contentLength;
            }
        }

        /// <summary>
        /// Gets the request data of the multipart upload.
        /// </summary>
        /// <value>The upload request data.</value>
        public byte[] RequestData
        {
            get
            {
                using (var stream = new MemoryStream())
                {
                    stream.WriteString(this.boundary);

                    foreach (var form in this.Forms)
                    {
                        stream.WriteString(CreateFormBoundaryHeader(form.Key, form.Value));
                        stream.WriteString(this.boundary);
                    }

                    foreach (var file in this.Files)
                    {
                        stream.WriteString(CreateFileBoundaryHeader(file));
                        StreamFileContents(file.Data, file, stream);
                        stream.WriteString(this.boundary);
                    }

                    stream.WriteString("--");

                    return stream.ToArray();
                }
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="MultipartUpload"/> class.
        /// </summary>
        public MultipartUpload()
        {
            this.boundaryCode = DateTime.Now.Ticks.GetHashCode() + "548130";
            this.boundary = $"\r\n----------------{this.boundaryCode}";
        }

        private static string CreateFileBoundaryHeader(MultipartFile multiPartFile)
        {
            return $"\r\nContent-Disposition: form-data; name=\"{multiPartFile.FieldName}\"; filename=\"{Path.GetFileName(multiPartFile.FileName)}\"\r\n" + $"Content-Type: {multiPartFile.ContentType}\r\n" + $"Content-Transfer-Encoding: {multiPartFile.ContentTransferEncoding}\r\n\r\n";
        }

        private static string CreateFormBoundaryHeader(string name, object value)
        {
            return $"\r\nContent-Disposition: form-data; name=\"{name}\"\r\n\r\n{value}";
        }

        private static void StreamFileContents(byte[] data, MultipartFile file, Stream stream)
        {
            switch (file.ContentTransferEncoding)
            {
                case "base64":
                    stream.WriteString(Convert.ToBase64String(data, 0, data.Length));
                    break;
                case "binary":
                    stream.Write(data, 0, data.Length);
                    break;
                default:
                    throw new Exception($"Unknown transfer encoding: {file.ContentTransferEncoding}");
            }
        }
    }
}


text:
///////////////////////////////////////////////////////////////////////////////
// Name:        src/msw/listbox.cpp
// Purpose:     wxListBox
// Author:      Julian Smart
// Modified by: Vadim Zeitlin (owner drawn stuff)
// Created:
// Copyright:   (c) Julian Smart
// Licence:     wxWindows licence
///////////////////////////////////////////////////////////////////////////////

// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

#if wxUSE_LISTBOX

#include "wx/listbox.h"

#ifndef WX_PRECOMP
    #include "wx/dynarray.h"
    #include "wx/settings.h"
    #include "wx/brush.h"
    #include "wx/font.h"
    #include "wx/dc.h"
    #include "wx/utils.h"
    #include "wx/log.h"
    #include "wx/window.h"
#endif

#include "wx/msw/private.h"
#include "wx/msw/dc.h"

#include <windowsx.h>

#if wxUSE_OWNER_DRAWN
    #include  "wx/ownerdrw.h"
#endif

// ============================================================================
// list box item declaration and implementation
// ============================================================================

#if wxUSE_OWNER_DRAWN

class wxListBoxItem : public wxOwnerDrawn
{
public:
    wxListBoxItem(wxListBox *parent)
        { m_parent = parent; }

    wxListBox *GetParent() const
        { return m_parent; }

    int GetIndex() const
        { return m_parent->GetItemIndex(const_cast<wxListBoxItem*>(this)); }

    wxString GetName() const
        { return m_parent->GetString(GetIndex()); }

private:
    wxListBox *m_parent;
};

wxOwnerDrawn *wxListBox::CreateLboxItem(size_t WXUNUSED(n))
{
    return new wxListBoxItem(this);
}

#endif  //USE_OWNER_DRAWN

// ============================================================================
// list box control implementation
// ============================================================================

// ----------------------------------------------------------------------------
// creation
// ----------------------------------------------------------------------------

void wxListBox::Init()
{
    m_noItems = 0;
    m_updateHorizontalExtent = false;
}

bool wxListBox::Create(wxWindow *parent,
                       wxWindowID id,
                       const wxPoint& pos,
                       const wxSize& size,
                       int n, const wxString choices[],
                       long style,
                       const wxValidator& validator,
                       const wxString& name)
{
    // initialize base class fields
    if ( !CreateControl(parent, id, pos, size, style, validator, name) )
        return false;

    // create the native control
    if ( !MSWCreateControl(wxT("LISTBOX"), wxEmptyString, pos, size) )
    {
        // control creation failed
        return false;
    }

    // initialize the contents
    for ( int i = 0; i < n; i++ )
    {
        Append(choices[i]);
    }

    // now we can compute our best size correctly, so do it again
    SetInitialSize(size);

    return true;
}

bool wxListBox::Create(wxWindow *parent,
                       wxWindowID id,
                       const wxPoint& pos,
                       const wxSize& size,
                       const wxArrayString& choices,
                       long style,
                       const wxValidator& validator,
                       const wxString& name)
{
    wxCArrayString chs(choices);
    return Create(parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
                  style, validator, name);
}

wxListBox::~wxListBox()
{
    Clear();
}

WXDWORD wxListBox::MSWGetStyle(long style, WXDWORD *exstyle) const
{
    WXDWORD msStyle = wxControl::MSWGetStyle(style, exstyle);

    // we always want to get the notifications
    msStyle |= LBS_NOTIFY;

    // without this style, you get unexpected heights, so e.g. constraint
    // layout doesn't work properly
    msStyle |= LBS_NOINTEGRALHEIGHT;

    wxASSERT_MSG( !(style & wxLB_MULTIPLE) || !(style & wxLB_EXTENDED),
                  wxT("only one of listbox selection modes can be specified") );

    if ( style & wxLB_MULTIPLE )
        msStyle |= LBS_MULTIPLESEL;
    else if ( style & wxLB_EXTENDED )
        msStyle |= LBS_EXTENDEDSEL;

    wxASSERT_MSG( !(style & wxLB_ALWAYS_SB) || !(style & wxLB_NO_SB),
                  wxT( "Conflicting styles wxLB_ALWAYS_SB and wxLB_NO_SB." ) );

    if ( !(style & wxLB_NO_SB) )
    {
        msStyle |= WS_VSCROLL;
        if ( style & wxLB_ALWAYS_SB )
            msStyle |= LBS_DISABLENOSCROLL;
    }

    if ( m_windowStyle & wxLB_HSCROLL )
        msStyle |= WS_HSCROLL;
    if ( m_windowStyle & wxLB_SORT )
        msStyle |= LBS_SORT;

#if wxUSE_OWNER_DRAWN && !defined(__WXWINCE__)
    if ( m_windowStyle & wxLB_OWNERDRAW )
    {
        // we don't support LBS_OWNERDRAWVARIABLE yet and we also always put
        // the strings in the listbox for simplicity even though we could have
        // avoided it in this case
        msStyle |= LBS_OWNERDRAWFIXED | LBS_HASSTRINGS;
    }
#endif // wxUSE_OWNER_DRAWN

    return msStyle;
}

void wxListBox::OnInternalIdle()
{
    wxWindow::OnInternalIdle();

    if (m_updateHorizontalExtent)
    {
        SetHorizontalExtent(wxEmptyString);
        m_updateHorizontalExtent = false;
    }
}

void wxListBox::MSWOnItemsChanged()
{
    // we need to do two things when items change: update their max horizontal
    // extent so that horizontal scrollbar could be shown or hidden as
    // appropriate and also invlaidate the best size
    //
    // updating the max extent is slow (it's an O(N) operation) and so we defer
    // it until the idle time but the best size should be invalidated
    // immediately doing it in idle time is too late -- layout using incorrect
    // old best size will have been already done by then

    m_updateHorizontalExtent = true;

    InvalidateBestSize();
}

// ----------------------------------------------------------------------------
// implementation of wxListBoxBase methods
// ----------------------------------------------------------------------------

void wxListBox::DoSetFirstItem(int N)
{
    wxCHECK_RET( IsValid(N),
                 wxT("invalid index in wxListBox::SetFirstItem") );

    SendMessage(GetHwnd(), LB_SETTOPINDEX, (WPARAM)N, (LPARAM)0);
}

void wxListBox::DoDeleteOneItem(unsigned int n)
{
    wxCHECK_RET( IsValid(n),
                 wxT("invalid index in wxListBox::Delete") );

#if wxUSE_OWNER_DRAWN
    if ( HasFlag(wxLB_OWNERDRAW) )
    {
        delete m_aItems[n];
        m_aItems.RemoveAt(n);
    }
#endif // wxUSE_OWNER_DRAWN

    SendMessage(GetHwnd(), LB_DELETESTRING, n, 0);
    m_noItems--;

    MSWOnItemsChanged();

    UpdateOldSelections();
}

int wxListBox::FindString(const wxString& s, bool bCase) const
{
    // back to base class search for not native search type
    if (bCase)
       return wxItemContainerImmutable::FindString( s, bCase );

    int pos = ListBox_FindStringExact(GetHwnd(), -1, s.t_str());
    if (pos == LB_ERR)
        return wxNOT_FOUND;
    else
        return pos;
}

void wxListBox::DoClear()
{
#if wxUSE_OWNER_DRAWN
    if ( HasFlag(wxLB_OWNERDRAW) )
    {
        WX_CLEAR_ARRAY(m_aItems);
    }
#endif // wxUSE_OWNER_DRAWN

    ListBox_ResetContent(GetHwnd());

    m_noItems = 0;
    MSWOnItemsChanged();

    UpdateOldSelections();
}

void wxListBox::DoSetSelection(int N, bool select)
{
    wxCHECK_RET( N == wxNOT_FOUND || IsValid(N),
                 wxT("invalid index in wxListBox::SetSelection") );

    if ( HasMultipleSelection() )
    {
        // Setting selection to -1 should deselect everything.
        const bool deselectAll = N == wxNOT_FOUND;
        SendMessage(GetHwnd(), LB_SETSEL,
                    deselectAll ? FALSE : select,
                    deselectAll ? -1 : N);
    }
    else
    {
        SendMessage(GetHwnd(), LB_SETCURSEL, select ? N : -1, 0);
    }

    UpdateOldSelections();
}

bool wxListBox::IsSelected(int N) const
{
    wxCHECK_MSG( IsValid(N), false,
                 wxT("invalid index in wxListBox::Selected") );

    return SendMessage(GetHwnd(), LB_GETSEL, N, 0) == 0 ? false : true;
}

void *wxListBox::DoGetItemClientData(unsigned int n) const
{
    // This is done here for the same reasons as in wxChoice method with the
    // same name.
    SetLastError(ERROR_SUCCESS);

    LPARAM rc = SendMessage(GetHwnd(), LB_GETITEMDATA, n, 0);
    if ( rc == LB_ERR && GetLastError() != ERROR_SUCCESS )
    {
        wxLogLastError(wxT("LB_GETITEMDATA"));

        return NULL;
    }

    return (void *)rc;
}

void wxListBox::DoSetItemClientData(unsigned int n, void *clientData)
{
    if ( ListBox_SetItemData(GetHwnd(), n, clientData) == LB_ERR )
    {
        wxLogDebug(wxT("LB_SETITEMDATA failed"));
    }
}

// Return number of selections and an array of selected integers
int wxListBox::GetSelections(wxArrayInt& aSelections) const
{
    aSelections.Empty();

    if ( HasMultipleSelection() )
    {
        int countSel = ListBox_GetSelCount(GetHwnd());
        if ( countSel == LB_ERR )
        {
            wxLogDebug(wxT("ListBox_GetSelCount failed"));
        }
        else if ( countSel != 0 )
        {
            int *selections = new int[countSel];

            if ( ListBox_GetSelItems(GetHwnd(),
                                     countSel, selections) == LB_ERR )
            {
                wxLogDebug(wxT("ListBox_GetSelItems failed"));
                countSel = -1;
            }
            else
            {
                aSelections.Alloc(countSel);
                for ( int n = 0; n < countSel; n++ )
                    aSelections.Add(selections[n]);
            }

            delete [] selections;
        }

        return countSel;
    }
    else  // single-selection listbox
    {
        if (ListBox_GetCurSel(GetHwnd()) > -1)
            aSelections.Add(ListBox_GetCurSel(GetHwnd()));

        return aSelections.Count();
    }
}

// Get single selection, for single choice list items
int wxListBox::GetSelection() const
{
    wxCHECK_MSG( !HasMultipleSelection(),
                 -1,
                 wxT("GetSelection() can't be used with multiple-selection listboxes, use GetSelections() instead.") );

    return ListBox_GetCurSel(GetHwnd());
}

// Find string for position
wxString wxListBox::GetString(unsigned int n) const
{
    wxCHECK_MSG( IsValid(n), wxEmptyString,
                 wxT("invalid index in wxListBox::GetString") );

    int len = ListBox_GetTextLen(GetHwnd(), n);

    // +1 for terminating NUL
    wxString result;
    ListBox_GetText(GetHwnd(), n, (wxChar*)wxStringBuffer(result, len + 1));

    return result;
}

int wxListBox::DoInsertItems(const wxArrayStringsAdapter & items,
                             unsigned int pos,
                             void **clientData,
                             wxClientDataType type)
{
    MSWAllocStorage(items, LB_INITSTORAGE);

    const bool append = pos == GetCount();

    // we must use CB_ADDSTRING when appending as only it works correctly for
    // the sorted controls
    const unsigned msg = append ? LB_ADDSTRING : LB_INSERTSTRING;

    if ( append )
        pos = 0;

    int n = wxNOT_FOUND;

    const unsigned int numItems = items.GetCount();
    for ( unsigned int i = 0; i < numItems; i++ )
    {
        n = MSWInsertOrAppendItem(pos, items[i], msg);
        if ( n == wxNOT_FOUND )
            return n;

        if ( !append )
            pos++;

        ++m_noItems;

#if wxUSE_OWNER_DRAWN
        if ( HasFlag(wxLB_OWNERDRAW) )
        {
            wxOwnerDrawn *pNewItem = CreateLboxItem(n);
            pNewItem->SetFont(GetFont());
            m_aItems.Insert(pNewItem, n);
        }
#endif // wxUSE_OWNER_DRAWN
        AssignNewItemClientData(n, clientData, i, type);
    }

    MSWOnItemsChanged();

    UpdateOldSelections();

    return n;
}

int wxListBox::DoHitTestList(const wxPoint& point) const
{
    LRESULT lRes = ::SendMessage(GetHwnd(), LB_ITEMFROMPOINT,
                                 0, MAKELPARAM(point.x, point.y));

    // non zero high-order word means that this item is outside of the client
    // area, IOW the point is outside of the listbox
    return HIWORD(lRes) ? wxNOT_FOUND : LOWORD(lRes);
}

void wxListBox::SetString(unsigned int n, const wxString& s)
{
    wxCHECK_RET( IsValid(n),
                 wxT("invalid index in wxListBox::SetString") );

    // remember the state of the item
    bool wasSelected = IsSelected(n);

    void *oldData = NULL;
    wxClientData *oldObjData = NULL;
    if ( HasClientUntypedData() )
        oldData = GetClientData(n);
    else if ( HasClientObjectData() )
        oldObjData = GetClientObject(n);

    // delete and recreate it
    SendMessage(GetHwnd(), LB_DELETESTRING, n, 0);

    int newN = n;
    if ( n == (m_noItems - 1) )
        newN = -1;

    ListBox_InsertString(GetHwnd(), newN, s.t_str());

    // restore the client data
    if ( oldData )
        SetClientData(n, oldData);
    else if ( oldObjData )
        SetClientObject(n, oldObjData);

    // we may have lost the selection
    if ( wasSelected )
        Select(n);

    MSWOnItemsChanged();
}

unsigned int wxListBox::GetCount() const
{
    return m_noItems;
}

// ----------------------------------------------------------------------------
// size-related stuff
// ----------------------------------------------------------------------------

void wxListBox::SetHorizontalExtent(const wxString& s)
{
    // the rest is only necessary if we want a horizontal scrollbar
    if ( !HasFlag(wxHSCROLL) )
        return;

    WindowHDC dc(GetHwnd());
    SelectInHDC selFont(dc, GetHfontOf(GetFont()));

    TEXTMETRIC lpTextMetric;
    ::GetTextMetrics(dc, &lpTextMetric);

    int largestExtent = 0;
    SIZE extentXY;

    if ( s.empty() )
    {
        // set extent to the max length of all strings
        for ( unsigned int i = 0; i < m_noItems; i++ )
        {
            const wxString str = GetString(i);
            ::GetTextExtentPoint32(dc, str.c_str(), str.length(), &extentXY);

            int extentX = (int)(extentXY.cx + lpTextMetric.tmAveCharWidth);
            if ( extentX > largestExtent )
                largestExtent = extentX;
        }
    }
    else // just increase the extent to the length of this string
    {
        int existingExtent = (int)SendMessage(GetHwnd(),
                                              LB_GETHORIZONTALEXTENT, 0, 0L);

        ::GetTextExtentPoint32(dc, s.c_str(), s.length(), &extentXY);

        int extentX = (int)(extentXY.cx + lpTextMetric.tmAveCharWidth);
        if ( extentX > existingExtent )
            largestExtent = extentX;
    }

    if ( largestExtent )
        SendMessage(GetHwnd(), LB_SETHORIZONTALEXTENT, LOWORD(largestExtent), 0L);
    //else: it shouldn't change
}

wxSize wxListBox::DoGetBestClientSize() const
{
    // find the widest string
    int wLine;
    int wListbox = 0;
    for (unsigned int i = 0; i < m_noItems; i++)
    {
        wxString str(GetString(i));
        GetTextExtent(str, &wLine, NULL);
        if ( wLine > wListbox )
            wListbox = wLine;
    }

    // give it some reasonable default value if there are no strings in the
    // list
    if ( wListbox == 0 )
        wListbox = 100;

    // the listbox should be slightly larger than the widest string
    wListbox += 3*GetCharWidth();

    // add room for the scrollbar
    wListbox += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);

    // don't make the listbox too tall (limit height to 10 items) but don't
    // make it too small neither
    int hListbox = SendMessage(GetHwnd(), LB_GETITEMHEIGHT, 0, 0)*
                    wxMin(wxMax(m_noItems, 3), 10);

    return wxSize(wListbox, hListbox);
}

// ----------------------------------------------------------------------------
// callbacks
// ----------------------------------------------------------------------------

bool wxListBox::MSWCommand(WXUINT param, WXWORD WXUNUSED(id))
{
    wxEventType evtType;
    if ( param == LBN_SELCHANGE )
    {
        if ( HasMultipleSelection() )
            return CalcAndSendEvent();

        evtType = wxEVT_LISTBOX;
    }
    else if ( param == LBN_DBLCLK )
    {
        // Clicking under the last item in the listbox generates double click
        // event for the currently selected item which is rather surprising.
        // Avoid the surprise by checking that we do have an item under mouse.
        const DWORD pos = ::GetMessagePos();
        const wxPoint pt(GET_X_LPARAM(pos), GET_Y_LPARAM(pos));
        if ( HitTest(ScreenToClient(pt)) == wxNOT_FOUND )
            return false;

        evtType = wxEVT_LISTBOX_DCLICK;
    }
    else
    {
        // some event we're not interested in
        return false;
    }

    const int n = ListBox_GetCurSel(GetHwnd());

    // We get events even when mouse is clicked outside of any valid item from
    // Windows, just ignore them.
    if ( n == wxNOT_FOUND )
       return false;

    if ( param == LBN_SELCHANGE )
    {
        if ( !DoChangeSingleSelection(n) )
            return false;
    }

    // Do generate an event otherwise.
    return SendEvent(evtType, n, true /* selection */);
}

// ----------------------------------------------------------------------------
// owner-drawn list boxes support
// ----------------------------------------------------------------------------

#if wxUSE_OWNER_DRAWN

// misc overloaded methods
// -----------------------

bool wxListBox::SetFont(const wxFont &font)
{
    if ( HasFlag(wxLB_OWNERDRAW) )
    {
        const unsigned count = m_aItems.GetCount();
        for ( unsigned i = 0; i < count; i++ )
            m_aItems[i]->SetFont(font);
    }

    wxListBoxBase::SetFont(font);

    return true;
}

bool wxListBox::GetItemRect(size_t n, wxRect& rect) const
{
    wxCHECK_MSG( IsValid(n), false,
                 wxT("invalid index in wxListBox::GetItemRect") );

    RECT rc;

    if ( ListBox_GetItemRect(GetHwnd(), n, &rc) != LB_ERR )
    {
        rect = wxRectFromRECT(rc);
        return true;
    }
    else
    {
        // couldn't retrieve rect: for example, item isn't visible
        return false;
    }
}

bool wxListBox::RefreshItem(size_t n)
{
    wxRect rect;
    if ( !GetItemRect(n, rect) )
        return false;

    RECT rc;
    wxCopyRectToRECT(rect, rc);

    return ::InvalidateRect((HWND)GetHWND(), &rc, FALSE) == TRUE;
}

// drawing
// -------

namespace
{
    // space beneath/above each row in pixels
    static const int LISTBOX_EXTRA_SPACE = 1;

} // anonymous namespace

// the height is the same for all items
// TODO should be changed for LBS_OWNERDRAWVARIABLE style listboxes

// NB: can't forward this to wxListBoxItem because LB_SETITEMDATA
//     message is not yet sent when we get here!
bool wxListBox::MSWOnMeasure(WXMEASUREITEMSTRUCT *item)
{
    // only owner-drawn control should receive this message
    wxCHECK( HasFlag(wxLB_OWNERDRAW), false );

    MEASUREITEMSTRUCT *pStruct = (MEASUREITEMSTRUCT *)item;

#ifdef __WXWINCE__
    HDC hdc = GetDC(NULL);
#else
    HDC hdc = CreateIC(wxT("DISPLAY"), NULL, NULL, 0);
#endif

    {
        wxDCTemp dc((WXHDC)hdc);
        dc.SetFont(GetFont());

        pStruct->itemHeight = dc.GetCharHeight() + 2 * LISTBOX_EXTRA_SPACE;
        pStruct->itemWidth  = dc.GetCharWidth();
    }

#ifdef __WXWINCE__
    ReleaseDC(NULL, hdc);
#else
    DeleteDC(hdc);
#endif

    return true;
}

// forward the message to the appropriate item
bool wxListBox::MSWOnDraw(WXDRAWITEMSTRUCT *item)
{
    // only owner-drawn control should receive this message
    wxCHECK( HasFlag(wxLB_OWNERDRAW), false );

    DRAWITEMSTRUCT *pStruct = (DRAWITEMSTRUCT *)item;

    // the item may be -1 for an empty listbox
    if ( pStruct->itemID == (UINT)-1 )
        return false;

    wxListBoxItem *pItem = (wxListBoxItem *)m_aItems[pStruct->itemID];

    wxDCTemp dc((WXHDC)pStruct->hDC);

    return pItem->OnDrawItem(dc, wxRectFromRECT(pStruct->rcItem),
                             (wxOwnerDrawn::wxODAction)pStruct->itemAction,
                             (wxOwnerDrawn::wxODStatus)(pStruct->itemState | wxOwnerDrawn::wxODHidePrefix));
}

#endif // wxUSE_OWNER_DRAWN

#endif // wxUSE_LISTBOX