private void updateProjectCache() { // Ensure we have a cache at all: initProjectCache(); // Loop through project path preference keys: for (Map.Entry<String, ?> entry : preferences.getAll().entrySet()) if (entry.getKey().startsWith(PREF_PROJECT_PATH_PREFIX) && entry.getKey().endsWith(PREF_PROJECT_PATH_POSTFIX)) { int projectID = getProjectID(entry.getKey()); int projectFingerPrint = getProjectFingerPrint(entry.getKey()); if (getCachedProject(projectID, projectFingerPrint) == null) { // Parse the project if it is not already in the cache: Project p = ProjectLoader.ParseProject(entry.getValue().toString()); if (p != null) { if (p.getFingerPrint() != projectFingerPrint) { Log.w( TAG, "XML finger print of project " + p.toString() + " has changed, possibly the " + ProjectLoader.PROJECT_FILE + " file (located in " + entry.getValue().toString() + ") was manually edited!"); // Remove old pref key: removeProjectPathPrefKey(projectID, projectFingerPrint); // Add new pref key: storeProjectPathPrefKey(p); } // Cache the project object: cacheProject(p); } } } }
/** * Gets project from cache if it is there, returns null otherwise * * @param projectID * @param projectFingerPrint * @return */ private Project getCachedProject(int projectID, int projectFingerPrint) { if (projectCache != null) for (Project cachedProj : projectCache) if (cachedProj.getID() == projectID && cachedProj.getFingerPrint() == projectFingerPrint) return cachedProj; return null; }
/* (non-Javadoc) * @see uk.ac.ucl.excites.sapelli.collector.db.ProjectStore#serialise(uk.ac.ucl.excites.sapelli.collector.model.Project, java.io.OutputStream) */ @Override public void serialise(Project project, OutputStream out) throws IOException { // Project XML bytes: InputStream projectXMLFileIn = new FileInputStream(ProjectLoader.GetProjectXMLFile(getProjectFolder(project))); ByteArrayOutputStream projectXMLBytesOut = new ByteArrayOutputStream(); IOUtils.copy(projectXMLFileIn, projectXMLBytesOut); projectXMLFileIn.close(); projectXMLBytesOut.close(); // FSI record bytes for each Form: List<byte[]> fsiRecordBytesList = new ArrayList<>(project.getNumberOfForms()); for (Form form : project.getForms()) { Record fsiRecord = retrieveFSIRecord(form); // we query the projectStore to save time... if (fsiRecord == null) // ... but we don't rely on it: fsiRecord = getFSIRecord(form); // may trigger optionality analysis fsiRecordBytesList.add(fsiRecord.toBytes(true)); } // Create serialisedProject valueSet: ValueSet<ColumnSet> serialisedProjectVS = new ValueSet<ColumnSet>( PROJECT_SERIALISIATION_CS, projectXMLBytesOut.toByteArray(), fsiRecordBytesList); // Return as byte array: BitOutputStream bitsOut = new BitWrapOutputStream(out); serialisedProjectVS.writeToBitStream(bitsOut, true); bitsOut.flush(); }
@Override public List<Project> retrieveProjectVersions(int projectID) { updateProjectCache(); // will also call initProjectCache() List<Project> projectVersions = new ArrayList<Project>(); for (Project project : projectCache) if (project.getID() == projectID) projectVersions.add(project); return projectVersions; }
/* (non-Javadoc) * @see uk.ac.ucl.excites.sapelli.collector.database.IDataAccess#retrieveV1Project(int, int) */ @Override public Project retrieveV1Project(final int schemaID, final int schemaVersion) { updateProjectCache(); // will also call initProjectCache() for (Project project : projectCache) if (project.isV1xProject() && project.getID() == schemaID && project.getV1XSchemaVersion() == schemaVersion) return project; return null; }
/** * Retrieves specific Project * * @return the project object or null if project was not found */ @Override public Project retrieveProject(final String name, final String variant, final String version) { updateProjectCache(); // will also call initProjectCache() for (Project project : projectCache) { if (project.getName().equalsIgnoreCase(name) && (variant != null ? variant.equals(project.getVariant()) : true) && project.getVersion().equalsIgnoreCase(version)) return project; } return null; }
@Override public void deleteSendSchedulesForProject(Project project) { try { if (project != null) rsWrapper.recordStore.delete( new RecordsQuery( SEND_SCHEDULE_SCHEMA, new EqualityConstraint( SEND_SCHEDULE_COLUMN_PROJECT, getProjectRecordReference(project)))); } catch (DBException e) { rsWrapper.client.logError( "Error upon deleting SendSchedules for Project \"" + project.toString(false) + "\".", e); } }
private Record getProjectRecord(Project project) { Record projRec = PROJECT_SCHEMA.createRecord(); PROJECT_ID_COLUMN.storeValue(projRec, project.getID()); PROJECT_FINGERPRINT_COLUMN.storeValue(projRec, project.getFingerPrint()); PROJECT_NAME_COLUMN.storeValue(projRec, project.getName()); String projVariant = project.getVariant(); PROJECT_VARIANT_COLUMN.storeValue( projRec, projVariant != null ? projVariant : ""); // replace null by empty string! PROJECT_VERSION_COLUMN.storeValue(projRec, project.getVersion()); PROJECT_V1X_SCHEMA_VERSION_COLUMN.storeValue(projRec, project.getV1XSchemaVersion()); return projRec; }
/* (non-Javadoc) * @see uk.ac.ucl.excites.sapelli.collector.db.ProjectStore#doAdd(uk.ac.ucl.excites.sapelli.collector.model.Project) */ @Override protected void doAdd(Project project) throws ProjectIdentificationClashException, ProjectSignatureClashException, IllegalStateException { try { // Note: we use insert() instead of store() to not allow updates // Store project record: rsWrapper.recordStore.insert(getProjectRecord(project)); // Store an FSI record for each record-producing form: for (Form form : project.getForms()) if (form.isProducesRecords()) rsWrapper.recordStore.insert(getFSIRecord(form)); // Cache the project: cacheProject(project); } catch (DBPrimaryKeyException dbPKE) { throw new ProjectIdentificationClashException(project, false); } catch (DBConstraintException dbCE) { throw new ProjectSignatureClashException(project); } catch (DBException e) { e.printStackTrace(System.err); throw new IllegalStateException(e); } }
private String getProjectPathPrefKey(Project project) { return getProjectPathPrefKey(project.getID(), project.getFingerPrint()); }
@Override protected void parseEndElement(String uri, String localName, String qName) throws Exception { // </Sapelli-Collector-Project>, or </ExCiteS-Collector-Project> (for backwards compatibility) if (qName.equals(TAG_PROJECT) || qName.equals(TAG_PROJECT_V1X)) { clearSubtreeParsers(); if (project.getForms().size() == 0) throw new SAXException("A project such have at least 1 form!"); else { // Resolve startForm Form startForm = project.getForm( startFormID); // will return null if startFormID is null or there is no form with // that name, uses equalsIgnoreCase() if (startForm != null) project.setStartForm(startForm); // else: first form of project will remain the startForm // Resolve form relationships: if (relationshipToFormID != null) for (Entry<Relationship, String> entry : relationshipToFormID.entrySet()) { Relationship rel = entry.getKey(); Form relatedForm = project.getForm(entry.getValue()); // uses equalsIgnoreCase() if (relatedForm == null) throw new SAXException( "Relationship \"" + rel.id + "\" in form \"" + rel.form.id + "\" refers to unknown related form \"" + entry.getValue() + "\"."); rel.setRelatedForm( relatedForm); // will trigger initialisation of Schema of relatedForm (this should // not be a problem, it will not be done again below) } // Initialise forms... for (Form form : project.getForms()) { try { // generates Schema, Columns & ValueDictionaries: form.initialiseStorage( fsiProvider != null ? fsiProvider.getByPassableFieldIDs(form) : null); // Note: fsiProvider will be null if this project is loaded/parsed for // the first time } catch (ModelFullException mfe) { throw new SAXException( "This project contains more data-producing Forms than allowed (maximum: " + Project.MAX_RECORD_PRODUCING_FORMS + ")."); } catch (DuplicateColumnException dce) { throw new SAXException( "Duplicate column name (\"" + dce.getColumnName() + "\") in schema for form \"" + form.id + "\"."); } addWarnings(form.getWarnings()); // !!! form.clearWarnings(); } // Seal project model: project.getModel().seal(); // Resolve relationship constraints: if (relationshipToConstraints != null) for (Entry<Relationship, List<ConstraintDescription>> entry : relationshipToConstraints.entrySet()) for (ConstraintDescription constrDesc : entry.getValue()) try { Relationship rel = entry.getKey(); rel.addConstraint(constrDesc.resolve(rel.getRelatedForm())); } catch (Exception e) { throw new Exception( "Error upon resolving constraint on Relationship \"" + entry.getKey().id + "\"", e); } } } }
@Override protected void parseStartElement( String uri, String localName, String qName, XMLAttributes attributes) throws Exception { try { // <Sapelli-Collector-Project>, or <ExCiteS-Collector-Project> (for backwards compatibility) if (qName.equals(TAG_PROJECT) || qName.equals(TAG_PROJECT_V1X)) { if (project != null) { addWarning("Ignoring additional " + TAG_PROJECT + " or " + TAG_PROJECT_V1X + " element."); return; } // Detect format version... int formatVersion = attributes.getInteger( ATTRIBUTE_PROJECT_FORMAT, qName.equals(TAG_PROJECT_V1X) ? Format.v1_x.ordinal() : DEFAULT_FORMAT .ordinal()); // default: v1.x for ExCiteS tag, DEFAULT_FORMAT for Sapelli // tag. // too low: if (formatVersion < LOWEST_SUPPORTED_FORMAT.ordinal()) throw new SAXException("Unsupported format version: " + formatVersion); // too high: else if (formatVersion > HIGHEST_SUPPORTED_FORMAT .ordinal()) { // issue warning and then try to parse as highest supported format // (might fail) addWarning( "Format version reported in XML file (" + formatVersion + ") is unsupported (" + LOWEST_SUPPORTED_FORMAT + " <= supported <= " + HIGHEST_SUPPORTED_FORMAT + "), attempting parsing with rules for version " + HIGHEST_SUPPORTED_FORMAT); format = HIGHEST_SUPPORTED_FORMAT; } // within range (or default because missing attribute): else format = Format.values()[formatVersion]; // Project... project = new Project( (format == Format.v1_x) ? Project.PROJECT_ID_V1X_TEMP : // for format = 1 we set a temp id value (will be replaced by Form:schema-id) attributes.getRequiredInteger( qName, ATTRIBUTE_PROJECT_ID, "because format is >= 2"), // id is required for format >= 2 attributes.getRequiredString(TAG_PROJECT, ATTRIBUTE_PROJECT_NAME, true, false), attributes.getString(ATTRIBUTE_PROJECT_VARIANT, null, true, false), attributes.getString( ATTRIBUTE_PROJECT_VERSION, Project.DEFAULT_VERSION, true, false), fingerPrint); // Set default language (or "en" if not specified): String lang = attributes.getString(ATTRIBUTE_PROJECT_DEFAULT_LANG, null, true, false); project.setDefaultLanguage(lang != null ? lang : Project.DEFAULT_DEFAULT_LANGUAGE); if (lang == null && format == Format.v2_x) addWarning( "No valid default language has been specified for this project. Languages should be declared using the BCP-47 syntax (e.g. \"fr-CA\" for Canadian French). English (en) will be set as the default language for this project."); // Read startForm ID: startFormID = attributes.getString(ATTRIBUTE_PROJECT_START_FORM, null, true, false); // Add subtree parsers: addSubtreeParser(new ConfigurationParser(this)); addSubtreeParser(new FormParser(this)); } // <?> else addWarning("Ignored unrecognised or invalidly placed/repeated element <" + qName + ">."); } catch (Exception e) { e.printStackTrace(System.err); throw new Exception("Error while parsing element <" + qName + ">: " + e.getMessage(), e); } }